A List Apart

Menu
Issue № 308

Taking Advantage of HTML5 and CSS3 with Modernizr

by Published in CSS, HTML, JavaScript30 Comments

Ten years ago, only the most cutting-edge web designers used CSS for layouts and styling. Browser support for CSS layouts was slim and buggy, so these people advocated for web standards adherence, while creating hacks that made CSS layouts work in all browsers. One hack that became widely used was browser sniffing: Detecting which browser and version the user had by looking at the navigator.userAgent property in JavaScript. Browser sniffing allowed for quick and easy code forking, allowing developers to target different browsers with different instructions.

Today, CSS-based layouts are commonplace and every browser has pretty solid support for them. But now we have CSS3 and HTML5, and the situation is repeating itself—different browsers demonstrate varying levels of support for these new technologies. We’ve smartened up, however, and no longer employ CSS hacks nor use browser sniffing—an unreliable, poor practice. We’ve also convinced more and more clients that websites don’t need to look exactly the same in every browser. So how do we deal with this new but familiar problem? Simple: We use feature detection, which means that we do not ask the browser “who are you?” and make unreliable assumptions from there on. Instead we ask the browser, “can you do this and that?” It’s a simple way to test browser capabilities, but doing all these tests manually all the time gets tiresome. To solve that problem (and others), you can use Modernizr.

Modernizr: the feature-detection library for HTML5 and CSS3

Modernizr is an open-source JavaScript library that makes it easy for web designers to support different levels of experiences, based on the capabilities of the visitor’s browser. This allows designers to take full advantage of everything in HTML5 and CSS3 that is implemented in some browsers, without sacrificing control over the user experience in other browsers.

When you embed the Modernizr script in your page, it detects whether the current browser supports CSS3 features like @font-face, border-radius, border-image, box-shadow, rgba() and so forth, as well as HTML5 features like audio, video, localStorage, and the new <input> element types and attributes. With this knowledge, you can take advantage of these native implementations in browsers that support these features, and decide whether to create a JavaScript-based fallback or simply gracefully degrade the page in browsers that don’t support them. Additionally, Modernizr makes the new HTML5 elements available for styling in Internet Explorer, so that you can start using their improved semantics right away.

Modernizr was created based on the principle of progressive enhancement, so it supports—encourages, even—building your website layer by layer, starting with a JavaScript-free foundation and adding layers of enhancement one by one. This is easy with Modernizr, since you’ll know what the browser is capable of. Let’s look at a practical example of how to use Modernizr to build cutting-edge websites.

Getting Started with Modernizr

Start by downloading the latest stable release at www.modernizr.com, where you can also see the full list of features it detects. Once you have the latest version, 1.5 at the time of this writing, include it in your page’s <head> section:

<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8">
 <title>My Beautiful Sample Page</title>
 [removed][removed]
</head>
…

Next, add the class “no-js” to the <html> element:

<html class=“no-js”>

When Modernizr runs, it will replace that class with the class “js” which allows you to know, in your CSS, whether or not JavaScript is enabled. But Modernizr doesn’t stop there: It will add classes for every feature it detects, prefixing them with “no-” if the browser doesn’t support it. So, your <html> element will look something like this upon pageload (providing JavaScript is enabled):

<html class=“js canvas canvastext no-geolocation rgba hsla multiplebgs borderimage borderradius boxshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions video audio localstorage sessionstorage webworkers applicationcache fontface”>

Modernizr also creates a JavaScript object, simply called Modernizr, which has a list of properties that contain boolean values for each feature. Modernizr.canvas will be true if the browser supports the new canvas element, and false if the browser does not support it. The JavaScript object also contains more detailed information about certain features, for example: Modernizr.video.h264 will tell you if the browser supports that particular codec. Modernizr.inputtypes.search will tell you if the new search input type is supported, and so forth.

Our raw sample page is looking a little bare-bones, but it’s highly semantic and accessible. Let’s give it some basic styling; some formatting, color, and layout to make it a little more visually pleasing. So far, this process is nothing new: we’re adding simple CSS to a semantically structured HTML page, and the result—while basic—is very straightforward.

Next, let’s enhance the page and make it more interesting. I want to use a fancy (open license) font for the h1 header, split the list of features into two columns, and move the section about Modernizr with a photo to the right of everything. I’m also going to change the border around the page and make it nicer. Now, CSS is pretty great because you can just add new properties to a rule, and if the browser doesn’t recognize (read: support) them, it simply ignores them. Combine that with the cascading nature of CSS, and you can use things like border-radius without having to rely on Modernizr. However, using Modernizr does offer something you can’t achieve without it: The ability to change properties that the browser does support, only on the condition that it supports some other property. I’ll illustrate this by adding two new CSS rules to our page:

.borderradius #content {
 border: 1px solid #141414;
 -webkit-border-radius: 12px;
 -moz-border-radius: 12px;
 border-radius: 12px;
}
.boxshadow #content {
 border: none;
 -webkit-box-shadow: rgba(0,0,0, .5) 3px 3px 6px;
 -moz-box-shadow: rgba(0,0,0, .5) 3px 3px 6px;
 box-shadow: rgba(0,0,0, .5) 3px 3px 6px;
}

The first rule adds a nice 12 pixel rounded corner to our #content element. However, the original rule we had for #content specified a border of “2px outset #666”, which looked nice when the box had square corners but with rounded corners, it’s not so nice. Thanks to Modernizr, I can instruct the browser to render only a solid, one-pixel border if the browser supports border-radius.

