A primer on progressive enhancement

When you build a website, it should work everywhere.

Websites should work with new browsers, but also with old browsers. They should work for users using speech-navigation software, and for those with slow mobile connections. Websites should also work for agents who don’t use them visually, like a web crawler.

You’d love to show your awesome website properly to every user. Unfortunately, in real life that’s often not possible. So go for second best: provide some core functionality to everyone, and then give the enhancements to the browsers that can handle it.

So speaks the mantra of progressive enhancement. And this is good for everyone.

It’s good for users. Your content loads for everyone, every time. Disabled users aren’t excluded from navigating your site.

It’s good for developers. More people see your site, and it’s probably faster too. Search engine crawlers get the information they need to optimise your SEO. Those things turn into more page views, lower bounce rates, and (potentially) more revenue for you.

The basic idea Link to heading

The idea is that you start off with a plain HTML version of your site. This is the most basic version and it renders properly for everyone.

Then you add a second layer of complexity; the CSS layer. Robots don’t need this layer; they’ll just use the HTML layer instead. This layer is for the rest of us who like seeing styled websites.

JavaScript is the third layer. Here’s where the fancy stuff comes in, and most visitors will see and use it. But some users, like those using voice navigation, or those on very slow internet speeds, can’t use this layer. They’ll use the first two layers instead.

Most sites are built using HTML, CSS and JavaScript. But they’re not built so there’s no essential content in the JavaScript layer that isn’t present in the HTML layer. Their CSS might add content not present in the HTML layer. They don’t follow progressive enhancement principles.

If a user can’t use a layer, you want them default down to the previous one. If the site is built using progressive enhancement, they’ll get a workable, useful site. If it’s not, who knows?

A development strategy Link to heading

Here’s how to build a site using progressive enhancement.

First, you decide what the base level of performance looks like, which you incorporate in the HTML layer. Then you add CSS and JavaScript to make your site better for those users who can handle it.

Let’s go through some specific tips for each layer to get progressive enhancement.

HTML Link to heading

Put your content in the HTML layer. Don’t generate content using the CSS content property; use HTML instead. Then this base layer will work properly.

Make the HTML semantic, meaning that the tags describe their content. Instead of <span> and <div>; use <article>, <section>, <nav> and <aside> instead.

Use HTML where you can instead of JavaScript. For example, you can use HTML to create accordions using the <details> and <summary> elements. You don’t need JavaScript for that. You can style it with CSS in the second layer. You want to avoid JavaScript as much as possible.

Here’s one pattern. Say you wanted to include a link to your tweets. Put a link to your Twitter profile inside a HTML element, and give it an id. :

<section id="twitter">
	<a href="https://twitter.com/yourtwitter">View my tweets</a>
</section>

Then once JavaScript is loaded, replace this <section> with a widget that embeds some tweets into your webpage. This means that if the JavaScript layer doesn’t load, there’s still something on the page.

CSS Link to heading

There is a base subset of CSS that works across most browsers, and most of the time you’d restrict yourself to that subset.

You can move outside the subset, but use a fallback: a bit of CSS that says: “if feature A doesn’t work, try feature B”. This lets you put in the latest CSS features, and still have something that works on older browsers.

You use fallbacks by including a number of different options into your CSS, with the most advanced one at the bottom. For example:

h1 {
  background-color: #000;
  background-color: rgba(0,0,0,0.5);
}

The browser will try all the options, and use the one closest to the bottom that it understands. 1 If it doesn’t support rgba, it’ll use #000 instead.

Like the HTML layer, where possible use CSS instead of JavaScript. For example, use CSS for animations rather than JavaScript.

JavaScript Link to heading

“All your users are non-JavaScript while they’re downloading your JavaScript.”Jake Archibald

JavaScript is the third layer.

Under progressive enhancement principles you’d try to minimise your JavaScript usage. Instead you’d rely on CSS and HTML.

Let’s compare the way HTML and JavaScript get to the browser. HTML is rendered piece by piece as it is downloaded, while the entire JavaScript file must be downloaded before execution. Nobody can run JavaScript code while it’s loading, and it frequently doesn’t finish loading, because there is a lot of it, or because the connection is bad, or because an ad-blocker stopped it halfway. You can see why HTML is preferred.

So see what your site looks like with JavaScript disabled. This highlights where you rely on JavaScript, and where you can use HTML/CSS to replace it.

Also, try to write unobtrusive JavaScript. Unobtrusive JavaScript follows some principles:

  • Put JavaScript in its own file; don’t mix it with HTML and CSS.
  • If the JavaScript fails, it falls back to something that works. It doesn’t show error messages to the user.
  • The accessibility of HTML remains untouched or improved by the presence of JavaScript.
  • Avoid global variables, or adding to the global object. This might involve using IIFE’s.

Tools like Modernizr are helpful because they let you automatically adjust website behaviour depending on the browser. Your new, supercool features will be delivered only to browsers supporting them, meaning the site won’t break with older browsers.

A final note Link to heading

It might seem that this methodology stops the developer using cutting-edge features.

Yet the opposite is true. You’re able to use the new features because you know if they’re not supported, you’ll still deliver a workable version of your site.


  1. You can also use @supports instead, which are kind of like an if-statement for CSS, saying basically: “if my browser supports this feature then do it, else do this other thing”. ↩︎