Progressive CSS Enhancement

A common – and desirable – technique for constructing JavaScript-based web applications is that of progressive enhancement: only providing capable browsers with the features that they are capable of utilizing – but providing incapable browsers with an adequate, albeit degraded, experience otherwise.

This provides the best of both worlds: Users of modern browsers (the majority audience) can get the best experience while those that are using incapable browsers (such as most mobile devices) will still get an interface that suits them well.

There’s one thing, overriding in all of this, however: Progressive enhancement is nearly only ever applied to the JavaScript functionality of web applications. Presumably it’s assumed that if a browser is capable of supporting the desired JavaScript features of an application then it must, also, be capable of supporting the specific CSS styling as well.

One technique that has greatly interested me, as of late, is one employed by the Filament Group (a local design shop here in Boston): Progressive CSS Enhancement. The premise is that progressive enhancement is done with page styling in mind, primarily, rather than from a purely-JavaScript perspective.

This is particularly important for a couple reasons:

  • It should be easy to degrade page styling in a manner that isn’t reliant upon CSS browser hacks – this technique makes it so.
  • Not all pages utilize heavy JavaScript (and, thus, progressive JavaScript enhancement does not apply to them).

Their technique works as follows: You choose to provide the user with, either, the enhanced or the decreased experience by default. In either case a basic script is run which attempts to verify a couple CSS styling behaviors along with some basic JavaScript functionality (just enough to be able to run the test).

A couple of the CSS techniques that they test for:

  • Box model: make sure the width and padding of a div add up properly using offsetWidth.
  • Positioning: position a div and check its positioning using offsetTop and offsetLeft.
  • Float: float 2 divs next to each other and evaluate their offsetTop values for equality.
  • Clear: test to make sure a list item will clear beneath a preceding floated list item.
  • Overflow: wrap a tall div with a shorter div with overflow set to ‘auto’, and test its offsetHeight.

With those in place you can pretty safely begin designing a useful CSS-based layout. Note that the experience will only ever be upgraded if all of the tests pass – if any fail then it simply won’t continue. Obviously there’ll still exist some browser discrepancies (like in the differences in the box model between Internet Explorer 6 and most other browsers) but that’s usually an acceptable level of hackage (meaning that you won’t have to deviate much from what you’re already doing).

The actual implementation is quite simple. It consists of a number of JavaScript-based rules that test for behavior. For example the following rule tests for a working box model:

var newDiv = document.createElement('div');
document.body.appendChild(newDiv); = 'hidden'; = '20px'; = '10px';
var divWidth = newDiv.offsetWidth;
if(divWidth != 40) {document.body.removeChild(newDiv); return false;}

That check, alone, is able to knock off a number of older browser whom aren’t able to successfully implement that CSS behavior. Currently all the rules are in a large code block, which makes maintenance unwieldily. I think that this library could definitely benefit from extensibility (being able to add/remove rules that you wish to honor).

When it comes time to actually use this technique within your application there are a number of strategies that you can use. However, for the sake of discussion here, let’s assume that you’re sending, by default, the degraded experience to the client (optionally upgrading if the browser is capable). Then you would be able to use these two techniques:

  • A class of “enhanced” is assigned to the body element to be used for optional CSS scoping (such as: body.enhanced {background: red;}).
  • Any links to alternate stylesheets that have a class of “enhanced” will be enabled.

In this manner you can specify all of your stylesheets in your header with some disabled (being alternate stylesheets) or with some CSS rules being only applied with the body.enhanced match.

Their implementation also allows you to only execute JavaScript if all the rules pass – however I’m not sure if that’s an acceptable solution, in this situation. If you want to verify that your desired JavaScript functionality is able to operate then you should check for just that. However, in this case, we can get the other side of the equation: Verifying that CSS works as you would expect it to, knowing that an adequate experience can be provided.

If you’re curious as to which devices are supported by the default rules in the test file you can view the result matrix on the tool’s site.

I definitely think that this technique has a lot of merit, especially in the realm of mobile-accessible web sites. Since it’s virtually impossible to design, and test, your pages to work on such a large number of obscure platforms this degraded strategy is really one that will help to benefit both you, and your users, in the long run.

Posted: March 15th, 2008

Subscribe for email updates

21 Comments (Show Comments)

Comments are closed.
Comments are automatically turned off two weeks after the original post. If you have a question concerning the content of this post, please feel free to contact me.

Secrets of the JavaScript Ninja

Secrets of the JS Ninja

Secret techniques of top JavaScript programmers. Published by Manning.

John Resig Twitter Updates

@jeresig / Mastodon

Infrequent, short, updates and links.