20 ways to build a better responsive website

Add to your responsive toolkit with these must-know techniques to help you build awesome, future-friendly sites

Responsive Web Design


Meta viewport tag

The start of any responsive site is the meta viewport tag. Here we set the width to the reported device width. This is important because, in the world of devices, a pixel is not necessarily a pixel. Taking the iPhone as an example, its actual width is 640px but because it has such a large pixel density it reports the device width as 320px.

001 <meta name=”viewport” content=”width=device-width, initial-scale=1;”>

The media query

Now that the browser has mapped the pixels on the screen to pixels we can use in our CSS, we can write our media query. Media queries evaluate an expression (or multiple expressions) and if they return true then the styles contained within the media query are applied. We’ll write one that only applies to widths under 320px.

001 @media only screen and (max-width: 320px)     {
 002    /* applies to devices with 320px width */
 003 }

Em media query

A growing trend in RWD is to become pixel independent. We’ve already seen that a pixel is not really a pixel, so it doesn’t make much sense to use pixel values unless we’re dealing with things like raster images. To convert our media query to use the em unit we use the equation target/base – so 320/16 = 20 = 20em!

 001 @media only screen and (max-width: 20em) {
 002    /* applies to devices with 320px width */
 003 }

Going mobile first

Another trend in RWD is a concept called mobile first. This uses the small-width layout as the starting point so you’ll often see min-width instead of max-width being used. This is because each media query builds on top of each other, starting with the basic one column and working up to traditional desktop widths and layouts.

001 @media only screen and (min-width: 20em) {
 002    /* applies to devices with 320px width */
 003 }

More than width

The humble media query isn’t restricted to just calculating widths, either. There’s a large array of properties that can be tested against. This includes if its colour or monochrome, colour depth, aspect ratio, height, orientation, and resolution. These have varying degrees of support and even when they are supported, the vendor may lie! For example, the Kindle reports itself as colour.

001 @media (min-width: 42em) and (orientation:     landscape), (min-aspect-ratio: 16/9) {
 002    /* targets landscape orientations with     large viewports */
 003 }

Logical operators

As well as the properties listed above, media queries can be chained with the logical operators: and, not, only, and comma-separated lists which act as ‘or’. This means that you can be very specific in the attributes that you target. It’s worth noting as well that even desktop browsers can change their orientation from landscape to portrait and vice versa.

001 @media only screen and (min-width: 20em),     not     (orientation: landscape) {
 002    /* applies to devices with 320px width */
 003 }

Using rem

Following our quest for resolution independence we can use the root em, or rem unit. This is new to CSS3 so it’s advisable to add a pixel-based fallback for older browsers. The main benefit of rem is it can be used with modules that are used throughout the site and that might have an em-based font size – which would otherwise cause our heading to be twice as large as the container’s font!

001 .heading {
 002    font-size: 24px;
 003    font-size: 1.5rem;
 004 }

Using matchMedia

Media queries aren’t confined to CSS, you can also use them in JavaScript with matchMedia, which has good mobile support ( reports 76.28% of all browsers) and a robust polyfill for browsers that don’t natively support it (mainly Opera Mini). This takes the media query (sans @media) as a string and returns a MediaQueryList object.

001 var small = matchMedia(‘screen and (min-    width:     20em)’);

Add a listener

The MediaQueryList object has two properties: matches, a boolean representing if the query evaluates as true or false; and media, the query that was evaluated. One of its most powerful features is its addListener method. You can pass a callback to it that is only fired when the query changes – without the hassle of having to have lots of resize event listeners.

001 small.addListener(function (mediaquery) {
 002    if (mediaquery.matches) {
 003         // do something
 004    } else {
 005         // revert something
 006    }
 007 });

The debounce technique

Sometimes the resize event is still useful but most browsers (the exception being Opera <13) calls it multiple times while resizing, which can grind the UI to a halt if there are many checks and DOM manipulations attached to it. Debouncing is a technique to make sure that an action is performed only once within a given timeframe.

001 //using underscore.js but technique is     library     independent
 002 //check underscore’s source to see how it’s     achieved
 003 var lazyLayout =  _.debounce(calculateLayout,     500);
 004 window.addEventListener(‘resize’,     lazyLayout,     false);

Detecting touch

When you click an element on a modern touchscreen browser there’s a ~300ms delay between the device detecting your touch and firing the click event. This may sound small but it can make a big difference to how responsive (as in speed) your site feels. A simple but not necessarily foolproof way is to check if ontouchstart exists.

001 var prod = ‘ontouchend’ in window ?     ‘touchend’     : ‘click’;
 002 element.addEventListener(prod, function     (event)     {
 003    console.log(event);
 004 }, false);

