CSS3 media queries are dead simple, in terms of their syntax. You’ve got an @media directive, a media type (which you already know from good ol’ CSS 2, like screen, print, all, etc.) and one or more media features (the characteristics we’re testing against). That’s it:
@media screen and (max-width:500px) {
}
There are some additional little syntax details, but this is basically all you need to know to actually make a media query work. Once you memorize this short, simple syntax and the various media features that are available, you could technically say that you know how to use media queries. But knowing how to use media queries effectively requires a whole lot more considerations than just where to put an @ or {.
Designing web layouts with media queries is a process. You need to keep them in mind from the very beginning and make decisions at several points about how to integrate them in ways that will make the most sense for your site. There are very few always-right or always-wrong answers. What type of media query set-up would work best will depend on your site, your users, and your own capabilities and experience. But I wanted to cover the pros and cons of some of the essential considerations that go into crafting robust media query-driven layouts. These considerations include whether to:
- Make the media queries embedded or external
- Overlap or stack your media queries
- Make mobile or desktop styles the default
- Use conditional comments or JavaScript to add support for IE 8 and earlier versions
This article is just as much for me as it is for you—it can be hard to keep track of all the different configuration variations you can use! Hopefully I’ll be able to make the pros and cons of the various approaches clearer so you can use this article to guide your decisions when you start a project involving media queries.
How to include your media queries: embedded vs. external
There are two ways to include media queries in your site: embed them within a style sheet or include them in a call to a separate, external sheet.
Here’s what an embedded media query looks like (pretend that we’re inside a style sheet):
body {
background: gray;
}
@media all and (max-width:500px) {
body {
background: blue;
}
}
For an external media query, simply extend the existing media part of the link element or @import rule:
<link href="narrow.css" rel="stylesheet" media="only screen and (max-width:500px)">
@import url(narrow.css) only screen and (max-width:500px);
Browsers that don’t support media queries won’t download these sheets, but browsers that do will download them all, regardless of whether they’re needed or not for the current viewing scenario. (The background images inside the currently-not-needed sheets won’t download, however.) On the one hand, this makes sense—the user could change his or her orientation or window size, and those alternate styles would suddenly need to be called into action. On the other hand, Greg Rewis found that even if you create a sheet that it seems like a device could never possibly use, such as a sheet for a max-width of 700 pixels never being needed by an iPad with its 768 x 1024 resolution, the device will download it anyway. But Chris Coyier pointed out to me that you could have an iframe in a page, for instance, that might need to use that smaller style sheet—so on the third hand, I guess this behavior is logical and unavoidable, if not perfect.
The pros and cons
| Embedded pros | External cons |
|---|---|
| No extra HTTP request(s) | Extra HTTP request(s) |
| Not out of sight, so harder to forget | Out of sight, so could be forgotten when updating |
| Embedded cons | External pros |
|---|---|
| Extra kb in file size for everyone to download whether query used or not | Smaller file size for default sheet used by browsers that don’t support media queries |
| Have to use JavaScript to make it work in IE 8 and earlier* | Can feed one of the queries to IE using conditional comments* |
| Harder to keep organized if CSS extensive | Easier to keep organized if CSS extensive |
* I’ll go into the details of both of these approaches to dealing with old versions of IE later on in this article, so sit tight if this IE stuff doesn’t make sense just yet.
The bottom line
In most situations, I think that embedded is the better way to go. I’ve never been a fan of separating out styles into separate sheets because I find it harder to keep things organized and easier to forget those extra sheets when debugging or updating. Plus, it adds extra HTTP requests, which are more expensive in terms of performance than having a single sheet that’s larger in file size. Because media-query-supporting browsers will download those extra sheets even they don’t currently need them, the smaller file size that you gain by separating the media queries into their own sheets is really only a benefit to browsers that don’t support media queries, as they won’t download those extra media query sheets.
But again—and this is the last time I’m saying this, but it applies to everything in this article—the best approach depends on your project.
How to use the cascade: overlapping vs. stacked
Regardless of whether you make your media queries embedded or external, you also need to decide whether you want them to overlap, bringing the cascade and specificity into play, or whether you want to “stack” them so only one media query applies at once.
Here’s what overlapping media queries look like (this example is embedded, but the same could be done with external sheets):
body {
background: gray;
font-family: sans-serif;
}
@media all and (min-width:500px) {
body {
background: blue;
font-family: serif;
}
}
@media all and (min-width:700px) {
body {
background: red;
color: white;
}
}
Do you see how these media queries are not mutually exclusive? Both apply to windows greater than 700 pixels wide. If my viewport was 800 pixels wide, for instance, it would meet both the minimum width of 500 pixels and the minimum width of 700 pixels and apply the styles from both media queries. I’d have a red background with white text in a serif font.
When you overlap your media queries, the cascade and specificity come into play when determining which rules to use. For instance, if I used the same exact media queries containing the same exact CSS, but changed their order so that the min-width:700px one was first, I’d get different results:
body {
background: gray;
font-family: sans-serif;
}
@media all and (min-width:700px) {
body {
background: red;
color: white;
}
}
@media all and (min-width:500px) {
body {
background: blue;
font-family: serif;
}
}
Now my 800-pixel-wide viewport would have a blue background instead of red—the rule that comes later with the same specificity wins.
Instead of overlapping your media queries, you can stack or isolate them so that each is mutually exclusive:
body {
background: gray;
font-family: sans-serif;
}
@media all and (min-width:500px) and (max-width:699px) {
body {
background: blue;
font-family: serif;
}
}
@media all and (min-width:700px) {
body {
background: red;
color: white;
font-family: serif;
}
}
Now only one of these media queries can apply at a time: the first applies in viewports between 500 and 699 pixels, and the second applies at 700 pixels and up. This means you don’t have to worry about cascade or specificity any more, but you do have to repeat any styles that you want to apply in both situations, such as font-family:serif in this example. On the other hand, you don’t have to override styles from earlier media queries that you don’t want to apply in the later media queries. So neither setup always leads to more or fewer lines of CSS.
Whether you overlap or stack also has an impact on which assets get downloaded in WebKit browsers. If you have multiple media queries that apply to the same situation (such as the 500px and 700px media queries both applying to the 800px viewport example above), WebKit-based browsers will download all the images in all of them, regardless of whether a later media query overrides one of the images in an earlier one. (This also happens with the styles outside any media queries, by the way.) This doesn’t happen if the media queries are stacked; in this case, WebKit will only download the images called for in the single, currently applicable one. (But again, it will download any images outside the media queries too, even if later overridden.) This handy information is from Greg’s “CSS3 Media Queries? Download Answers” and Cloud Four’s “Media Query Image Download Test.”
The pros and cons
| Overlapping pros | Stacked cons |
|---|---|
| Smaller file size due to cascade not requiring you to repeat shared rules | Bigger file size due to having to repeat rules you want to apply in multiple scenarios |
| Easy to update shared rule in one place | Possible to forget to change all instance of repeated rule when updating |
| Overlapping cons | Stacked pros |
|---|---|
| Bigger file size due to overriding rules from earlier queries in later queries | Smaller file size due to starting with clean slate with no earlier rules to override |
| Images that get overridden/hidden later are still downloaded in WebKit-based browsers | Browsers only download images inside currently-applicable media query |
The bottom line
I think overlapping media queries is more often than not the most efficient way to go. Most sites don’t have radically different looks between media queries—sure, the layout might appear very different, but things like the typography, colors, and various visual effects are usually the same. Because of this, I’ve found that in most cases you’ll end up writing a lot more lines of CSS if you stack your media queries and don’t get to take advantage of the cascade. (Hooray for the cascade!)
However, if you do happen to have a site that has very different styles in each media query, stacking may be more efficient than overlapping. Also, because of WebKit’s downloading behavior, if you’re switching a lot of images between media queries, it may be wise to stack those queries instead of overlapping them, even if that means more lines of CSS and more chance of forgetting to update a rule inside multiple queries.
Which styles to make the “default”: mobile vs. desktop
While it’s possible to put all of your styles inside media queries, you’ll almost never see this, as browsers that don’t support media queries will get no styles whatsoever. What usually happens is that a chunk of styles sit outside media queries, either inside your single style sheet or in their own style sheet using a standard @import call or link, and these essentially become your “default” or starting styles. You then layer on one or more media queries to form alternate styles for different scenarios.
Media queries aren’t just for creating different layouts for different screen sizes, but let’s face it, that’s their primary use right now—and a dang good one. So that’s what I’m focusing on here: whether to make the small-screen styles the default or whether to make wider-screen styles the default. A few caveats, however:
- For ease of writing, I’m just going to refer to the smaller-screen styles as “mobile” and the wider-screen styles as “desktop,” even though mobile styles might be viewed on desktop devices and vice versa, and even though not all mobile devices support media queries.
- Also keep in mind that even though I’m simplifying it to two options for ease of discussion, you can have multiple media queries for each of these scenarios (such as 320px, 480px, and 600px “mobile” styles and 760px, 960px, and 1200px “desktop” styles), and any of these could be your default. You don’t have to make one extreme or the other the default.
- Finally, I’m specifically talking about which styles you make the default in the CSS itself, not about which version of the layout you first design for when coming up with wireframes, comps, and what-have-you. In a certain project, it may make the most sense to plan for the desktop design first, but when it comes time to build the media queries, make the mobile styles first. Nathan C. Ford talks about this in “Design for a Target Experience First.”
OK, are we all good? Good. Moving on.
Once again, WebKit’s downloading behavior is a big factor here. If you make the desktop styles the default and then override some of its assets in a mobile media query, either by setting new background images or setting elements that contain background or foreground images to display:none, Safari on iOS will still download the overridden or hidden desktop assets. The reverse is true: when mobile styles are the default, desktop Safari and Chrome will download all the mobile assets, even if overridden or hidden by the desktop styles.
Update May 23, 2012: It’s appears it’s not just WebKit that does this. Tim Kadlec found that nearly all browsers do this when you use display:none on the element as the way of overriding an earlier background image on that element. If you instead override a background image outside a media query with a different background image inside a media query, Tim found that a few versions of WebKit download both images (as well as Fennec 10+) but that other versions do not. This doesn’t apply if the media query uses min-device-pixel-ratio as the media feature it’s testing against, however. See tests 2, 4, and 7 on his “Media Query & Asset Downloading Results” article.
This behavior isn’t “wrong,” by the way. It’s common for browsers to download assets that are set to display:none, as it allows the content to be preloaded and ready-to-go if later revealed by a script, such as a hover effect over text that reveals a thumbnail image. WebKit doesn’t discriminate between situations where the currently hidden image might be needed later, such as if the user can and does resizes her viewport, or if it will never, ever be called into play.
If the downloading of the unneeded image is problematic in your case, there are a few ways to work around it:
- Cloud Four’s test page shows that if you set an image as a background on a
div, and then instead of hiding thedivyou hide thediv‘s parent element, WebKit won’t download the image. (Update May 23, 2012: Tim’s test 3 confirmed this, and not just for WebKit: all browsers except for Fennec did not download the image.) - You could simply choose to make your default styles neither mobile or desktop—just restrict them to basic text formatting, colors, and the like. Introduce layout and images only inside media queries.
- There are a variety of ways to use scripting to control what gets downloaded when. Read “Combining media queries and JavaScript” by Peter-Paul Koch for the general idea behind this, as well as “Responsive IMGs Part 2 — In-depth Look at Techniques” by Jason Grigsby for a discussion of a whole bunch of scripting techniques to load images on an as-needed basis.
These solutions won’t work with all sites, but they are options in some cases.
The pros and cons
| Mobile-default pros | Desktop-default cons |
|---|---|
| Prevents mobile devices from downloading unneeded desktop assets | Mobile devices may have to download unneeded desktop assets |
| Older, non-media-query-supporting mobile browsers still get the mobile styles without any extra work | Requires separate style sheets or JavaScript to make mobile design appear in IE Mobile and older mobile browsers |
| Mobile-default cons | Desktop-default pros |
|---|---|
| Requires separate style sheets or JavaScript to make majority desktop design appear in IE 8 and earlier | No extra work to make majority-width appear correctly on IE 8 and earlier |
| Desktop devices may have to download unneeded mobile assets | Prevents desktop devices from downloading unneeded mobile assets |
| Requires complete overhaul of existing CSS when retrofitting existing site | Easiest way to retrofit existing site |
The bottom line
In most cases, I would advocate making the mobile styles the default, as it allows old mobile browsers to see the correct mobile styles. Yes, desktop IE 8 and earlier will also see those mobile styles, but this is an easy to fix with some JavaScript—a much more quick and straightforward process than getting your mobile styles to apply to non-media-query-supporting mobile browsers when you make desktop the default. Also, the fact that mobile devices won’t have to download any of the assets you call in the media-query-desktop styles is a big plus, as mobile devices are more likely (but definitely not guaranteed) to have slower connections and more limited bandwidth than desktop devices. I’ve made mobile styles the default in the last two sites I worked on, and I anticipate this being my go-to method going forward.
While mobile-first design, as it’s been dubbed by Luke Wroblewski, has become quite popular, and I too will sing its praises, I don’t agree with those who say it’s the only viable way to craft media queries. Desktop-default has a fair share of merit too, as you can see in its pros list above. It’s a great way to retrofit an existing site, since you can add on a chunk of mobile styles instead of having to rewrite all the CSS from scratch. It’s also a great way to get your feet wet with media queries. Designing mobile-first is a big shift in both your mindset and design process, so sticking to desktop-first might be best on your first foray into creating a site utilizing media queries. In Chapter 6 of my book Stunning CSS3 , I take a desktop-default approach, as I think this is an easier way to first learn media queries. The default styles are targeted towards a size that most desktop users will see, with styles for both wider and narrower versions layered on. This means that even if I didn’t use JavaScript to fix IE 8 and earlier, or if the JavaScript doesn’t load, users will still see a layout that will work well for the majority of desktop viewport sizes—just the same as they would before we had media queries as an option. Media queries are used as progressive enhancement, not a requirement.
How to deal with IE 8 and earlier versions: conditional comments vs. JavaScript
Speaking of IE, you probably know that Internet Explorer 8 and earlier versions don’t support media queries. Since these browsers are likely a big chunk of your audience, I suspect you’ll want to figure out a way to get your media queries to work in IE 8 and earlier. You have a couple options: use conditional comments to feed separate media query sheets to different versions of IE or use a pre-fab script to make media queries magically work in IE.
To use conditional comments, you’ll vary your approach based on which set of styles, mobile or desktop, are your default. In either case, you’ll need to have your media queries separated into external sheets, not embedded in a single sheet along with your default styles.
If desktop styles are the default and you want to use conditional comments, you don’t need to really worry about desktop versions of IE 8 and earlier—they’ll get the desktop styles automatically. Sure, they won’t get any variations on those desktop styles that you might have included to better fit every particular viewport size, but if you’re using media queries as progressive enhancement, this isn’t likely to be a problem. Sure, it’s not ideal if an IE user with an 1100-pixel viewport sees the default 700-960-pixel styles instead of the 960-and-up media query, but it shouldn’t be a deal-breaker—the layout they see isn’t likely to look broken or be unusable. They shouldn’t know anything is “wrong.” So there’s nothing extra that has to be done for desktop IE 8 and earlier in this desktop-default scenario.
But the default desktop styles have a much bigger impact in the mobile version of IE used on Windows Phone 7. Luckily you can target it using a conditional comment and feed it your mobile-specific sheet:
<link rel="stylesheet" href="global.css" media="all">
<link rel="stylesheet" href="mobile.css" media="all and (max-width: 700px)">
<!--[if IEMobile 7]>
<link rel="stylesheet" href="mobile.css" media="all">
<![endif]-->
If mobile styles are your default, the situation is basically reversed: mobile-IE is fine without any extra work, but you have to feed desktop-IE the desktop styles using conditional comments. Again, we’re in luck when it comes to conditional comment syntax: it’s possible to craft a conditonal comment that targets IE 8 and earlier—which includes the version of IE on Windows Phone 7—but then also explicitly excludes mobile-IE:
<link rel="stylesheet" href="global.css" media="all">
<link rel="stylesheet" href="desktop.css" media="all and (min-width: 700px)">
<!--[if (lt IE 9)&(!IEMobile 7)]>
<link rel="stylesheet" href="desktop.css" media="all">
<![endif]-->
I learned about this handy conditional comment trick from Jeremy Keith’s article “Windows mobile media queries.” That Jeremy is one clever guy.
Both of these conditional comment variations don’t actually make media queries work in IE—they basically feed an alternate, static layout to IE, not based on viewport size or anything other than IE version number and platform, without any ability to switch dynamically between states. If you need more robust media queries support in IE 8 and earlier, or if you want to be able to embed your media queries in a single sheet, you can instead simply link to a ready-to-use script that detects your media queries and makes them work in non-supporting browsers. There are currently two such scripts:
- Respond by Scott Jehl: This script is much smaller than the other, and when you resize your window, it makes the changes appear right away without any lag. It’s main downside is that it only supports the
min-widthandmax-widthmedia features (the only ones I tend to use anyway!). Also, I found it triggered some totally obscure IE bug in a layout I was working on recently. But in general, I don’t have any problems with this script and highly recommend it. - css3-mediaqueries.js by Wouter van der Graaf: This script is bigger and slower than Respond, but it does support more media features and may be less buggy in some scenarios. It’s not my first choice, but it’s a great option too.
Both of these scripts will work in non-IE browsers too, by the way. If you want to limit them to IE so others don’t have to take the HTTP hit, hide the script call in a conditional comment:
<!--[if (lt IE 9)&(!IEMobile 7)]>
<script src="respond.min.js"></script>
<![endif]-->
The pros and cons
| Conditional comments pros | JavaScript cons |
|---|---|
| Works when JavaScript is disabled | Doesn’t work when JavaScript is disabled |
| All the pros of having external media queries | All the cons of having embedded media queries |
| Conditional comments cons | JavaScript pros |
|---|---|
| Feed IE only one static layout, rather than making media queries work so that layout can change on the fly | Makes existing media queries work so layout can change on the fly |
| All the cons of having external media queries | All the pros of having embedded media queries |
The bottom line
Plugging in a little script takes no effort whatsoever, so if you’re using embedded media queries, why not use one of the JavaScripts? For the small percentage of people using IE 8 and earlier with JavaScript turned off, I’m not concerned about them simply seeing the default styles; if mobile is default, they’ll get simple but readable and clean, and if desktop is default, it will probably look just right anyway!
Summing it all up
By now you’ve seen that there are lots of important decisions that go into how you set up your media queries for maximum efficiency, robustness, and ease of use. These decisions include whether to:
- Make the media queries embedded or external
- Overlap or stack your media queries
- Make mobile or desktop styles the default
- Use conditional comments or JavaScript to add support for IE 8 and earlier versions
Hopefully I’ve given you the information you need to make those decisions easier.
There are lots of other decisions you’ll need to make along the way that I haven’t covered, such as:
- Which media features to use (Use
max-widthormax-device-width? Useorientation? That sort of thing.) - Which dimensions to target, aka the breaking points in the design
- Which units to use for those breaking points (Pixels or ems?)
Don’t let all these decisions scare you off using media queries. Just know that, like any design technique, you can go about it with careful consideration or you can just quickly slap some sloppy media queries on your site. Think through the pros and cons of your media query configuration so that your media queries don’t just work, but work well.