
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 (caniuse.com 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);
Box-sizing
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 (meyerweb.com/eric/thoughts/2010/02/10/rounding-off) 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.