Touch bugs

Touchend is fired when the user lifts their fingers from the display, touchstart is when a touch is first detected and touchmove is fired when the finger moves. There’s a bug present in Chrome for Android that causes touchend to not fire if a touchmove was detected. Devices with multiple inputs (touchscreens attached to keyboards/mice) provide fresh challenges.

001 document.body.addEventListener(‘touchstart’, function (event) {
 002    console.log(‘started touching’, event);
 003 }, false);
 004 document.body.addEventListener(‘touchmove’,     function (event) {
 005    console.log(‘touch is moving’, event);
 006 }, false);
 007 document.body.addEventListener(‘touchend’,     function (event) {
 008    console.log(‘not fired on Chrome for     Android 4 if moved’);
 009 }, false);


One irritating aspect about the box model is that padding increases the width of an item, and this is especially prevalent in RWD. An alternative model exists that contains the padding within the width, anything above IE7 supports this so you can specify width:100%; padding: 1em; and not break your layout.

001 *, *:before, *:after {
 002    -webkit-box-sizing: border-box;
 003    -moz-box-sizing: border-box;
 004    box-sizing: border-box;
 005 }            

SVGs with fallback

On our path to resolution independence one of the best things that we can do is use SVG images that are vector based rather than raster-based ones. For a long time there’s been no easy way to fallback to PNG without JavaScript but a certain bright spark named Alexey Ten came up with the following simple and elegant solution.

001 <svg width=”96” height=”96”>
 002    <image xlink:href=”svg.svg” src=”svg.    png”     width=”96” height=”96”/>
 003 </svg>

How it works

The above works because browsers alias <image> to our beloved <img> and browsers that recognise SVG fetch the xlink:href instead of the src (as this is non-standard). You could have your SVG markup inline as this will be ignored by browsers that don’t support SVG and the src attribute is ignored by those that do.

001 <svg width=”96” height=”96”>
 002    <ellipse fill=”#FFFFFF” stroke=”#FFFFFF” stroke-miterlimit=”10” cx=”295.566”         cy=”218.961” rx=”72” ry=”58”/>
 003    <image src=”circle.png” width=”96”     004 height=”96”/>
 005 </svg>

Rounding discrepancies

At some point you’ll probably end up encountering how different browsers round percentages. Eric Meyer covers this in detail ( but essentially if we have a value of 33.3333 per cent, some browsers will round this up while others will round it down, and Firefox will round some up and some down to make sure the total amount of pixels is covered.

Accounting for differences

If you do encounter rounding errors then you can use JavaScript’s getComputedValue() method to work out the actual calculated pixel value and what you expect it to be. You can then adjust for this, perhaps by taking the parent’s width or manually adding pixels to the width. getComputedStyle is supported by all major browsers and Internet Explorer 9+.

001 var actual = getComputedStyle(element).    getPropertyValue(‘width’),
 002    expected = getComputedStyle(element.    parentNode).getPropertyValue(‘width’) / 3;
 003 if (actual !== expected) {
 004    //set as desired width
 005 }

iOS scrolling

Another useful CSS property is the proprietary WebKit overflow scrolling that is used by iOS to create the native momentum scrolling effect on any element. A limitation of this is that it can’t be restricted to the X or Y axis – it’s both or nothing. Previous to this, heavy JavaScript libraries were used to mimic it.

001 .scrollable {
 002    overflow: scroll;
 003    -webkit-overflow-scrolling: touch;
 004 }

Minify files

One of the biggest oversights currently with RWD is page load. The site may look great on handheld devices but if it has to apply twice the number of styles, download more stylesheets and execute more JavaScript then the device is going to struggle compared to its desktop counterpart. One of the ways to minify this if not using a mobile-first process is to concatenate all of your JavaScript and CSS files and Gzip them.

Calc() value

A burgeoning CSS feature that can be enjoyable to use is calc(). Calc computes a value and can be used with any property that takes a unit. Why not just use a percentage? Imagine that you wanted to guarantee a 2em gap between your element and the width of the container – but this could result in a larger or smaller gap.

001 .slightly-smaller-than-full-width {
 002    width: 95%; /* fallback */
 003    width: calc(100%-2em);
 004 }

How em’s calculated

Em-based media queries are calculated by taking the font size of the HTML element. By default this is 16px so when calculating em-based media queries you can divide the width that you want (eg 640px) by the font size (16px), 640/16 = 40 so our media query will be 40ems.

Go forth!

We’ve covered a range of techniques that should help to power up your RWD, but it’s worth remembering that it’s a philosophy embracing all devices, not just popular viewports. You can have both macro and micro breakpoints and create media queries only when the content doesn’t feel right the way it is.