Home > front-end development > Conditional stylesheets vs CSS hacks? Answer: Neither!

Conditional stylesheets vs CSS hacks? Answer: Neither!

October 20th, 2008

From what I've seen a good number of developers these days are split between conditional stylesheets and css hacks.

You're used to seeing this:

  <link rel="stylesheet" type="text/css" media="screen" href="css/style.css" />
  <!--[if IE 7]><link rel="stylesheet" type="text/css" media="screen" href="css/ie7.css"  />< ![endif]-->
  <!--[if IE 6]><link rel="stylesheet" type="text/css" media="screen" href="css/ie6.css"  />< ![endif]-->

But probably also plenty of this:

div.infoBox { float: left; padding-right: 10px; _padding-right: 5px; }

CSS hacks to target specific browsers stay where the rest of your styles are, but they certainly don't validate. For sometime now, the standards community has rallied around conditional stylesheets as a solution to the validation problem.

There are a few problems with it though:

  • Conditional stylesheets mean 1 or 2 additional HTTP requests to download
  • As they are in the the <head>, the rendering of the page waits until they're totally loaded.
  • Also - Yahoo's internal coding best practices do not recommend conditional stylesheets
  • It can separate a single CSS rule into multiple files. I've spent a lot of time wondering "Where the eff is that rule coming from!?" when it turned out to be tucked away in a conditional stylesheet.

Here's my proposed solution:

<!--[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]-->

Using the same conditional comments, we're just conditionally adding an extra class onto the body tag. This allows us to keep our browser-specific css in the same file:

 div.foo { color: inherit;}
