April 2012

Volume 27 Number 04

Building HTML5 Applications - Using CSS3 Media Queries to Build a More Responsive Web

By Brandon Satrom | April 2012

This article discusses Internet Explorer 10 Platform Preview. All information is subject to change.

A few years ago, I was approached by a client who wished to build a mobile-only Web site for its young, tech-savvy user base. The project was actually pitched to us as an “iPhone site,” and came with a fully conceived design. Excited to create such a cutting-edge mobile app, we began in earnest and made decent headway—until it came time to consider other mobile devices on the market. This client wanted support for just about all of them. Thus began our valiant effort to answer two of the most important questions Web developers face when attempting to build mobile-tailored experiences: How do you know when to change the experience for a given mobile browser, and how do you go about making that change when one is required?

Responsive Web Design

These questions are at the heart of responsive Web design, a term coined by Ethan Marcotte (bit.ly/aO2cFa). Responsive Web design is about using context clues from the browser to tailor the way a site is presented to a user, and even the way it behaves. It’s about responding to information presented by the browser with a customized and compelling experience that fits a user’s interaction expectations on that device. If the right clues reveal enough information about the browser and device in question, it becomes feasible to provide customizations, such as alternate images and fluid layouts.

Traditionally, you get those context clues by parsing the User Agent string presented by the browser—a practice called browser or UA sniffing—and then using that information to determine how to proceed. I covered UA sniffing in my September 2011 article, “No Browser Left Behind: An HTML5 Adoption Strategy” (msdn.microsoft.com/magazine/hh394148). As I noted then, browser sniffing is unreliable and has essentially been replaced in favor of feature detection, which helps you make decisions about markup, CSS and JavaScript, based on available features, and not on a given browser and version.

In the context of responsive Web design, all modern desktop and major mobile browsers support a feature called media queries, a CSS3 module that extends the idea of media types introduced in CSS 2.1—the ability to specify alternative style sheets for print, screen and the like—with the ability to determine some of the physical characteristics of a device visiting a site. Similar to the rationale for using feature detection, media queries allow you to focus on getting the right context clues for a current visitor, which you can then use to responsively tailor the delivered experience. The power of media queries is that they provide such clues to your style sheets.

In this article, I’ll provide an overview of the media queries CSS3 module. I’ll briefly discuss its syntax and use, and then show a simple example that leverages media queries to build smartphone- and tablet-friendly views for an online photo gallery. Finally, I’ll share a tip essential for all forms of mobile development before wrapping up with a brief discussion on media query listeners, a separate World Wide Web Consortium (W3C) specification (bit.ly/wyYBDG) that provides an API for reacting to changes in the media environment via JavaScript. By the end of this article, you should have a solid foundation for creating responsive Web sites and applications using only CSS and some tailored styles.

CSS Media Queries

As mentioned previously, CSS 2.1 currently supports media-dependent style sheets based on “media type” declarations (bit.ly/xhD3HD). The following markup illustrates how you can use media types to specify conditional style sheets:

<link rel="stylesheet" type="text/css" href="main.css" media="screen" />
<link rel="stylesheet" type="text/css" href="print.css" media="print" />

With these elements in place, a site will load a different style sheet from the default (or “screen”) style sheet when a page is loaded in print preview mode. As useful as this feature is (though historically under-utilized), it doesn’t provide any useful context clues to use for building truly responsive Web sites. And though the media types specification details the use of 10 media types, browser vendors never fully embraced all of the specified types.

The “media” attribute lays a solid foundation, though, and the W3C decided to build media queries (bit.ly/zbIeDg) right on top. As you specify a media type (such as print, screen or all) media queries let you add expressions to the media attribute that check for the presence of certain media features, such as the width, height, orientation and even screen resolution of the current device. Here’s an example that loads the main.css style sheet if the media type is screen and the device width is at least 800 pixels:

