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.
Director’s note: Private equity
PE also stands for Private Equity * vomits *
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 .
Progressive enhancement is not something you install using nice package manager (or “NPM”).
Progressive enhancement is not a kind of handled vessel for vegetable soup.
Progressive enhancement is not a <noscript>
tag with the text, “please turn on JavaScript.”
Progressive enhancement is not a shark with legs. And a fez.
Progressive enhancement is not that thing where you render a bunch of HTML on your server, only to have it re-rendered in the user’s browser via “hydration”.
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.
Director’s note: Andy Bell
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.
Director’s note: Boarding
I used to own a skateboard but I never got any good at skateboarding. I did, however, tie it to a motorbike with a length of rope and get my brother to pull me around. You inhale a lot of fumes that way, which explains a lot.
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.
The basic layout is not a broken layout.
The basic layout is not a broken layout.
The basic layout is not a broken layout.
Director’s note: Performance
I believe that, if we can just get used to the above statement, the web would be a lot faster, easier to maintain, the lot.
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.
Director’s note: My throat
It’s hard to do a comedy accent that isn’t offensive to someone. Somehow I arrived at whatever this is.
“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.
Director’s note: Your design system
Your design system probably doesn’t need a tab interface. Don’t add one just to be completist or because everyone else has one.
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:
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.
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.
This (type="module"
) is for modern browsers supporting modern syntax and APIs.
This (nomodule
) is ignored by modern browsers and can be used to include polyfills and transpiled code for older browsers where absolutely necessary .
Use JavaScript to do JavaScript things… and not JavaScript to do, like, not JavaScript things.
And finally:
KNOW. YOUR. HAWKS.
Tony Hawk
Tony Hawks
Tony Hawkses
Hawk The Slayer
Hawks The Slayers
A literal hawk
Phil Hawksworth