A Modern Take on Cutting the Mustard
One of the easiest ways to progressively enhance your web experience is the Cutting the Mustard technique. The essence of Cutting the Mustard is:
- Provide an accessible base-line experience for everyone using semantic HTML and CSS
- Write a test for a minimum subset of JavaScript functionality to support.
- If the test passes, enhance the experience with modern JavaScript
- If the test fails, enhance enhance the experience with older JavaScript (and usually a subset of the modern JavaScript’s functionality)
The original test was developed by the BBC team to distinguish between HTML5 browsers and non-HTML5 browsers. That test was fairly straight forward:
This works really well! The enhancements being looked for are all functions that hang off of document
or window
and can looked for using checks, so we use these tests even in browsers that don’t support that functionality! Our progressive enhancement life is wonderful!
Enter ES2015+
Starting with ES2015, or ES6, new JavaScript functionality started to be added that couldn’t be tested for by checking if a property exists; it introduced some actual new JavaScript syntax, like const
, let
, arrow functions, and classes. The problem we now face wanting to test for support of these new syntaxes is that we can’t even write the new syntax and deliver it to browsers that don’t support it without errors! This is because, unlike HTML and CSS which can gracefully ignore unsupported syntax, JavaScript can’t! How, then, can we Cut the Mustard in a way that won’t cause errors in unsupported browsers without compiling or transpiling our modern JavaScript syntax?
JavaScript Modules to the Rescue
Using Modules to Cut the Mustard
Let’s get straight to the code, then we’ll explain it:
When JavaScript modules were introduced, two new bits of HTML syntax were introduced that make this all possible: type="module"
for <script>
tags, and the nomodule
attribute.
In all browsers that support JavaScript module via script
tags, <script type="module">
will be interpreted as a JavaScript module and loaded and run as expected, but for browsers that don’t recognize that type, it’ll just be ignored because it’s unknown HTML syntax! Easy Peasy! Mustard, cut.
We can also “test” for the opposite by including the nomodule
attribute. In all browsers that support type="module"
(except Safari 10.1 and iOS Safari 10.3), adding the nomodule
attribute to out <script>
tags will have that JavaScdipt ignored in module-supporting browsers while it’ll load and run as expected in all other browsers! While it’s not absolutely perfect coverage (less than 1% of browsers globally will fall in to this category at the time of this writing), it’s likely good enough for most production work.
What’s Our New Baseline
Using JavaScript modules as our new baseline gives us a great modern baseline of HTML, CSS, and JavaScript functionality that we can just know is available to us. Some JavaScript highlights from this list are:
const
and partiallet
support- Promises and async functions
fetch
- Arrow functions
- Rest parameters
- Classes
- Template Literals
- Generators
- And, of course, JavaScript Modules
That’s quite a bit of new syntax we get out of the box by setting our baseline to JavaScript modules. This is, of course, in addition to everything we can test for and use that’s not based on new syntax, like Service Workers and Intersection Observers. We can even do it on a feature-by-feature basis:
With the power of JavaScript modules as a modern Cutting the Mustard technique, we have a modern JavaScript baseline we can code to. Combined with our tried-and-true progressive enhancement feature testing, it may finally be time to put down our compilers and transpilers and start delivering cutting-edge JavaScript straight from editor to browser.