I've been hankering for a way to serve two mutually-independant stylesheets: one for older browsers, and one for newer browsers. Namely, to serve a basic stylesheet to IE6 and other browsers not supporting CSS2 properly. I'm tired of having CSS inheritance issues in my stylesheets, not to mention having to sport IE-specific stylesheets, etc etc.
In order to facilitate this, I manufactured a truly simple hack that can be be used with almost no effort. I chose CSS that should logically work on any browser that can parse properly, meaning this is very forward-looking. It is also validates CSS 1.
My previous attempts (available here) were successful, but not quite optimal. Some browsers probably should not have been served modern stylesheets, while others did not receive styles at all. Thankfully I've managed to coax what I wanted out of a brand new technique.
If you want, you can skip to the technique. What follows is a review of how this technique is derived.
The key insight is to use the data protocol on browsers that support it, which are browsers likely to support CSS2 very well (IE8 supports data URIs now, incidentally). Older browsers, including Mozillas, Safaris, and so forth, are basically disused. Even if they can hypothetically support CSS2 well, newer versions are available that users should be using without question, even by my standards. I aim to create a lo-fi stylesheet for mobile devices, but make it fluid enough to handle desktop browsers as well.
Using data URIs we can start with a basic link tag. We load a data URI that contains a stylesheet with a single import statement. A major caveat with this approach is that you must provide a fully-qualified URL for the stylesheet to be imported, otherwise Gecko doesn't seem to load it at all:
<link rel="stylesheet" type="text/css" href="data:text/css,@import 'http://fqdn/modern.css';"></link>
Now we have to import the basic stylesheet for browsers that cannot handle the data URI protocol. Mercifully this isn't too hard, since most browsers only load the first stylesheet they come across in a link tag and ignore the rest. As a consequence, older browsers won't find the first stylesheet and will load the second. Thus we can simply toss in a second link after the first:
<link rel="stylesheet" type="text/css" href="data:text/css,@import 'http://fqdn/modern.css';"></link>
<link rel="stylesheet" type="text/css" href="basic.css"></link>
Now we have to deal with Opera, which will load the second stylesheet instead of the first. This is thankfully trivial, we simply have to assign titles to each stylesheet, which forces Opera to load only the first one (due to the CSS spec saying it must). As a side-benefit, doing so lets us specify the second stylesheet as an "alternate" stylesheet, and modern browsers with a UI for changing stylesheets will list them both:
<link rel="stylesheet" title="Modern" type="text/css" href="data:text/css,@import 'http://fqdn/modern.css';"></link>
<link rel="alternate stylesheet" title="Basic" type="text/css" href="basic.css"></link>
The only problem is that Opera 7.1x didn't support the data protocol, and will therefore get no styles whatsoever.. this is probably not something that can be easily rectified, but all other Opera versions, including 7.0x and 7.2 up will have no trouble, so it's probably safe to just ignore the problem.
Essentially we've now covered all of our bases except Internet Explorer. Internet Explorer 8 will load both stylesheets, while 7 gets just basic one due to it's lack of support for data URIs. Mercifully, we can tell them both to avoid the second stylesheet using a simple conditional comment, and still let older browsers read that link by escaping the conditional comment:
<link rel="stylesheet" title="Modern" type="text/css" href="data:text/css,@import 'http://fqdn/modern.css';"></link>
<!--[if lt IE 7]>--><link rel="alternate stylesheet" title="Basic" type="text/css" href="basic.css"></link><!--<![endif]-->
Now, IE8 gets the point. IE7, however, is without styles at all. You know what that means: another conditional comment for IE7:
<link rel="stylesheet" title="Modern" type="text/css" href="data:text/css,@import 'http://fqdn/modern.css';"></link>
<!--[if lt IE 7]>--><link rel="alternate stylesheet" title="Basic" type="text/css" href="basic.css"></link><!--<![endif]-->
<!--[if IE 7]><link title="Modern" rel="stylesheet" type="text/css" href="modern.css"></link><![endif]-->
Voila! We're done. In fact, we can tweak this technique easily now to support particular versions of IE, although IE8 will always read the data URI; be sure to remember that. Frankly, IE6 and under will soon be of little consequence, and thus serving a basic stylesheet is probably going to be for the best.
The nice thing about this approach is that we can easily add alternate stylesheets with basically no effort: just add more link tags. Those with data URIs will be handled as "modern" ones, and those in the first conditional comment will be the basic ones. Of course, you'll have to add the modern tags to IE7's list as well, but this is a small price to pay for such a simple technique (at least in my opinion).
The other benefits over my old mutually-exclusive stylesheets technique are clear: only Opera 7.1x gets no styles, the code is cleaner and more easily tuned to handle specific Internet Explorer versions, and none of the actual stylesheets needs any special consideration or hackery. The only problem I can think of is that Safari versions under 1.2 will not see the modern stylesheets, but to me this is a worthwhile price to pay. Very few Mac OS X users are likely to be using those older versions, since to my knowledge all G3-class Macs should be capable of running at least Safari 1.2.
To summarize, you just have to put precisely the following in the head section of a document to properly import everything (filenames are of course up to you). What goes in the actual stylesheets is your choice:
<link rel="stylesheet" title="Modern" type="text/css" href="data:text/css,@import 'http://fqdn/modern.css';"></link>
<!--[if lt IE 7]>--><link rel="alternate stylesheet" title="Basic" type="text/css" href="basic.css"></link><!--<![endif]-->
<!--[if IE 7]><link title="Modern" rel="stylesheet" type="text/css" href="modern.css"></link><![endif]-->
I have yet to test this new technique across the board. It appears to be handled properly by all Windows browsers I have thrown at it, but I will not be able to test Mac OS browsers for a while. If anyone could test it, please do so and let me know if there are any issues. Here is a test page, about as minimal as it gets. It is the version discussed above, so it will show IE6 and under the basic styles (a red background).
Last modified Friday March 3 2008 by BTreeHugger