Taking advantage of html5 and css3 with Modernizer
Issue № 308

Taking Advantage of HTML5 and CSS3 with Modernizr

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.

Article Continues Below

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#section2

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#section3

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>
 <meta charset="utf-8">
 <title>My Beautiful Sample Page</title>
 <script src="modernizr-1.5.min.js"></script>

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

Faruk Ateş

Faruk Ateş is a designer, developer, and entreprenerd. He’s the creator of Modernizr, co-founder of Presentate, and a public speaker. In his alternate life he performs improv comedy and fights hard for justice, like Batman but without the wealth, mansion, gadgets, butler, cape, or personal theme song.

30 Reader Comments

  1. This is a wonderful article. It has excited me greatly to jump on the HTML5 bandwagon. I’ve been hesitant with wanting to start using HTML5 and CSS3 for this exact reason. I don’t like having to play with CSS hacks and alternate stylesheets for detecting what kind of a browser the person is using. This will save a lot of hassle and using the term “gracefully degrading” is spot on. Thanks so much for the post.

  2. I hate to be the one to say this, but I do take issue with the font choice. “Beautiful ES” looks, smells, walks and talks like a vector-for-vector rip of Bickham Script, a typeface that _isn’t_ free.

    This is not uncommon on Fontsquirrel, sadly, and it does not improve the state of web fonts if piracy (or at least blatant copying) is condoned by the sites that help fuel the movement. I also realise this is not the right podium, perhaps, but it is a matter that will become more and more relevant as people optimise their typefaces for the screen.

    I’m not faulting you, Faruk. I love this article. Especially the part about the @font-face rule, to set a different font size depending on the ability to show a different font. It’s supremely useful. In light of such generous qualities, using a rip-off is a blemish I think it could do without.

    (Reasons I think it’s a rip-off: vectors are one-for-one overlay-checked rips; if you have the implied drawing skills to make this TrueType font work, how come the metrics are so horribly off? It doesn’t compute. It’s a rip.)

  3. @*comet*: I’m happy to hear that! This is precisely the goal of Modernizr, to make starting with HTML5 and CSS3 more tenable.

    @*catchmyfame*: what page are you talking about, the article or one of the sample pages? If you mean the sample page with the @font-face inclusion, that could well be because the browser loads fonts asynchronously and thus may start rendering the page and change it once the font is loaded. It’s not ideal, but browsers have opted this mechanic to prevent pages from feeling like they load really slowly, hanging on a font to download first.

    @*Rob*: I didn’t know that, and that is a shame. I merely picked “Beautiful ES” because it is both a unique typeface with no comparable equivalent that comes pre-installed on people’s systems, and because it’s such a size distortion (needing a very different font-size setting to appear similar in size to fallback fonts).

    It is indeed unhelpful with this being commonplace (on Fontsquirrel or just the web itself), but unless there’s an easy way for people to know or find out about these things, it’s hard to combat. I’m no type aficionado (I appreciate good type design, but I don’t have a vast knowledge of it), and so I wasn’t aware that “Beautiful ES” was a rip. That is unfortunate.

    *Note to future commentators*:

    Some small notes on the Modernizr use in this article: it is limited to introducing the basics of fine-grained control over the look & feel of a website, and examples were kept rudimentary and clean for simplicity’s sake. For a more robust and complete @font-face implementation method, I recommend you read “Bulletproof @font-face syntax”:http://paulirish.com/2009/bulletproof-font-face-implementation-syntax/ by Paul Irish, who is also the biggest contributor to Modernizr these days.

    Modernizr is also particularly useful for handling fallbacks to HTML5 features in JavaScript, but the scope of the article just couldn’t support going into depth on that at all. Perhaps that part will be an article of its own one day.

  4. I guess Internet Explorer is our adopted child.
    But the example is not working for IE (I’ve tried on 6, 7 and 8)
    And @font thing is fully supported in IE since version 4!
    So… modernizr isn’t such a good library, and we, as front end developers, have still some work to do 🙂

  5. It’s as simple as that. My customers can’t afford to pay me to fix browser incompatibilities. They all support tables … so tables it is. When they all support CSS columns, then I’ll use them.

    I’m not a designer … can you tell? I’m a developer who’s interested in functionality with merely adequate aesthetics. My customers hire me to create complex back-end workflow solutions. If they want pretty too then we have to hire the pretty.

    But I keep reading these articles and hoping things will improve. When I can create equal height columns in CSS simply (meaning no floats and huge negative margins hacks) then I’ll stop using tables and start using CSS for this.



  6. To *tomasdev*: the custom font not working in Internet Explorer is correct; the examples have been kept super simple to keep the complexity of the article down. As such, the example code used to do the font excludes an .eot font, thus it won’t work in IE.

    Read the aforelinked “Bulletproof font-face syntax”:http://paulirish.com/2009/bulletproof-font-face-implementation-syntax/ to get the full details on working with fonts and full cross-browser compatibility. It’s an article all by itself!

    To *RobShaver*: well, you _can_ use CSS columns now. IE doesn’t support them, but the latest Firefox does, so do Chrome and Webkit (and latest Opera if you care about that). Using tables for layout is hurting not just yourself but also your customers, though, so if you’re more a back-end guy perhaps what they need is to pay at least enough for you to work with a front-end developer.

  7. Once again, another excellent article. Also, I have been watching and waiting on Modernizr for a while waiting specifically for SVG detection support. So, to find a new article on my favorite magazine talking specifically about Modernizr along with news about version 1.5? I could not be more delighted.

    For my own personal projects I have become obsessed with HTML5, CSS3 and using them in conjunction with progressive enhancement. I’ve been daydreaming about how awesome SVG implementation could be if I could control it with something like Modernizr. So, thank you for this article!

    On an aside, your example page causes Mac IE5 to crash. Not that I support it or expect others to do so but just fyi.


  8. To *Ritz*: which example page in particular causes the crash? Because as much as we doubt many people in the world at all are still supporting IE5/Mac, making it crash is perhaps worth investigating.

  9. As everyone else has said, what a great article – this looks like a really useful way of getting all these HTML5+ features onto our sites with minimum pain. Thanks.

    A tangential note: Chrome’s aliasing on transformed images is just plain ugly, but nicely anti-aliased on Firefox. Also, the first time I loaded the sample page on Firefox it was rendering as if it didn’t have @font-face, even though it showed up in the list of features. A quick F5 fixed that though.

  10. I’ve looked at Modernizr a couple of times in the past but couldn’t really see the usefulness of it as I thought if a browser didn’t support a CSS feature, it just would skip over it, but your examples have illustrated how you can have more fine grained control. I’ll have to take another closer look for future projects.

  11. *Faruk*, the raw sample page and its simple css styled page (the first two examples) render well enough in IE 5 on the Mac. However, examples after that (sample-medium.html and forward) crash. I did isolate it down to the _@font-face_ css rule for the sample-medium.html page so I’m going to guess the same issue applies to the other example. If it provides consolation, http://www.zeldman.com/ use of custom fonts also makes IE 5 crash.

  12. Seriously… These are some real, quality, unique, and useful articles you got here, and this tool sounds very helpful, thanks a lot.

  13. So it would appear that IE5/mac crashes when using @font-face. I guess that’s one way to eradicate any last remnants of use that browser has.

    Thanks for reporting, *Ritz*.

  14. Great article. The control freak side of me is intrigued by the power of Modernizr to control the small details of the user experience in various browsers.

    But it all reminds me of browser sniffing in 2002. we added chunks of code that were essentially hacks, and were rendered useless in short time as MS browsers slowly began adhering to standards. Will Modernizr be the same when IE8 or IE9 comes out, and presumably won’t require a shiv for HTML5, and will render CSS3 correctly (again, presumably.)

    Earlier in the article, you reminded us that “Websites don’t need to look exactly the same in every browser.” Having no interest in repeating the ugly kludges of a decade ago, I will stick to that mantra: “Websites don’t need to look exactly the same in every browser,” and revel in the simplicity and beauty of standards-compliant code.

    Thank you for the thought-provoking article.

  15. Well I’ll leave the title, but after a bit of poking and prodding I’ve changed my stance…

    Originally I was going to ask about the increase in size of the CSS, download times of the ttf, and overall heavier load on our servers, but here’s the stats (Chrome 5.0):

    Plane Jane version (v1): 3kb
    Styled, square corners: 6.9kb
    Styled, round corners, font: 8.33kb
    Finished site, animation, font: 9.03kb (though on one fresh load the fonts came through at 88kb – not sure what happened there. Anyone have thoughts?)

    So an extra 6kb for a font, nicely rounded corners and a drop shadow – not bad at all! The sprite you’d need for that as well as the CSS positioning would account for at least that much, but be a way bigger P.I.T.A.!

    The only problem of course is when a browser doesn’t support, say for instance, the rounded corners, you would have to use CSS sprites or the like anyways – if you have that client who must have things perfect. So this may not be an option for a little while. However, as the link in the article so convincingly pointed out, websites don’t have to look the same in all browsers 🙂

  16. To *Seanh*: fonts are typically between 50 and 200 kb, but that’s a tradeoff a lot of people will want to make because richer typography is an important part of branding. Having the font in a text format that can be adjusted and reused easily is tremendously valuable. However, font files are typically cached after the first load, so from then on it will be much faster.

    As for the rest, it’s as you say: only IF the client must have things perfect. But guess what: if they do, you can charge them more money because it means more time and more work. So, you either stand to make more, or you won’t have to. Win-win for everyone except people stuck on older browsers (who you can at least cater to nicely with Modernizr).

  17. Great article. I’m keen to try Modernizer myself.

    However, I know it’s common practice now to place classes on the HTML element. But doesn’t this make our code invalidate?

    Shouldn’t elements modified through javascript still be able to validate? (Upon asking Zeldman over Twitter, his answer was yes.)

  18. To *Binyamin*: Modernizr doesn’t enable CSS3 support in any browser, it only detects IF the browser supports (some of) these things. Since IE6 & 7 don’t support anything in CSS3, their results are false.

    However, a new library, CSS3PIE, does add some functionality to IE6 & 7: “CSS3 Pie”:http://css3pie.com/

    To *leepowell*: classes on the @html@ element are valid under HTML5. Use an HTML5 Doctype and you’ll be fine, if you really want to validate. 🙂

    (note: validation is not anywhere near as important as _things working properly_, and classes on the @html@ element do work in every browser no matter the doctype)

  19. Have never used Modernizr before, and your examples have illustrated how you can have more fine grained control. I’ll have to take a look for future projects.

  20. This is a great tool, and I’m giving it a try in my next project in conjunction with HTML5 Boilerplate.

    One question I have is why specify both _class_ and _no-class_? When writing css I would rather code for the future and specify fallbacks (i.e. only use the .js to specify _no-classes_), or on more conservative projects code for reliability and specify progression (use the .js to specify only working _classes_).

    Maybe I am being a neat-freak and focusing more on human readable source code than makes sense, but is there a reasoning behind including both? Would you consider including a config in future releases to specify including _class_, _no-class_, or both?

  21. that is amazing, i just came across modernizr and thought id read a bit more about it, not only is your article top notch, but modernizr is complete class!

  22. First of all, thank you for the article. Very well written with a straight forward (progression) example.

    Can you explain the illustration at the top of the article? I really don’t get what it has to do with the article.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA