Browser sniffing with conditional comments

May 13th, 2010. Tagged: browsers, CSS, IE, performance

Browser sniffing is bad. But sometimes unavoidable. But doing it on the server is bad, because UA string is unreliable. The solution is to use conditional comments and let IE do the work. Because you're targeting IE most of the times anyway.

In fact IE8 is a decent browser for the most practical purposes and often you're just targeting IE before 8.

Conditional comments in practice use the following pattern:

  1. Load the decent browsers CSS
  2. Conditionally load IE6,7 overrides

The drawback is that IE6,7 get two HTTP requests. That's not good. Another drawback is that having a separate IE-overrides stylesheet is an excuse to get lazy and instead of solving a problem in a creative way, you (and the team) will just keep adding to it.

We can avoid the extra HTTP request by creating our CSS bundles on the server side and having two browser-specific but complete stylesheet files:

  1. The decent browsers CSS
  2. The complete CSS for IE6,7 not only the overrides

Then the question is loading one of the two conditionally without server-side UA sniffing. The trick (courtesy of is to use conditional comments to comment out the decent CSS so it's not loaded at all:

<!--[if lte IE 7]>
  <link href="IE67.css" rel="stylesheet" type="text/css" />
<!--[if gt IE 7]><!-->
  <link href="decent-browsers.css" rel="stylesheet" type="text/css" />

The highlighting suggests what the decent browsers see.

IE6,7 see something like this after the conditional comments are processed:

  <link href="IE67.css" rel="stylesheet" type="text/css" />
  <link href="decent-browsers.css" rel="stylesheet" type="text/css" />

Tell your friends about this post: Facebook, Twitter, Google+

24 Responses

  1. Wow, how come I hadn’t heard of that before!?

    wait, actually I have… 5 years ago.

  2. Isn’t it amazing thaty people comment on stuff they didn’t bother to read thoroughly in the first place?

    Anyway, thank you Stoyan! Interesting little hackery to reduce yet one more http-request.

    The problem I see is that you’d be actually circumventing one of the main features of CSS: By hacking the conditional comments that way you’d have to have ALL the necessary CSS for the website in both files, for the decent browsers + for IE. That means twice as much work if something in the CSS needs to be changed. And also, the IE-stylesheet therefore becomes much larger than usual, now carrying all CSS instead of only the overwrite-CSS to fix the rendering-bugs specific for IE.

    So, the question is: Is one less http-request worth this effort?

  3. thanks Tobias, appreciate your comment.

    To your question, I think in general saving an HTTP request is pretty much always a clear win

    Now, the browser-specific stylesheets can use some more nice touches :) I wouldn’t duplicate the development work (coding the same css twice). I see two options:
    a/ you have IE67 only overrides in a separate file. Then have your build process merge, minify and produce two bundle files – one without the overrides (decent.css) and one with them (ie67.css)
    b/ you write everything in one place using * and _ hacks for IE, this is nice because all the selectors are the same, no fights over specificity, all declarations are at the same place. Then have your build process merge, minify and additionally parse and strip out browser-specific stuff

    For b/ I mean:
    – decent.css will not have any _, *, zoom, filter.
    – ie7.css will not have any -moz-, -webkit-, -o-, -ms- (since IE8 is decent)

    A very simple parser/stripper is here for testing and evaluation of b/ strategy: (ignore “make JS” option, that’s another experiment)

    A good use case is also if you’re using data URIs vs MHTML, then the base64 stuff is big enough to justify two completely separate files

  4. This method is used to, more than a year.

  5. @banzalik – good stuff. Loaded it in FF, seems like they’ve moved to inline CSS

    Another interesting optimization I see is that they omit spaces between quoted attributes, e.g.
    <a href=”something”class=”my”..

  6. Clever, but as @Tobias states above, this means we’ll still need to maintain two separate stylesheets. Depending on what your build process is, this might not be viable.

    As an alternative, have you seen Paul Irish’s approach?

    It calls for markup like this:

    <link href=”allstyles.css” rel=”stylesheet” type=”text/css”>
    <!–[if lt IE 7 ]><body class=”ie6″><![endif]–>
    <!–[if IE 7 ]><body class=”ie7″><![endif]–>
    <!–[if IE 8 ]><body class=”ie8″><![endif]–>
    <!–[if IE 9 ]><body class=”ie9″><![endif]–>
    <!–[if gt IE 9]><body><![endif]–>
    <!–[if !IE]><!–><body><!–<![endif]–>

    and CSS like this:

    .someclass { padding: 10px; }
    .ie6 .someclass { padding: 5px; }
    .ie7 .someclass { … etc … }

    In a nutshell:
    * Same number of HTTP requests as the conditional comments approach you suggest above (only 1)
    * Plus: Easier maintenance of CSS (all one file, no need to duplicate changes),
    * Minus: Slightly larger filesize (even good browsers will have to download rules for bad browsers.)

  7. @Stoyan – yes, sorry, on the home page – another way of optimizing. But on the other projects the company is used this technique.

    By the way, we invented a new way to optimize (not supported by IE6).
    Discussion of the theory: (in Russian, but I think you’ll find examples of the basic idea)

  8. @banzalik – wow, just wow

  9. Jesper Sjöquist

    Holy crap banzalik, that’s truly insane, and totally awesome!

  10. @banzalik: we are including it to WEBO Site SpeedUp, definitely :)

  11. @banzalik, glad to see our groundwork here, but can you please point to the author next time?
    It is vexing to see tweets like “hardcore html+css minification by banzalik” :)

  12. @DeepSweet – I do not hide, and therefore gave a link to the source, and add it as an example to, sorry if it offended you :(

  13. @banzalik, this is not a problem anymore :)

    here is IE7-8 “hardcore html” workaround

    1. custom xmlns declaration (w/o =”")
    2. x\:x for styles
    3. required because IE puts custom elements into

  14. oops.
    * 3. body required because IE puts custom elements into head

  15. @DeepSweet, my bad, sorry about the improper credit

  16. @DeepSweet that is gnarly stuff!
    Ruthie BenDor beat me to it, the conditional body tag is my preferred variation on this as well. Benefits are:

    1. See the browser specific styles in context, makes maintenance much easier.
    2. 1 less http request.
    3. Uses the cascade for IE related exceptions rather then ugly browser hacks (*, _, etc).

  17. @Sasha, I guess you meant…


    3. uses cascade and ugly long selectors fighting for specificity instead of simple (and obvious, elegant, at the same place) * and _ browser “extensions”


  18. @Stoyan, you’re right – when writing CSS for yourself or for an employer who has a robust development workflow, approachs a/ and b/ are both better than the conditional body tag approach.

    But not everyone works in an environment that has a build process. I’ve consulted for nonprofits and small organizations that don’t have any in-house developers, and have neither a build process nor an inkling of what one is or why they need one. When you’re writing CSS for a client like this, it’s been my experience that the conditional body tag approach is far more maintainable over the long term.

    This approach preserves the big performance win of only one HTTP request, and at the cost of selectors fighting for specificity (which isn’t that ugly, by the way), we’re able to eliminate two big hurdles to making CSS changes:
    1) no need to manually duplicate changes across stylesheets (remember, no build process means we’d have to maintain goodbrowsers.css and badbrowsers.css files separately), and
    2) self-documenting code. Twelve months later, some other developer looks at the stylesheet I wrote and says, ‘Ah, I see, if it says “.ie6 someselector {}”, must be an ie6-only rule.’

    Just my two cents on how to improve performance under less-than-ideal circumstances.

  19. @Ruthie: Thank you for the link to Paul Irish’s approach! It seems like an awesome idea & like you pointed out later, it is also quite maintainable in the long run.

  20. Thanks Ruthie, that’s a very good point. BTW, with “ugly” I was just teasing Sasha who said that _ and * are ugly ;)

    To me * and _ are obvious and I consider them sort of like extensions, just like -webkit- and -moz-. For most practical purposes they can be treated as extensions. The benefits are obvious – no extra declaration blocks and selectors, all is together: the rule and the exceptions.

    But you’re right – .ie6 and .ie7 are much more obvious than _ and *. There’s no doubt why they are there and which one was which.

  21. I’d like to find out more about these “build processes” – preprocessors like Less/Compass?

    Plus conditional body tags **validate** – a big plus for some environments like mine!

    What I’ve been doing so far is regular conditional imports, but of complete browser-specific stylesheets rather than overrides, as advocated here for performance reasons (see also

    I use a GUI diff tool (WinMerge is one of the few examples of Windoze having a superior dev tool) to maintain a good-browser stylesheet version (with extensions where needed) vs an IE one. I use the IE one to validate (often catch typos that way), and diff’ing let’s me easily code in both sheets at the same time – but I have everything open in my coding editor as well – save/switch/refresh/edit/save/switch keystrokes end up becoming second nature, the dev process isn’t slowed down at all.

    When others are taking over my design, they just need to master diff’ing, not other, more advanced tool sets. Or I could always do a one-time merge if they don’t want/need to validate, especially easy with class-based browser selectors.

    But I do try to convince everyone I know to keep everything in version control too :)

  22. Here’s the start of a method to enable developing multiple “full alternative” versions of browser-targeted stylesheets rather than the usual IE override technique. It uses diff, plus the Compass authoring toolset a “CSS meta-framework” (which in turn is based on Sass). This allows you to maintain ALL your style code in a single source file, and compile/output different versions of standard CSS by using variables, conditionals and other real programming features within your CSS. Feedback please – hansbkk [at] gmail

  23. [...] Browser sniffing with conditional comments / Stoyan’s [...]

  24. I will immediately grab your rss as I can’t in finding your e-mail subscription link or newsletter service. Do you’ve any?
    Please let me realize so that I could subscribe.

Leave a Reply