.ie6 div.foo { color: #ff8000; }

Plus it totally validates and works in all browsers.

(Hat tip to Paul Hammond and Adam McIntyre for being s-m-r-t.)

2010.05.07: Patrick O'Neill put this technique on some steroids. I rather like it. See below:
<!--[if lt IE 7]>  <body class="ie ie6 lte9 lte8 lte7"> <![endif]-->
<!--[if IE 7]>     <body class="ie ie7 lte9 lte8 lte7"> <![endif]-->
<!--[if IE 8]>     <body class="ie ie8 lte9 lte8"> <![endif]-->
<!--[if IE 9]>     <body class="ie ie9 lte9"> <![endif]-->
<!--[if gt IE 9]>  <body> <![endif]-->
<!--[if !IE]><!--> <body>             <!--<![endif]-->

Doug Avery of Viget points out he prefers to use this technique on the HTML tag, freeing the body tag for dynamic classes. That works just fine. Also it combo's really well with the Avoiding the FOUC v3.0 technique as well as Modernizr.

You fancy something different?

2010.05.07: Oh sheesh ya'll. Mat Marquis pointed out the code would leave a page <body>-less in IE10, etc. (Well not really because IE would auto-construct a body tag, but anyway…) Some gt later and we're good.

2010.05.20: Turns out IE conditionals around css files for IE6 can slow down your page load time in IE8! Crazy, right? The solution here avoids that, as does forcing IE to edge rendering.

2010.06.08: And then…

Rob Larsen points out that when these conditional classes are applied to the HTML element, it doesn't block. No empty comment necessary. I think that's a new winner.

Paul Irish front-end development

  1. Matt Dolan
    #1

    Hi. I think you're on to something… I implemented this just last month at work.

    I'm doing my detection in JSTL (its on a jsp) testing against the user agent string so have it working for all browsers. Perhaps not ideal but works for the majority of users.

    One of the great reasons to implement this from my perspective was the ease at which it is to decommission styles for now unsupported browsers. One simple search for .ie6 (if only!) and you're done. Plus if multiple browsers need the same fix no need to look in hundreds of places they'll all be there next to each other.

  2. #2

    Whilst I like the idea of adding a class to the body tag as a way of separation, I don't really understand the first problem you mentioned.

    The HTTP requests only occur if you're using those browsers targetted, i.e. you don't get the additional requests, if you're using firefox.

    Your third point contradicts what you're saying in your post. Although it's not a conditional stylesheet per-say, you are using conditional-comments to feed conditional-css.

    Surely it's better to bloat the bad browsers with a couple of requests, than it is to bloat all browsers with unused css, example.. #ie8 .something{} won't apply to firefox, but you'll have loads of these littered in your stylesheets.

  3. #3

    Hey Mike,
    True, the extra requests only go to IE.
    And yes, it means extra KB on the wire for non-IE.

    But on the whole, I'd think you're looking at a net gain for speed for all your users.
    If I could prove this to you with numbers I'd try. :)

  4. george
    #4

    good idea… but this way you would invalidate your stylesheet (conditional stylesheets usually contain some hacks - e.g. zoom:1, or others.. )

    reducing amount of requests is a great idea - even at the cost of bigger file size…

    and then again, if you're doing optimization (sprites, minimize number of requests & gziping your css/js files), added filesize shouldn't be bigger than 1kb even for a huge site…

  5. Eric
    #5

    Hammond's post turned me on to this technique. I really like it; instead of breaking out the IE fixes into one or more extra files that I have to keep up-to-date, I just add the IE or IE6 adjustments below the relevant styles.

  6. #6

    Interesting idea, Paul. But Mike makes some very good points in his earlier comment. What he didn't mention is that the repetition makes this solution less extensible. And what happens if someone fat fingers one of the Conditional Comments and the visiting user agent decides to march forward with the wrong BODY tag? Oh boy!

    As an alternative to build on your idea, consider using script to set the value of the BODY tag's class attribute and save some repetition:

    document.body.className += " ie"

    Happy coding!

  7. michael
  8. #8

    Yeah definitely similar..

    You can sniff on the backend, but that poses problems when it comes to caching.

  9. #9

    You mentioned this technique on 24 ways. I think I may start using it.

    I think the reduction in http requests outweighs slightly larger stylesheets for non-IE browsers, as byte for byte the size of my IE style sheets are very very small.

    Why would the suggestion made by Josh cause caching issues?

  10. #10

    Christopher,

    Thanks! I think it's better to develop with, too. :)

    Serverside user agent sniffing means you're delivering different pages to different people. Means more effort if you want a comprehensive caching strategy. And likely not as responsive a response.

  11. Kyle Bradshaw
    #11

    Don't know if you were aware of this script
    http://rafael.adm.br/css_browser_selector/

    But you can target browsers via CSS inheritance with this script as well, but then again it's JS not a conditional comment so you're making concessions either way.

  12. #12

    Good link, Kyle.

    Yeah my optimal solution would be one that works sans-javascript. (Heh, although most of the sites I make totally require javascript)

    Maybe its the anal about performance part of me. :)

    Thanks for pointing that out.

  13. Alice
    #13

    Thanks so much for this helpful post!

  14. #14

    FYI-
    This is an interesting application of a similar technique, by Keenora. Probably my favorite js approach I've seen.

  15. unclegrampa
    #15

    Yes. This is a great thing for DB's to debate while actual worthwhile software is being created.

    See you in Harvard Square Paul!

  16. #16

    This is very inspiring.
    Thanks for sharing.

  17. Me
    #17

    Works great!! Thanks mate!

  18. swed
    #18

    Isn't CSS hacks to target specific browsers really totally obsolet? Sure thing! It's about time to force all browser creators to fully implement the W3C Recommendations. Yes, the same recommendations we use creating web contents. By publishing a totally W3C-validated work ur, of coz, done! I don't understand why we should make any effort to balance out all the browser shortcomings. These self-imposed tasks will only postpone the proper implementation of the W3C recommendations by browser creators. If I'm wrong, please correct me.

  19. thedude
    #19

    @george
    and what happens when you invalidate a stylesheet? absolutely nothing. except that it "doesn't validate."

    This approach is much smarter than serving multiple stylesheets.

  20. abomb79
    #20

    @swed
    Sure we would all like to say the heck with non standard friendly browsers, but the truth is people still use them. I was in a presentation the other day given by the CEO of my company and he presented in a webex using IE6. It was a new release and I had missed a couple IE6 tweaks but luckily they were the kind of thing only a designer would notice. The point is IE6 is not dead yet. Let us hope the day is soon.

  21. #21

    Beautiful! Conditional comments are the safest way to go and require less page hits, but its a pain to maintain multiple sheets. With this I can keep all my css in one sheet and still have safe and accurate browser detection.

  22. Erin
    #22

    Great idea! You can get this to work in Dreamweaver by replacing the closing body tag with:
    <!–[if lt IE 7 ]> </body> <![endif]–>
    <!–[if IE 7 ]> </body> <![endif]–>
    <!–[if IE 8 ]> </body> <![endif]–>
    <!–[if !IE]>–> </body> <!–<![endif]–>

  23. Bob
    #23

    @Erin

    Erin,

    I have tried using your additional to get it to display correctly in DW CS4 but have been unable - any ideas?

    Thanks,

    Bob

  24. #24

    I've been using this technique for a while now. Works like a charm.

    I've found that I very rarely need the .IE8 selector and the .IE6/7 selectors see minimal use once you've got your head around how they work. Mind you, with 94% of my user on some form of IE, I've long since learned to code defensively anyway :)

  25. #25

    This is an excellent idea. I'm going to implement it asap.

  26. Jaspero
    #26

    I agree with abomb79. No matter how much we hate IE6, it is widely used than any other browsers. I work for a big company and average of 70% of users are IE users mostly IE6. And by the way, excellent post, ideas and discussions.

  27. #27

    And just in case you're trying to get Paul's ie-conditional-body-tag technique to work in XSLT, I've created a small code sample (which even allows you to set an additional arbitrary body class or classes):

    http://gist.github.com/256183

  28. #28

    I prefer a conditional stylesheet all of its own, and here is why… Other coders/programmers have proven that long descendent CSS selectors slow down render time of the page. So, a line for a bit of nested navigation might look like "body.ie6 #wrapper ul#nav li a { }". A long line like that slows down the page render. If it is in the main stylesheet, then ALL browsers are reading it, even if the rule wasn't meant for them.

    When compared to the download time for another (hopefully short) stylesheet, I think I'd opt for the extra download time, as it is only being downloaded for IE users, so it is an extra step for one segment (albeit a large one) of the population.

    By the time I am done coding a page, my stand-alone IE stylesheet needs maybe four to six rules to fix any IE-specific rendering issues. If you code defensively, and wrap things properly, and follow as many standards as possible, the difference between FF and webkit browsers vs. IE can be fairly minimal Plus, I like having all of the non-compliant/non-validating rules in one place… that way, my main screen.css will valdate just fine.

  29. #29

    I love this technique, but because the body tag is so useful, I've been putting this on the HTML tag instead and clearing up body for misc. dynamic tags. Any downsides to that?

  30. #30

    @Doug,

    Yah up on the HTML tag is totally groovy for this. No downsides whatsoever.

  31. #31

    I started using this technique a few months ago and don't know why I never had before. It's great for keeping a readable CSS file. We still allow for a few 'zoom: 1's in our css but have also moved a lot of the IE6 wrangling over to jQuery.

    It also makes it easy to find your IE6 hacks when the time comes to retire support for it.

  32. #32

    IMHO, if you're using IE?8 you deserve the penalty of an extra HTTP request.

  33. #33

    Sounds like a good idea. One problem of CSS filters is that they are as self-documenting as having a "body.ie6" selector.

    But then again: I only use 2 CSS filters: "* html" and "*+html" and they don't give any problems with validation.

  34. #34

    Looks ugly as sin, but I love the idea. I feels very modernizer-esque. I wish there was a better way to externalize this a-la modernizr.

    I especially like that the approach has better semantics and readability than browser hacks, yet it allows you apply IE specific modifications to style definitions together with the base definition for better maintainability.

    Thankfully I'd use this on a template driven system so I wouldn't have to maintain this on a bunch of different pages. Without templates, this could become a nightmare to maintain.

  35. #35

    @Paul Irish
    heh ehe heh, you said "sniff on the backend"

  36. #36

    I disagree with penalizing other browsers for the transgressions of a few. Chrome, Firefox, Opera, and Safari should not have to and have no need to download styles specific to IE. With the focus on performance, the goal is to reduce unnecessary code. Conditional comments allow the browsers that require the hacks and fixes to bear the weight of their shortcomings. I think that's how it should be really.

    J. Hogue also raised a good point in terms of the speed of parsing rules.

  37. #37

    Paul's technique translated to HAML:

      /[if lt IE 7] 
        %body{:class=>"ie6"}  
      /[if IE 7] 
        %body{:class=>"ie7"}  
      /[if IE 8] 
        %body{:class=>"ie8"}  
      :plain
        <!--[if !IE]><!--> 
      %body
        :plain
           <!--
  38. #38

    That is a solution, but really, you can code css to be cross browser compatible. You should also design your sites that they will be easy to implement as such.

  39. #39

    @Scott Corgan
    Your own site would disagree with you. As much as I'd love to agree, it's just not practical to not have browser-specific styles.
    I think most front-end developers rely on them too heavily. With good experience you can certainly reduce the amount of browser-specific stuff you have. But you'll still need them.

  40. #40

    Was catching up on blog stuff and caught stoyan's post. I've used a variation of this pattern with the classes on the HTML element. I was happy to discover that that pattern doesn't block, no empty comment needed:

    http://www.webpagetest.org/result/100608_0431442088c94bfba54f79e8ad027165/

  41. bz
    #41

    This is my überbloat edition of this technique:

    <!--[if !IE]><!--> <!--
    <!--[if IE]>-->
     
     
     
     
     
     
    //=0);
      var ie_mac  =(s.indexOf('mac')>=0) && (s.indexOf('msie')>=0);
      var ie6     =c.indexOf('MSIE6')>=0;
      var ie7     =c.indexOf('MSIE7')>=0;
      var ie_lte_6=c.indexOf('MSIE-lte-6')>=0;
      var ie_lte_7=c.indexOf('MSIE-lte-7')>=0;
      var ff_lte_2 =(ua.indexOf('firefox/0')>=0) || (ua.indexOf('firefox/1')>=0) || (ua.indexOf('firefox/2')>=0);
      c=c.replace('JS-OFF', 'JS-ON');
      c=c.replace('NA-IPHONE', (iPhone?'':'NOT-')+'IPHONE');
      c=c.replace('NA-IEMAC',  (ie_mac?'':'NOT-')+'IEMAC');
      var mode=(document.compatMode=='BackCompat')?' QUIRKS':' STRICT';
                   c+=mode;
      if(ie6)      c+=mode+'-IE6';
      if(ie7)      c+=mode+'-IE7';
      if(ie_lte_6) c+=mode+'-IE-lte-6';
      if(ie_lte_7) c+=mode+'-IE-lte-7';
      if(ie_mac)   c+=mode+'-IEMAC';
      if(ff_lte_2) c+='FF-lte-2';
      if(this.wp_bodyclass)c+=' '+wp_bodyclass;
      document.body.className=c;
    //]]>
  42. bz
    #42

    Direct text pasting failed, so here is a textsnip link:
    http://textsnip.com/3ef57a

  43. Hans
    #43

    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

  44. DudeWithAHat
    #44

    Hi Paul,

    If I use this technique on the HTML tag along with your Avoiding the FOUC technique, should I put the no-js class on each body tag including the final one?

  45. DudeWithAHat
    #45

    Sorry I mean on each HTML tag not each body tag, typo.

  46. DudeWithAHat
    #46

    Okay I got off my behind and tested it myself, the answer is yes (but html not body, typo).

    However I have another question, is this what Rob is referring to as the empty comment:

    Because it's not in his example.

  47. lucideer
    #47

    Came here after wondering why this method was being used in html5-boilerplate (from your link in comments)… and tbh I'm still quite baffled. You mentioned a lot of disadvantages to the conditional comment method (well you actually only mention one disadvantage but have somehow managed to split it out into 4 points), but you fail to mention any of the disadvantages of your suggested method:

    1. It increases the size of the stylesheet for ALL standards-compliant browsers.
    2. It impairs readability of the markup (and increases its size, albeit only a few bytes).
    and most importantly, imo
    3. It impairs readability of the stylesheet as you could have many rules that are all irrelevant to the browser you're currently testing in.

    One of the advantages of conditional stylesheets is that you need not deal with IE's when testing in other browsers, and vice-versa.

    You say conditional stylesheets means more http requests - but this is true only if you're serving IE the default one. Try doing this instead:

    <!--[if !IE]<>!--<>link rel="stylesheet" href="css/style.combined.css"/><!-->![endif]-->
      <!--[if IE 7]<>link rel="stylesheet" href="css/ie7.combined.css"/>< ![endif]-->
      <!--[if IE 6]<>link rel="stylesheet" href="css/ie6.combined.css"/>< ![endif]-->
    

    It also gives you more styling flexibility, and could be more efficient as IE-specific styles often need not override rules not intended for IE.

  48. Julián Landerreche
    #48

    lucideer, you raised some valid points, imho.

    As most times, the "perfect" solution may come from balancing or combining a few techniques.
    So, in my next project, I think I'll be combining the ugliness IE-specific classes on (or ), as this article suggests, with the tidiness of keeping an IE-only conditional stylesheet. So, all IE-specific styles will live on a single CSS, prefixed by proper .ieX, served only to IE users.

    Thus, all IE users will be "punished" equally, as they will get an extra CSS, with all the IE-specific prefixed rules, no matter which IE version they are using. It's a fair price to pay, for making our life more miserable, by "choosing" to use a browser that is just a PITA, and the PITA is bigger on each new release of IE.

  49. Julián Landerreche
    #49

    Ops, the system ate some basic markup on my previous comment.

    It should have read:

    "(…)I think I'll be combining the ugliness of IE-specific classes on [body] (or [html]) (…)"

  50. #50

    Sorry to comment on a such old article. Wyy not prefer ID over CSS classes?

  51. #51

    wow i wish i found this 2 years ago!

  52. neXXus
    #52

    doesnt IE ignore conditional comments starting with version 9? I remember reading that somewhere. That would mean the lines


    would be senseless and also the statement that IE10 would have no body would be false.

  53. neXXus
    #53

    @neXXus

    seems html comments are not escaped ;)

  54. heavymark
    #54

    It seems from the updates, that the best version of the code has changed since it was initially written. To now use html instead of body, and remove an empty tag. Thought I noticed that new version has not been posted here or as the recommended solution in the new html5boilerplate. Is their a reason for this, if using html instead of body is clearly a winner? Thanks!

  55. #55

    I don't get the point honestly.

  56. DudeWithAHat
    #56

    @heavymark

    Which is the empty tag?

  57. #57

    I'm not sure if this is a big deal or not, but I found it interesting nonetheless. Using the Fetch as Googlebot function in Google's webmaster tools, I noticed that all of these conditional comments are interpreted simply as this:

    &lt;!--[if IE]&gt;&lt;![endif]--&gt;

    It appears that Googlebot doesn't see the final tag at all. I'm unaware if this may cause any problems.

  58. #58

    @Joe Lencioni Actually, I was completely mistaken. I was looking at a different part of my file on accident. Please delete/ignore my comments.

  59. #59

    About to implement HTML 5 Boilerplate on a Wordpress install and came across this post in the comments. I love this idea.

  60. Richard
    #60

    Christ on a crutch, this is effing genius.

    You are fast becoming my idol, I hope you know that.

  61. #61

    I strongly dislike this hack. It is ugly, and I would never clutter my sources with that! Yeeh, I just get a headache just by looking at it!

    I will keep delivering conditional stylesheets to IE7, IE6 and so on, and as #28 has said - those conditional stylesheets may contain invalid code, like expressions etc. And as #32 has said, users of those browsers may deserve an extra request.

    Keeps the main stylesheet clean and standards-compliant. Which was the very idea of using conditional stylesheets at all.

    Kind reagrds, mtness.

  62. quiaro
    #62

    Good stuff. Thanks, Paul!

    Looking at the boilerplate code, I'm wondering … why instead of:

    <!--[if (gt IE 9)|!(IE)]><!-->  <!--

    not just have:

    <!--[if (gt IE 9)|!(IE)]>-->  <!--

    I still can't figure out why the "<!" in there. Can anyone tell me if I'm overlooking something? Thanks

For code blocks, use <pre lang="javascript" escaped="true">. css and html4strict are also accepted.

i left this space here for you to play. <3