The pro's guide to responsive web design
Advanced techniques from top web designers to build better sites for any device.
Responsive web design sounds incredibly simple. Opt for flexible grids for the layout, use flexible media (images, video, iframes), and apply media queries to update these measurements to best arrange content on any viewport. In practice we've learnt it is not really that easy. Tiny issues that crop up during every project keep us scratching our heads, and occasionally even carving fingernail trenches on our desks.
Since I began curating the Responsive Design Weekly newsletter I've been fortunate enough to speak with many members of the web community and hear their experiences. I wanted to find out exactly what the community really wanted to learn, so I circulated a survey to readers. Here are the top results:
- Responsive images
- Improving performance
- Responsive typography
- Media queries in JavaScript
- Progressive Enhancement
- Layout
With those topics in mind, I ran a series of podcasts asking some of our industry leaders for their thoughts. In their responses, one point was unanimous: focus upon getting the basics right before you start worrying about exciting and advanced techniques. By taking things back to the basics, we are able to build a robust interface for everyone, layering on features when the device or user's context allows.
'So ... what about these advanced techniques?' I hear you ask. I think Stephen Hay summed it up best when he said: 'The ultimate RWD technique is to start off by not using any advanced RWD techniques. Start with structured content and build your way up. Only add a breakpoint when the design breaks and the content dictates it and ... that's it.'
In this article, I'll begin with the basics and add layers of complexity as the situation allows, to build up to those advanced techniques. Let's get started.
Responsive images
Fluid media was a key part of RWD when it was first defined by Ethan Marcotte. width: 100%; , max-width: 100%; still works today, but the responsive image landscape has become a lot more complicated. With increasing numbers of screen sizes, pixel density and devices to support we crave greater control.
The three main concerns were defined by the Responsive Images Community Group (RICG):
Get top Black Friday deals sent straight to your inbox: Sign up now!
We curate the best offers on creative kit and give our expert recommendations to save you time this Black Friday. Upgrade your setup for less with Creative Bloq.
- The kilobyte size of the image we are sending over the wire
- The physical size of the image we are sending to high DPI devices
- The image crop in the form of art direction for the particular size of the viewport
Yoav Weiss, with help from Indiegogo, has done the majority of the work on the Blink implementation – Google's fork of WebKit, and by the time you're reading this it will be shipped in Chrome and Firefox. Safari 8 will ship srcset, however the sizes attribute is only in nightly builds and <picture> is not yet implemented.
With the arrival of anything new to our web development process, it can be difficult to get started. Let's run through an example step by step.
<img
<!-- Declare the fallback image for all non picture supporting browsers -->
src="horse-350.jpg"
<!-- Declare all of the image sizes in srcset. Include the image width using the w descriptor to inform the browser of the width of each image.-->
srcset="horse-350.jpg 350w,
horse-500.jpg 500w,
horse-1024.jpg 1024w,
horse.jpg 2000w"
<!-- Sizes inform the browser of our site layout. Here we're saying for any viewport that is 64ems and bigger, use an image that will occupy 70% of the viewport -->
sizes="(min-width: 64em) 70vw,
<!-- If the viewport isn't that big, then for any viewport that is 37.5ems and bigger, use an image that occupies 95% of the viewport -->
(min-width: 37.5em) 95vw,
<!-- and if the viewport is smaller than that, then use an image that occupies 100% of the viewport-->
100vw"
<!-- always have alt text.-->
alt="A horse" />
From a performance point of view it doesn't matter if you use min-width or max-width in the sizes attribute – but the source order does matter. The browser will always use the first matching size.
Also, remember we are hard-coding the sizes attribute to be directly defined against our design. This may cause issues moving forwards. For example, if you redesign your site, you'll need to revisit all of the <img> or <picture>s and redefine the sizes. If you are using a CMS, this can be overcome as part of your build process.
WordPress already has a plugin to help. It defines the srcset on WP standard image varieties and allows you to declare sizes in a central location. When the page is generated from the database, it swaps out any mentions on <img> and replaces them with the picture markup.
Basic
- Think about whether you really need to include an image. Is the image core content, or is it decorative? One less image will mean a faster load time
- Optimise the images you do need to include using ImageOptim
- Set expire headers for your images on your server or .htaccess file (see details under ‘Performance')
- PictureFill provides polyfill support for pictures
Advanced
- Lazy load your images using jQuery's Lazy Load plugin
- Use HTMLImageElement.Sizes and HTMLPictureElement for feature detection.
- The advanced PictureFill WP plugin, found on Github, will allow you to define custom image widths and sizes values
Performance
To get the fastest perceived performance on our pages, we need all the HTML and CSS required to render the top part of our page within the first response from the server. That magic number is 14kb and is based on the max congestion window size within the first round-trip time (RTT).
Patrick Hamann, frontend technical lead at the Guardian, and his team have managed to break the 1000ms barrier using a mixture of frontend and backend techniques. The Guardian's approach is to ensure the required content – the article – is delivered to the user as quickly as possible and within the 14kb magic number.
Let's look at the process:
- User clicks on a Google link to a news story
- A single blocking request is sent to the database for the article. No related stories or comments are requested
- The HTML is loaded containing Critical CSS
- in the <head>
- A ‘Cut the mustard' process is undertaken and any conditional elements based upon the user's device features are loaded
- Any content related to or supporting the article itself (related article images, article comments and so on) are lazy loaded into place
Optimising the critical rendering path like this means the <head> is 222 lines long. However, the critical content the user came to see still comes within the 14kb initial payload when gzipped. It's this process that helps break that 1000ms rendering barrier.
Conditional and lazy loading
Conditional loading improves the user's experience based on their device feature. Tools like Modernizr allow you to test for these features, but be aware that just because a browser says it offers support, that doesn't always mean full support.
One technique is to hold off loading something until the user shows intent to use that feature. This would be considered conditional. You can hold off loading in the social icons until the user hovers over or touches the icons, or you could avoid loading an iframe Google Map on smaller viewports where the user is likely to prefer linking to a dedicated mapping application. Another approach is to ‘Cut the mustard' – see boxout above for details on this.
Lazy loading is defined as something that you always intend to load on the page – images that are a part of the article, comments or other related articles – but that don't need to be there for the user to start consuming the content.
Basic
- Enable gzipping for files and set expire headers for all static content (netm.ag/expire-260)
- Use the Lazy Load jQuery plugin. This loads images when approaching the viewport, or after a certain period of time
Advanced
- Set up Fastly or CloudFlare. Content delivery networks (CDNs) deliver your static content to users faster than your own server, and have some free tiers
- Enable SPDY for http2-enabled browsers to take advantage of http2 features like parallel http requests
- Social Count allows for conditional loading of your social icons
- Using the Static Maps API will allow you to switch out Interactive Google maps for images. Take a look at Brad Frost's example at netm.ag/static-260
- Ajax Include Pattern will load content snippets from either a data-before, data-after or data-replace attribute
Responsive typography
Typography is about making your content easy to digest. Responsive typography extends this to ensure readability across a wide variety of devices and viewports. Jordan Moore admits that type is one thing he isn't willing to budge on. Drop an image or two if you need, but make sure you have great type.
Stephen Hay suggests setting the HTML font size to 100 per cent (read: just leave it as it is) because each browser or device manufacturer makes a reasonably readable default for a particular resolution or device. For most desktop browsers this is 16px.
On the other hand, Moore uses the REM unit and HTML font-size to set a minimum font size for certain content elements. For example, if you want the byline of an article to always be 14px, then set that as the base font-size on the HTML element and set .byline { font-size: 1rem;}. As you scale the body: font-size: to suit the viewport you will not impact the .by-line style.
A good reading line length is also important – aim for 45 to 65 characters. There's a bookmarklet you can use to check your content. If you are supporting multiple languages with your design, then line length may vary as well. Moore suggests using :lang(de) article {max-width: 30em} to cover off any issues there.
To maintain vertical rhythm, set margin-bottom against content blocks, <ul>, <ol>, <blockquote>, <table>, <blockquote> and so on, to the same as your line-height. If the rhythm is interrupted with the introduction of images you could fix it by adding Baseline.js or BaselineAlign.js.
Basic
- Base your font on 100 per cent body
- Work in relative em units
- Set your margins to your line height to maintain vertical rhythm in your design
Advanced
- Improve font loading performance with Enhance.js or deferred font loading
- Use Sass @includes for semantic headings.
- Often we need to use the h5 style in a sidebar widget that requires h2 markup. Use Bearded's Typographic Mixins to control the size and remain semantic with the below code:
.sidebar h2 {
@include heading-5
}
Media queries in JavaScript
Ever since we have been able to control the layout across a variety of viewports through media queries, we've been looking for a way to tie that into running our JavaScript as well. There are a few ways to fire JavaScript on certain viewport widths, heights and orientations, and some smart people have written some easy-to-use native JS plugins like Enquire.js and Simple State Manager. You could even build this yourself using matchMedia. However, you have the issue that you need to duplicate your media queries in your CSS and JavaScript.
Aaron Gustafson has a neat trick that means you don't have to manage and match your media queries in your CSS and your JS. The idea originally came from Jeremy Keith and in this example Gustafson has taken it to a full implementation.
Using getActiveMQ (netm.ag/media-260), inject div#getActiveMQ-watcher at the end of the body element and hide it. Then within the CSS set #getActiveMQ-watcher {font-family: break-0;} to the first media query, font-family: break-1; to the second, font-family: break-2; to the third and so on.
The script uses watchResize() (netm.ag/resize-260) to check to see if the size of the viewport has changed, and then reads back the active font-family. Now you can use this to hook JS enhancements like adding a tabbed interface to a <dl> when the viewport allows, changing the behaviour of a lightbox, or updating the layout of a data table. Check out the getActiveMQ Codepen at netm.ag/active-260.
Basic
- Forget about JavaScript for different viewports. Provide content and website functions to users in a way they can access it across all viewports. We should never need JavaScript
Advanced
- Extend Gustafson's method by using Breakup as a predefined list of media queries and automating the creation of the list of font families for getActiveMQ-watcher
Progressive enhancement
A common misconception about progressive enhancement is that people think, 'Oh well I can't use this new feature', but really, it's the opposite. Progressive enhancement means that you can deliver a feature if it's only supported in one or even no browsers, and over time people will get a better experience as new versions arrive.
If you look at the core function of any website, you can deliver that as HTML and have the server side do all the processing. Payments, forms, Likes, sharing, emails, dashboards – it can all be done. Once the basic task is built we can then layer the awesome technologies on top of that, because we have a safety net to catch those that fall through. Most of the advanced approaches we have talked about here are based upon progressive enhancement.
Layout
Flexible layout is simple to say, but it has many different approaches. Create simple grid layouts with less markup by using :nth-child() selector.
/* declare the mobile first width for the grid */
.grid-1-4 { float:left; width: 100%; }
/* When the viewport is at least 37.5em then set the grid to 50% per element */
@media (min-width: 37.5em) {
.grid-1-4 { width: 50%; }
/* Clear the float every second element AFTER the first. This targets the 3rd, 5th, 7th, 9th... in the grid.*/
.grid-1-4:nth-of-type(2n+1) { clear: left; }
}
@media (min-width: 64em) { .grid-1-4 { width: 25%; }
/* Remove the previous clear*/
.grid-1-4:nth-of-type(2n+1) { clear: none; }
/* Clear the float every 4th element AFTER the first. This targets the 5th, 9th... in the grid.*/
.grid-1-4:nth-of-type(4n+1) { clear: left; }
}
Wave goodbye to using position and float for your layouts. While they have served us well to date, their use for layout has been a necessary hack. We've now got two new kids on the block that will help fix all our layout woes – Flexbox and Grids.
Flexbox is great for individual modules, controlling the layout of pieces of content within each of the modules. There are layouts we attempt to deliver that can be more easily achieved using Flexbox, and this is even more true with responsive sites. For more on this, check out CSS Tricks' guide to Flexbox or the Flexbox Polyfill.
CSS grid layout
Grid is more for the macro level layout. The Grid layout module gives you a great way to describe your layout within your CSS. While it's still in the draft stage at the moment, I recommend this article on the CSS Grid layout by Rachel Andrew.
Finally
These are just a few tips to extend your responsive practice. When approaching any new responsive work, take a step back and ensure that you get the basics right. Start with your content, HTML and layer improved experiences upon them and there won't be any limit to where you can take your designs.
This article originally appeared in issue 260 of net magazine.
Thank you for reading 5 articles this month* Join now for unlimited access
Enjoy your first month for just £1 / $1 / €1
*Read 5 free articles per month without a subscription
Join now for unlimited access
Try first month for just £1 / $1 / €1