Browser sniffing with conditional comments
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:
- Load the decent browsers CSS
- 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:
- The decent browsers CSS
- 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 duris.ru) 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" /> <![endif]--> <!--[if gt IE 7]><!--> <link href="decent-browsers.css" rel="stylesheet" type="text/css" /> <!--<![endif]-->
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" /> -->
This entry was posted on Thursday, May 13th, 2010 and is filed under browsers, CSS, IE, performance. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Get notification for future posts: follow me on Twitter or subscribe to my RSS feed

May 13th, 2010 at 10:54 pm
Wow, how come I hadn’t heard of that before!?
wait, actually I have… 5 years ago.
May 14th, 2010 at 2:20 am
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?
May 14th, 2010 at 2:46 am
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:
http://www.phpied.com/files/css-parse/css-strip.html (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
May 14th, 2010 at 3:41 am
This method is used to yandex.ru, more than a year.
May 14th, 2010 at 10:51 am
@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”..
May 14th, 2010 at 12:00 pm
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? http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/
It calls for markup like this:
<head>
<link href=”allstyles.css” rel=”stylesheet” type=”text/css”>
</head>
<!–[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.)
May 15th, 2010 at 5:38 am
@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).
Example: http://banzalik.ru/labs/selectors/hardcore.html
Discussion of the theory: http://clubs.ya.ru/bem/replies.xml?item_no=712&ncrnd=7327 (in Russian, but I think you’ll find examples of the basic idea)
May 16th, 2010 at 12:59 am
@banzalik – wow, just wow
May 16th, 2010 at 2:02 am
Holy crap banzalik, that’s truly insane, and totally awesome!
May 16th, 2010 at 2:14 am
@banzalik: we are including it to WEBO Site SpeedUp, definitely
May 16th, 2010 at 4:04 am
@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”
May 16th, 2010 at 4:39 am
@DeepSweet – I do not hide, and therefore gave a link to the source, and add it as an example to banzalik.ru, sorry if it offended you
May 16th, 2010 at 6:29 am
@banzalik, this is not a problem anymore
here is IE7-8 “hardcore html” workaround http://soulshine.in/hardcore-html/index.ie.html
1. custom xmlns declaration (w/o =”")
2. x\:x for styles
3. required because IE puts custom elements into
May 16th, 2010 at 6:30 am
oops.
* 3. body required because IE puts custom elements into head
May 16th, 2010 at 10:53 am
@DeepSweet, my bad, sorry about the improper credit
May 17th, 2010 at 6:14 pm
@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).
May 18th, 2010 at 2:20 am
@Sasha, I guess you meant…
benefits:
1.
2.
drawbacks:
3. uses cascade and ugly long selectors fighting for specificity instead of simple (and obvious, elegant, at the same place) * and _ browser “extensions”
May 19th, 2010 at 9:46 am
@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.
May 21st, 2010 at 4:52 am
@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.
May 21st, 2010 at 4:16 pm
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.
July 5th, 2010 at 7:33 am
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 http://www.phpied.com/category/performance).
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
July 16th, 2010 at 11:10 pm
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
http://groups.google.com/group/compass-users/browse_thread/thread/23f81314c706d01
June 10th, 2011 at 2:22 am
[...] Browser sniffing with conditional comments / Stoyan’s phpied.com [...]