Progressive enhancement

It’s our job, as web designers and developers, to accommodate as many possible uncertainties and allow our content to be consumed by anybody, anywhere, anytime.

It’s about everybody

Since the web is open and available to every human being, throughout the world, there are a many uncertainties we have to understand and accommodate. The only thing we can be certain about is that the content will be consumed—beyond that we know very little.

Your user is not you

The power of The Web is in its universality. Access by everyone regardless of disability is an essential aspect. — Tim Berners-Lee, W3C Director and inventor of the World Wide Web

Don’t assume everybody has a large resolution monitor. Don’t assume everybody has a super-fast–always-on internet connection. Don’t assume everybody can see your design. Don’t assume everybody can tell the difference between red and green. And don’t assume that your content will be consumed just on your website.

There is very little we know about our users and a lot we don’t know:

  • We don’t know where in the world our users are coming from;
  • We don’t know what time of day our users will consume our content;
  • We don’t know what format or context our content will be consumed in;
  • We don’t know what devices our users are using;
  • We don’t know our users’ browser window size;
  • We don’t know our users’ internet connection speed;
  • We don’t know the mindset of our users;
  • And we don’t know what abilities our users have.

(From: CSS-Tricks: What We Don’t Know.)

All we know is that our users are human (unless they’re robots.)

Your users are not you. The Open Web opens so many doors for every human being. It’s our job as web designers to empower humans by helping them easily consume our website’s content—independent of their abilities, independent of their computer’s abilities, and often independent of the original source: our website.

We can control all situations but we need to try to make our website functional for as many people as possible.

Checklist of what-ifs

Here’s a big checklist of things to consider when developing your website. These things could happen for many different reasons, often not the user’s choice.

You’ll notice many of these overlap with performance and accessibility—in fact progressive enhancement is very closely related.

  • ❏ What if the images don’t download or aren’t visible?
  • ❏ What if the images are black & white?
  • ❏ What if the font-size is significantly larger? Or smaller?
  • ❏ What if CSS is disabled or doesn’t load?
  • ❏ What if the browser doesn’t support newer CSS features?
  • ❏ What if the browser doesn’t support newer HTML features?
  • ❏ What if they’re using a text-only browser?
  • ❏ What if the browser is smaller? Or bigger?
  • ❏ What if they’re using only a keyboard?
  • ❏ What if they’re using a touch device?
  • ❏ What if JavaScript is disabled?
  • ❏ What if there’s a JavaScript error and it doesn’t run?
  • ❏ What if the Internet connection is slow?
  • ❏ What if the user prints out the website?
  • ❏ What if your user isn’t giving their full attention?
  • ❏ What if there is double the amount of content on the page?
  • ❏ What if your content is consumed on another website?

Most of all, test in as many browsers as you can—especially with JavaScript off.


Detecting CSS features

Browsers are not a single platform, each browser supports a different set of features. Often one set of features will work great in one browser but not in another browser and we need to have different CSS for different scenarios.

Detecting in CSS

There’s a native feature detection CSS function that we can use in browsers—the @supports declaration:

@supports (transform-style: preserve-3d) {
  /* Put your parallax code in here */

You can put practically any property and value into the brackets and the browser will detect it’s existence.

You can combine multiple properties together with and:

@supports (display: flex) and (filter: grayscale(100%)) {
  /* When flexbox & filter are supported */

There’s also a not operator for negating properties:

@supports (display: flex) and (not (display: grid)) {
  /* When flexbox is supported but grid is not */

See the @supports availability on Can I Use.

Detection with JavaScript

In JavaScript we can access CSS’s @supports functionality (using the same syntax) like so:

if (CSS.supports('(transform-style: preserve-3d)')) {



Modernizr is a popular JavaScript solution that detects CSS, HTML & JavaScript features. There are two different ways to use it: through CSS classes or through the JavaScript interface.

  1. On the Modernizr website, configure what features you want to test on the download page.
  2. Generate your version of Modernizr.
  3. Put the JavaScript you get into a file named modernizr.min.js and include it on your website.

When it comes to Modernizr, it’s often best to put it in the <head> of your HTML file. That’s usually a performance no-no because it pauses rendering in the browser. In the case of Modernizr we don’t want the website rendered until after Modernizr has run.

  <script src="js/modernizr.min.js"></script>

Modernizr CSS classes

After Modernizr runs in the browser it will add classes to the <html> element, you’ll end up with something like this:

<!DOCTYPE html>
<html class="csstransforms">

So, if the browser supports transform we’ll get a class named csstransforms, if the browser doesn’t support transforms we’ll get a class named no-csstransforms.

In our CSS we can then do something like this:

h1 {
  transform: rotate(45deg);

.no-csstransforms h1 {
  /* Do something different for non-supporting browsers */

Modernizr JavaScript conditionals

We can also use Modernizr in our JavaScript code with the Modernizr object.

In our JavaScript we could write something like this:

if (!Modernizr.csstransforms) {
  /* Do something when CSS transforms aren’t supported */


Modern browsers only

Sometimes we only want to target our JavaScript at fairly modern browsers. We should detect each JavaScript feature before we use them to confirm the browser has the abilities we need.

That doesn’t mean the website becomes unusable without JavaScript, just that users can still access all the information but with a different user experience.

Cutting the mustard

The BBC came up with a technique they call “Cutting the Mustard”: if the browser doesn’t cut the mustard it doesn’t get the JavaScript.

In the BBC article they choose to test for three major features: querySelector, localStorage & addEventListener. We can test these in JavaScript and keep the results in a variable:

var cutsTheMustard = ('querySelector' in document && 'localStorage' in window && 'addEventListener' in window);

Then using an if-statement we can load our JavaScript file if the browser passes the test:

var js;

if (cutsTheMustard) {
  js = document.createElement('script');
  js.src = 'js/enhanced.js';
  js.async = true;

This is a very, very basic JavaScript script loader, check out the links for a few more below.



JavaScript on & off

When JavaScript is off we don’t need to provide an identical experience for the user—we just need to make sure the content is available.

Progressively enhanced tabs

For tabs, as an example, the non-JavaScript experience would just be a list with internal links that jump down to the appropriate pieces of content. There are no visual tabs but a list of content where each of the tab panels are always visible.

When the JavaScript executes, it builds the tab interface back up, triggering the appropriate CSS, adding the ARIA attributes, and making the tabs function as we’re used to.

We don’t need to have the ARIA attributes on the default non-JS version because ARIA is primarily for interactions built with JavaScript.

Video list

  1. Progressive enhancement: Detecting CSS features
  2. Progressive enhancement: Cutting the mustard
  3. Progressive enhancement: Tabs