The second rule takes this a little bit further by adding a drop shadow to the #content element, and removes the border altogether if the browser supports the box-shadow property. Why? Because most browsers don’t render the border-with-border-radius combination, and box-shadow very nicely. (A flaw in browsers, it should be noted, but something we have to live with for now.) I’d rather use the drop shadow around our element than not use a drop shadow and only use a border. This way, I can have the best of all worlds, or rather, the best of all CSS: browsers that support the box-shadow property will show us a nice drop shadow, browsers that only support border-radius will show us nice rounded corners with a thinner border, and browsers that support neither get our original two-pixel outset border.

In our next example we add a special font for our header. Here’s our original h1 declaration, which we’re not changing but I’m highlighting here for the example:

h1 {
 color: #e33a89;
 font: 27px/27px Baskerville, Palatino, "Palatino Linotype", 
        "Book Antiqua", Georgia, serif;
 margin: 0;
 text-shadow: #aaa 5px 5px 5px;
}

This declaration worked fine for our basic page, and the font size of 27 pixels is fine for all of these fonts, but it is far too small for our custom font, named Beautiful. Let’s add the CSS rules to use it:

@font-face {
 src: url(Beautiful-ES.ttf);
 font-family: "Beautiful";
}
.fontface h1 {
 font: 42px/50px Beautiful;
 text-shadow: none;
}

First, we add the @font-face declaration wherein we specify the path, filename, and font-family name for our custom font. Then we add the font choice in a CSS rule to our h1 element, but as you can see, I’m giving it a much bigger font size. That’s because the Beautiful font renders much smaller than all the other fonts I specified for the h1 element, and so we have to instruct the browser to render our header at a much larger size, but only when it renders our custom font.

Additionally, our beautiful script font has some issues with text shadow rendering, so I’m removing the shadow when the browser decides to render the custom font. Also, the list of features still needs to be split up. To do so, I want to use the awesome CSS columns feature, which allows browsers to intelligently separate a list into columns without messing up its order—and our list, though not visibly numbered, is ordered alphabetically. Of course, not every browser supports CSS columns yet, so for those browsers we’ll just use floats to make the list two columns; it’ll no longer be alphabetically sorted (visually), but it’s better than nothing:

.csscolumns ol.features {
 -moz-column-count: 2;
 -webkit-columns: 2;
 -o-columns: 2;
 columns: 2;
}
.no-csscolumns ol.features {
 float: left;
 margin: 0 0 20px;
}
.no-csscolumns ol.features li {
 float: left;
 width: 180px;
}

Again, I’m using Modernizr to apply very common properties only under certain circumstances—something I couldn’t do via property overloading or the cascade. If the browser supports CSS columns, our work here is done: the list looks great and is still alphabetically ordered. If the browser doesn’t support CSS columns, as determined by the “no-csscolumns” class added to the <html> element in that scenario, we float our list items and apply some margins and widths to get a similar result. It’s not as good, but it’s an improvement over one long, single-column list.

As you may have noticed, I’m specifying my columns differently from the border-radius and box-shadow properties in the examples above. That’s because, for one, Opera only supports CSS columns via the vendor prefix at this time, and two, Mozilla doesn’t recognize the “columns” shorthand property yet, hence the use of -moz-column-count for that browser.

With these and a couple of other, similar changes, our new page looks decidedly better.

Let’s finish this tutorial by adding even more progressive enhancement to our page. WebKit-based browsers support some pretty darn cool things—CSS transitions, animations, and three-dimensional transforms—and I want to show some of that off in our final step. Again, some of these properties could simply be added to our existing CSS rules and get safely ignored by browsers that don’t support them, but other properties would interfere with the layout in all browsers if I added them just like that, so I’m using Modernizr to control very precisely when these enhancements are applied and when they are not.

First we set the stage:

@-webkit-keyframes spin {
   0% { -webkit-transform: rotateY(0); }
 100% { -webkit-transform: rotateY(360deg); }
}
.csstransforms3d.cssanimations section {
 -webkit-perspective: 1000;
}

The @keyframes rule is part of the new CSS animations specification, which, currently, only WebKit supports. It allows you to declare a full animation path, with whichever properties you want, and change them at whichever steps you’d like. Note that you don’t specify a duration in this declaration, only the key frames themselves—you specify things like the duration, loop count, and easing curves when you apply the animation in a CSS rule. This allows you to re-use a particular animation at different speeds on different elements, and thus gives you the greatest flexibility. For more information on using animations, see the W3C Working Draft specification.

Next, we apply our animation—which makes an element rotate around in 3D space—to our secondary header:

.csstransforms3d.cssanimations section h2 {
 background-image: url(modernizr-logo.png);
 overflow: hidden;
 -webkit-animation: spin 2s linear infinite;
}

Because we’re now rotating in 3D space, I’d like the background image of the Modernizr logo to look nice and anti-aliased, so I’m replacing it with a PNG version. I’m also adding an overflow:hidden property to hide the original text in the header, which we were offsetting to -9999px. Rotating the element in 3D made it show up during the rotation, which was amusing but didn’t look very nice. Lastly, we apply the animation rule, set it to take two seconds, spin it in a linear fashion, and keep it going indefinitely.

Our final page looks great, and even has some fun animation going on in WebKit browsers. I hope you now have a good understanding of how much more control you get over your websites with Modernizr, and how much easier it makes doing truly progressive enhancements. That’s not all that Modernizr is good for, though; it’s also invaluable for creating JavaScript-driven fallbacks and using cool new features from HTML5—but that’s a topic we’ll have to discuss another time.

About the Author

30 Reader Comments

Load Comments