This is actually an old version of danielmall.com. For the latest and greatest, check out the new site.
 

Enhanced

When properly progressively enhancing a page, you’ll often have the need to modify your layout—sometimes more significantly than others—to accommodate the new features. Here’s a little technique I’ve been using to handle this.

It’s certainly worth mentioning the techniques that already exist. The most popular one I’ve found is using JavaScript to append a “flag” to your <html> or <body>. sIFR adds a class of .sIFR-active to your <html>. This way, you can write specific styles with a specificity domain that only applies when sIFR has been activated.

While this approach is great, works well, and is in complete harmony with the principles of progressive enhancement, I prefer a different way. Inspired by the logic of conditional comments, I want a stylesheet that only loads when JavaScript is enabled. This has 2 benefits:

  1. All of my JavaScript-enabled scenario styles are consolidated in their own stylesheet.
  2. The stylesheet doesn’t load at all if JavaScript is disabled, just like stylesheets referenced in a conditional comment don't load unless your browser is a version of Internet Explorer. For people with JS disabled, it removes the need to download styles—read: extra code—that they don’t need.

Let’s do a quick example, setting up a progressively-enhanced tabbed interface. I’ll quickly create my HTML and CSS to read well without JS (example 1).

Now let’s turn the headings into our tabs and the subsquent content into tabbed content. Here’s the crucial part. Just under my main stylesheet, I’ll add these lines of code:


<script type="text/javascript">
<!--
document.write('<link href="enhanced.css" rel="stylesheet" media="screen, projection" type="text/css" />');
//-->
</script>

This uses JavaScript to write a new stylesheet to the page called enhanced.css. Anything I write in this stylesheet will only be available when JavaScript is enabled. Have a look at our enhanced example (with jQuery and custom JS included), now with tabs fully functioning.

If you look into our enhanced.css, I’m hiding the tabs and the headings in the stylesheet. I could easily have done the same in my JavaScript file instead, but I find 2 advantages to this:

  1. I’m following a more purist view of separating presentation and behavior.
  2. Because my styles are in a stylesheet added in the head, I don’t see the flash of unstyled content between the time the page initially renders and the time it would take JS to modify my markup.

The technique can easily can be extended too. In the same line of thinking, I can add an “enhanced-flash.css” to the page if Flash is installed using the hasFlashPlayerVersion() method of SWFObject. Or you could add some new styles for when you enable Typekit. The possibilities are endless.

I certainly wouldn’t call this a perfect solution, but it’s served me well over the past few years of using it. Hope it helps you too!

Comments

Stoyan said:

One drawbacks I can see here, apart from the use of document.write, is that this is an extra http request, so worse performance. I think you'll end up hurting the experience of 99% users (those with JS on) in order to improve the experience of the rest.

One solution that avoids the extra HTTP request is to have the CSS as part of the JS, as you suggested. Of course this is a pain to maintain, but a build process can do it for you - bacsically just shove the content of the css into a JS string, which you then add to a dynamic `style` tag.

Posted on April 5, 2010 01:58 AM

Christof said:

I use a similar technique but no need for document.write(). I put the following script right at the beginning of HTML head. It addionally replaces html/@id (class is strangely not valid but id is):


(function(css){
// replaces html#id from ``nojs`` to ``js``
document.documentElement.id='js';
var s = document.createElement("style");
s.type = "text/css";
if (s.styleSheet) s.styleSheet.cssText = css;
else s.appendChild(document.createTextNode(css));
document.getElementsByTagName("head")[0].appendChild(s);
})('@import"jsonly.css";.jsdisplay{display:none}/*...*/')

I find a few classes (like .jsdisplay above) sufficient in most cases but you could of course use and @import there too.

Posted on April 5, 2010 05:22 AM

Roy said:

I agree with Stoyan, hitting your 99% with a delayed http request seems counterproductive. The "#sifr-enabled" way is more complicated when writing the css, but it also seems more graceful with regard to performance. I wonder if there's some way to use the noscript tag for your reversed effect.

Posted on April 21, 2010 01:25 PM

Lance said:

I do think the performance issue of having another http request is negligible here.

A good read Dan.

Posted on April 26, 2010 12:34 AM

Sorry: comments are closed.