<link rel="stylesheet" media="screen and (min-width: 800px)" href="main.css" />

The media query is what’s inside the parentheses. The left side specifies the property to test (width), with an optional min- or max- modifier, and the right side specifies the value of that property. If the device or browser in question presents a screen width of at least 800 pixels, the styles in main.css will be applied. If not, they won’t. This illustrates how media queries give developers useful context clues for creating responsive Web sites and applications.

Getting Started

As noted, CSS3 media queries are supported in the latest versions of all major browsers (bit.ly/wpamib), so you should feel comfortable using them today. There are three ways to leverage media queries for your sites. The first is to conditionally load entire style sheets, as shown in the previous example. The second method is to use @import directives in your style sheets. Here’s the same test, this time as an import directive:

@import url("main.css") screen and (min-width: 800px);

Just as in the first example, this statement will evaluate the width of the current device and, if it’s at least 800 pixels, load main.css and apply its styles.

Finally, you can use media queries inline in CSS using @media directives, like so:

@media screen and (min-width: 800px) { ... }

In this case, instead of defining styles in a separate file, you place them inline in an existing style sheet, and wrap them with a media query that ensures these styles are applied only when appropriate.

Now let’s look at media queries in action, starting with a simple example. If you want to try this at home, you can check out the sample source code for this article, or open up your favorite IDE or text editor and add the markup in Figure 1.

Figure 1 A Sample HTML Document

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="media.css">
  </head>
  <body>
    <article id="small">This is a small screen</article>
    <article id="medium">This is a medium screen</article>
    <article id="large">This is a large screen</article>
    <article id="landscape">... and you're in landscape mode...</article> 
  </body>
</html>

Next, create a style sheet called media.css in the same folder, and add the styles defined in Figure 2. Notice that I’ve defined all of my @media directives at the bottom of the file; you’ll likely want to do the same in your style sheets. If you choose to define multiple style sheets in your markup, you’ll also want to place elements for specific style sheets after your main style sheets. Due to the cascading behavior of CSS, your media-specific rules will probably need to supersede your primary rules and should be defined appropriately. You’ll notice that I’ve defined styles for my article and body elements, and then followed these with media queries (and their associated rules) for each experience I wish to support. Screens smaller than 480 pixels will receive one set of additional styles; screens between 480 pixels and 1024 pixels will receive another; and screens larger than 1024 pixels receive a third set of styles. I’ve also added a query for orientation, and will apply yet another set of styles if the device is in landscape mode.

Figure 2 CSS Media Queries Defined Inline with the @media Directive

article {
  display: none;
}
body {
  font-size: 24px;
  font-weight: 800;
  color: white;
}
@media screen and (max-width: 480px) {
  body {
    background-color: #ff2a18;
  }
  #small {
    display: inline;
  }
}
@media screen and (min-width: 480px) and (max-width: 1024px) {
  body {
    background-color: #00ff00;
  }
  #medium {
    display: inline;
  }
}
@media screen and (min-width: 1024px) {
  body {
    background-color: #0000ff;
  }
  #large {
    display: inline;
  }
}
@media screen and (orientation: landscape) {
  body {
    background-color: #ff7f00;
  }
  #landscape {
    display: inline;
  }
}

Once you’ve created the page in Figure 1 and style sheet in Figure 2, open the page in any browser. Testing your media queries is simple; you can even do so without a mobile device. Just resize your browser window and watch how the text and background colors change as the context of the visible window changes.

Now that we’ve laid a foundation for media queries, let’s take things a step further and walk through a more realistic example. For this example I’m going to add media queries to an online photo gallery so I can support smartphones and tablets. You’ll find the code for this example, which uses the Photo Gallery template that ships with WebMatrix (webmatrix.com), at msdn.com/magazine/msdnmag0412.

If you open this page in a mobile or tablet browser, you’ll notice the page appears “zoomed out” and is difficult to see or read, as Figure 3 shows. With a few media queries, and some other tricks, we can create a tailored experience without new markup or browser sniffing of any kind.

