Scott is here at An Event Apart in Atlanta to talk to us about being responsibly responsive. Scott works with Filament Group on large-scale responsive designs like the Boston Globe. They’ve always focused on progressive enhancement and responsive design feels like a natural evolution of that.
But responsive design is just a small part of what goes into responsible design. Responsible design isn’t just about layout, it’s about making sure that adding advanced features doesn’t inconvenience anyone. Mostly, we need to care; we need to care about people in situations other than our own.
This became very clear to Scott when he was travelling in southeast Asia, working remotely with his colleagues back in Boston. Most of the time he was accessing the web through a USB dongle over a cellular network. That’s how most people get online there. So don’t make assumptions about screen size and bandwidth.
Browsing via this dongle was frustrating. It was frustrating for Scott because, as a developer, he knew that there was no reason for the web to be so difficult to use on this connection.
When you prototype interactions, are you thinking about your own clock …or the user’s?
The average load time for the top 200 websites is between 6 and 10 seconds on a good connection.
Good performance is good design. Scott shows a graph of “webpage loading time” on one axis against “swear words” on the other. The graph trends upwards.
Now those 1MB webpages were probably desktop-specific sites, not responsive sites. But 86% of responsive sites send the same assets to all devices.
We have to design for new sizes and new input methods, but at the same time, the old contexts aren’t going away.
We’re moving from normalisation to customisation. We used to try to make things look and work the same in every browser. It was always a silly goal, but now it seems totally ridiculous. But content parity does not require experience parity. In fact, if things look and act the same across all devices, that might mean that we’ve missed a lot of opportunities.
We should avoid presumptions. We might be able to get the dimensions of a screen, but that could be different to the dimensions of the viewport. Instead of using pixels for breakpoints, we can use ems so that the user’s font size determines the layout changes.
Before we look at some responsible techniques, let’s look at the anatomy of a page load.
We begin with typing a URL. That request goes to a DNS server. Then the request is sent to the right host. Then the host sends a response. But on a mobile device, there’s an extra step. You have to go through a tower first, before reaching the DNS server. That connection to the tower takes two seconds (for radio). That two-second delay only happens once, thankfully.
After about eight seconds, the carrier drops the connection. That two-second connection needs to be made again. So we should try to get everything during that initial eight second period.
Network conditions can change. Every HTTP request is a gamble. Say you’ve embedded a third-party widget, like Twitter’s. If you’re in a country like China that blocks Twitter, the page will never load. Chrome will hang for thirty seconds.
We need to ensure that we’re delivering assets responsibly. Consider using conditional loading. On the Boston Globe, the home page has a lot of content. The headlines certainly belong there. But content from individual sections (that you can get to from the top navigation) is also being pulled into the homepage. We definitely want to provide the link to, for example, the sports section, but the latest content from the sports section could be conditionally loaded.
Scott mentions my 24 Ways article on conditional loading for responsive designs. At Filament Group, they abstracted this “link/conditional load” into a pattern. It uses HTML5
data attributes to indicate what should be pulled in via Ajax and where it should go.
Let’s look at how we deliver CSS. The way that we load CSS today is going to catch up with us. According to the HTTP Archive, the transfer size of CSS has the highest correlation to render time.
As a start, we should be using mobile-first media queries. That means starting with the styles for absolutely every device. Then we start adding our media queries for wider and wider viewport widths. This gives you a broadly-usable default. Those initial styles should go to everyone, but Scott likes to qualify them with an
only all media query for some enhanced small-screen layout declarations. That makes sure that really old browsers won’t mess it up.
Generally mobile-first media queries work pretty well. But there’s a problem. We’re sending all the CSS to all the devices, even if they’ll never use half of it.
Would it be better to use multiple
link elements with different media types? Alas, no. Browsers will download all stylesheets (even if the media type is set to “nonsense”) just in case they’ll need ‘em later. And unfortunately those requests are blocking. Modern WebKit browsers do a bit better. It still downloads all the stylesheets but it will render once it has the small-screen CSS.
The best approach is, unfortunately, to send just one CSS file but minify, compress, gzip and cache the shit out of it. CSS compresses really well. Gzip works by spotting redundant data—as soon as it notices a repeating segment, you get a gain. And CSS is full of repeated properties and declarations.
Images are an interesting problem. Remember they were the worst offenders in page bloat. Fortunately they don’t block, but still—this problem will only get worse.
There are background images. They’re easy. Browsers have gotten very smart about only downloading the background images they need.
Foreground images aren’t so easy. There’s the compressive image technique that Luke mentioned. Make the JPEG bigger in size, but lower in quality. When it’s scaled down in the browser, it looks perfectly fine. A 1x image saved at 90% quality, it’s 95K. The same image at 2x with 0% quality is 44K.
But there’s a concern about the memory footprint of doing this on some devices. Filament Group are looking into this but it’s very hard to test.
With compressive images, you just have to point to them in one
img element using the
Responsive images are much trickier. There are two proposals.
The first is the
picture element, which uses multiple
source elements to specify different images for different breakpoints. There’s also a fallback image as a last resort for older browsers.
The second proposal is the
srcset attribute. It’s particularly well-suited to different pixel densities. The advantage of this one is that the browser, rather than the author, gets the last say about which image should be displayed. There’s also talk about merging both proposals.
Neither proposal works today so Scott created Picturefill, a polyfill for the
picture proposal, although it uses
divs. The fallback image goes in a
noscript element to prevent browsers from pre-fetching it.
picture and Picturefill work with media queries, perhaps you can default to standard definition but allow users to opt in to high definition versions.
Managing different images for different resolutions and pixel densities gets very tedious. Maybe we should abandon the pixel. Certainly for icons, SVG can be really useful. It’s well-supported today. It also compresses well, because it is text: it’s a markup language, like HTML. That means you can also edit the source of an SVG image in a text editor.
You can reference the SVG file directly in the
src attribute of an
img element. For older browsers, you could use
onerror to replace it with a different image format.
You can also put SVG as a background image. And you use them as data-URLs and just write out the SVG in your stylesheet.
Building on that, there’s a tool called Grunticon that generates and regenerates CSS whenever you make changes to an image. It also generates a preview document for you.
Scott uses a handful of variables to determine what needs to be loaded or not:
- Environmental conditions. Media queries, basically.
- Template type. Define in your markup what kind of page this is (using a
meta element e.g. “shoppingcart”. Then test whether we need shoppingcart.js.
You can even load CSS with YepNope but be careful. You could use it for fonts, for example, after you’ve tested that @font-face is supported.
Dealing with third-party content is tricky—it’s blocking. The Twitter embed code for tweets now includes an
async attribute. It’s pretty well supported in modern browsers.
On the Boston Globe, they had to deal with ads. Nightmare! They used iframes.
But the main idea here is: defend the critical path. Develop responsively and responsibly. Today we have the tools to build rich experiences without excluding anyone.