skip to content

Is Progressive Enhancement Dead Yet? (video player)

Transcript ↓

Let’s talk about progressive enhancement.

It’s a strong contender for the least popular P.E.-based acronym since Physical Education. In fact, a lot of people would love to see the back of it.

But just like that time you whacked a zombie over the head with a shovel only to discover it was really your friend Jimmy with a hangover, this would be a grave mistake. And I use the term grave advisedly, because having sent Kyle to an early grave, this would only increase the likelihood of him becoming a zombie in the not too distant future.

There are a lot of misconceptions around progressive enhancement, so before we talk about what it is, let’s talk about what it most definitely is not.

So what is progressive enhancement? Astonishingly enough, it’s all to do with skateboards. Imagine two skateboards. One is the old fashioned kind; just a board with wheels. The other has a screw hole towards the front allowing you to attach some handlebars and make the skateboard a scooter.

Just because the handlebars don’t attach to the older skateboard doesn’t mean it’s broken. It still works as a skateboard, meaning you are still able to pull off at least one category of rad moves, including ollies, kickflips, and errrrrrr plasma spins.

In this excruciatingly contrived metaphor, the skateboards are browsers, and the handlebars are a feature browsers may or may not support: an enhancement that can be applied progressively.

Take masonry layouts. Masonry layouts (or “Pinterest layouts” for anyone whose brain goes to sleep until they hear a brand name) are when the gaps created by rows in a grid of differently proportioned elements are filled.

This has been possible for some time with JavaScript, so where’s the beef? Well, JavaScript isn’t really designed for layout, so when it does layout, it does so inefficiently. Plus, JavaScript is liable to error and take all the other JavaScript on the page down with it—hence its nickname: JengaScript.

In CSS, we can use the @supports at-rule to test and safely add a masonry layout where it is supported with CSS Grid:

.grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);

@supports (grid-template-rows: masonry) {
  .grid {
    grid-template-rows: masonry;

Browsers that don’t support either the masonry keyword or the @supports rule itself still render a grid—just one with gaps. The next part is really important, so I’m going to say it several times over.

For reasons of performance and robustness, it’s better the traditional skateboard (old browser) is left intact than to take a drill (JavaScript) and drunkenly fill it full of fucking handlebar holes.

Where a really neat CSS feature really isn’t supported anywhere, you might want to use JavaScript to emulate it. But you can still use progressive enhancement to do this in a responsible way.

For example, I wanted to use something like “container queries” for my layouts. For performance, I wanted to use the optimized ResizeObserver interface and for convenience a custom element.

<watched-box> lets you define container-specific breakpoints using its widthBreaks and heightBreaks props. And it only does any of this if both ResizeObserver and custom elements are supported.

if ('ResizeObserver' in window && 'customElements' in window) {
  customElements.define('watched-box', WatchedBox);

The element definition itself should be loaded using a <script> tag with the type="module" attribution. Why? Because browsers that don’t support the funky class syntax needed to define a custom element also don’t support JavaScript modules. The module is ignored in these browsers and a massive pileup is avoided.

Now you just need to make sure you don’t do something silly like using the classes generated by the fancy custom element with the brand spanking JavaScript to add basic styles like font sizes and colors. You silly billy.

What if an enhancement isn’t really an enhancement?

Skateboarding legend and notorious land mammal Tony Hawk goes into a skateboard shop and approaches a sales assistant.

“Hello, good sir,” he says. “I would like to purchase a new skateboard.”

“I have just the thing,” replies the assistant. “Our new model is fitted with a special screw hole for attaching a set of handlebars. It is a skateboard and a scooter all in one.”

“I have literally no fucking interest in a skateboard that can be converted into a scooter,” Tony Hawk rejoins. “I am skateboarding legend Tony fucking Hawk, pioneer of vert skating and that screw hole would only disrupt the airflow around my board while I am airbourne.”

“My sincere apologies, Tony Hawk,” the assistant pleads. “I am afraid I had you mixed up with the British comedian and actor Anthony Gordon Hawksworth, known professionally as Tony Hawks. With an S.”

“That is quite alright,” says Tony Hawk. And then… they kiss.

It’s like tab interfaces. Building one progressively means founding it on well structured HTML wherein the tabs are analogous to links in a table of content. Where JavaScript runs, the content can be transformed into the interactive tab interface, allowing users to traverse sections of content using the tabs.

There’s just one problem. Tab interfaces are shit. They’re a pain to make technically accessible, and even then most screen reader users I’ve observed don’t know what they are or how to use them. Not to mention, when you add content, this starts to happen.

The enhancement is complex and costly. And it’s not even an enhancement; it’s more of a terrifying flashback to antiquated desktop software design. The browser is already an interface, and it already provides a well-established and multimodal means for traversing content, called scrolling.

In any case, foregoing enhancements when they are not really enhancements and just letting the browser do its thing is a practice called “unprogressive nonenhancement”.

In summary:

  1. Progressive enhancement is not just about adding JavaScript. It could mean adding specific JavaScript features or browser API support, adding specific CSS features, or supporting anything that isn’t available universally in a way that doesn’t break stuff where it isn’t available.
  2. Progressive enhancement doesn’t mean more code. In fact, by avoiding polyfills and transpilation it means less code and less time spent dicking around with compilers and bundlers and shit like that.
  3. This (type="module") is for modern browsers supporting modern syntax and APIs.
  4. This (nomodule) is ignored by modern browsers and can be used to include polyfills and transpiled code for older browsers where absolutely necessary.
  5. Use JavaScript to do JavaScript things… and not JavaScript to do, like, not JavaScript things.

And finally:


  1. Tony Hawk
  2. Tony Hawks
  3. Tony Hawkses
  4. Hawk The Slayer
  5. Hawks The Slayers
  6. A literal hawk
  7. Phil Hawksworth