Mobile View Without Media Queries
Figure 3 Mobile View Without Media Queries

At the bottom of the desktop.css file created for this sample, start by adding your media query directive and condition. Because my current concern is the smallest of screens, I’ll create the following definition:

@media screen and (max-width: 480px) {}

Now, inside that definition, let’s create some rules to resize and reposition the images for smaller screens. Those rules are shown in Figure 4.

Figure 4 Modifying Styles for Mobile Devices

body {
  min-width: 120px;
  max-width: 320px;
}
h1 {
  font-size: 1.5em;
}
img {
  width: 250px;
  height: 187.5px;
}
ul.thumbnails li {
  width: 265px;
    height: 200px;
    line-height: 200px;
}
ul.thumbnails li span.image-overlay {
    width: 265px;
}

If you refresh the page after adding these styles, you should see something similar to what’s shown in Figure 5. The images look better, but the main navigation is misaligned. What’s more, the gallery uses a CSS :hover pseudo-selector to display additional information about each image, as shown in Figure 6. Because this is a user experience that doesn’t make sense in an environment where hover events are less common (smartphones and tablets), we need to come up with an alternative presentation for that aspect of the page as well. We’ll do so by adding the styles in Figure 7 inside the current @media directive.

Mobile View with Improved Images
Figure 5 Mobile View with Improved Images

An Image with the Hover Effect Applied
Figure 6 An Image with the Hover Effect Applied

Figure 7 Styles for Mobile-Friendly Navigation and Image Info

#banner {
  height: 110px;
  background: #eaeaea;
}
#menu li {
  display: block;
  margin-top: 3px;
  margin-bottom: 3px;
}
#menu li.tags a,
#menu li.galleries a,
#menu li.account a {
  background: none; 
}
#menu li.login {
  display: none;
}
ul.thumbnails li span.image-overlay {
  display:block;
  position: absolute;
  top: 0;
  left: 0;
  line-height: normal;
  width: 515px;
    padding: 5px;
}
ul.thumbnails li {
    background: #f3f6f7;
    border-color: #dbe2e5;
    border-radius: 7px;
    box-shadow: 0px 0px 20px 5px #A9A9A9;
}

Now, if you refresh the page in your browser, you should see cleaned-up navigation, along with the information that previously appeared on a hover event, similar to Figure 8.

The Gallery with Better Navigation and Image Boxes
Figure 8 The Gallery with Better Navigation and Image Boxes

So far, we have a desktop view for our gallery, as well as a decent mobile version, and all we needed were a few additional CSS rules. Let’s take this a step further and add support for screens larger than 480 pixels and smaller than 1024 pixels—tablets.

If you’re viewing the photo gallery in a desktop browser and you resize to a size smaller than 1024 pixels (the screen shown in Figure 8), you’ll notice that many of the images are cut off. Because tablet devices generally have a larger screen size, it makes sense to provide larger images on the gallery page. So let’s add another media query for tablet-size devices:

@media screen and (min-width:480px) and (max-width:1024px)

In the online source, I’ve refactored some of the smartphone rules into a set of styles that apply to all non-desktop devices, so all I need to set for my tablet view are those styles that relate to document, image and thumbnail size, as shown in Figure 9.

Figure 9 Styles for the Tablet-Friendly View

body {
  min-width: 480px;
  max-width: 800px;
} 
img {
  width: 500px;
  height: 375px;
  align: center;
} 
ul.thumbnails li {
  width: 515px;
    height: 390px;
    line-height: 200px;
}

Now refresh the screen in a tablet-view browser, and you’ll see something similar to Figure 10. We’re now targeting multiple screen sizes—and thus multiple devices—and all it cost us was the time to create some alternative styles.

Photo Gallery, Tablet-Friendly View
Figure 10 Photo Gallery, Tablet-Friendly View

Mobile First

Before I wrap up this discussion on media queries, I want to share a tip that isn’t specific to the use of media queries, but is important when doing mobile Web development of any kind. In particular, you should set the viewport and design for mobile first.

If you’ve been coding along as I’ve walked through media queries in this article, you might have seen a slight issue when viewing the page on a smartphone or smartphone emulator: On some devices, the “zoomed out” effect didn’t change, even after the conditional rules were applied. What’s happening is that the mobile browser is trying to “optimize” the experience on a smaller screen, which is helpful for sites that pay no attention to mobile users. For this site, however, the preference is that this optimization doesn’t occur, and you can override this behavior by adding a single <meta> tag to the <head> of your page, like so:

<meta name="viewport" content="width=device-width">

Now, if you refresh the page, and your media queries are properly defined, you should see your rules in effect.

You might have also taken note of the fact that, in the photo gallery example, I added a mobile experience to an existing site. As a developer familiarizing yourself with media queries, you’re likely do this initially as well. However, there’s a growing movement that emphasizes a “mobile first” approach to designing and developing Web sites and applications. Just as it sounds, mobile first is about making your mobile experience a top priority, instead of an afterthought (as was often done in the past). When you consider that in the last two years, smartphone and tablet sales have outstripped PC sales and that mobile browsing now makes up more than half of all Web browsing, this makes sense. Beyond that, a mobile-first mindset encourages “progressive enhancement,” whereby sites are built from a baseline (and often text-only) experience, and progressively enhanced for more capable browsers and devices. To learn more about the mobile-first approach, I highly recommend the book by that name, by Luke Wroblewski (available at bit.ly/zd9UWT).

Media Query Listeners

As an extension to media queries support in CSS3, the CSS Working Group is looking to add JavaScript APIs that enable developers to evaluate media queries at run time and listen for runtime changes in media query conditions. These APIs are documented in the CSSOM View Module specification (bit.ly/w8Ncq4). Figure11 shows an example of performing media queries and creating listeners from within JavaScript.

Figure 11 Working with Media Query Listeners

listener = window.msMatchMedia("(min-width: 480px)");
evaluate(listener); // Perform an initial evaluation
listener.addListener(evaluate); // Evaluate each time the width changes
 function evaluate(listener) {
   if (mql.matches) {
    expandCommentList(); // Screen is at least 480px
   } else {
     shrinkCommentList(); // Screen is smaller
   }
 }

Listeners have the potential to be quite valuable for cases where a device’s viewport might change at run time—for instance, when the orientation changes on smartphones and tablets. With media query listeners, you can respond to these changes, not just from within CSS, but from your scripts. The CSSOM View specification is currently in a Working Draft state, but Internet Explorer 10 Platform Preview provides support for media query listeners, so you can try this out at Internet Explorer Test Drive (bit.ly/gQk7wZ).

The ability to support multiple platforms, screens and experiences in Web applications has come a long way over the last several years. Whereas in the past it was normal to create device-specific versions of a site, the proliferation of mobile-friendly devices has quickly made device targeting unfeasible. Thankfully, the introduction of CSS3 media queries across all major desktop and mobile browsers means you can now use the context clues that really matter to deliver tailored experiences for a wide range of devices, all with conditional CSS. For more information on CSS3 media queries, I recommend the chapter on media queries from Peter Gasston’s excellent “The Book of CSS3” (No Starch Press, 2011). For a gallery of great experiences delivered with the help of media queries, check out the media queries site. I hope this article has equipped you with enough information to get started building tailored experiences today. I look forward to seeing the awesome experiences you create!


Brandon Satromis the product manager for Kendo UI (kendoui.com), an HTML5 and mobile toolset from Telerik. He can be followed on Twitter at twitter.com/BrandonSatrom.

Thanks to the following technical experts for reviewing this article: John Box, Sharon Newman and Jacob Rossi