Progressive enhancement with CSS
There’s been a lot of words in this principles module, but it’s really important stuff. A big part of getting better at writing CSS involves all the stuff you need to do around writing the syntax. If you really prioritise the mental models and the approach you’ve learned in this module, I promise, that will improve your CSS work in itself.
There is more to improve though, and with all honesty, progressive enhancement is one of those crucial mindsets that help you to deliver user interfaces that work for everyone and thanks to that, are incredibly resilient.
The most important thing to remember is:
We build for everyone. Not just for ourselves or our peer groups.
We’re lucky with CSS as it is designed to do exactly this thanks to the cascade in particular. Let me explain with this example. Say you want to use clamp() to achieve fluid type and space.
- Code language
- css
.my-element { font-size: clamp(1rem, calc(5vi + 1rem), 4rem); }
What if someone’s browser doesn’t support clamp()? The cascade can work that out for us without breaking a sweat.
- Code language
- css
.my-element { font-size: 2rem; font-size: clamp(1rem, calc(5vi + 1rem), 4rem); }
Because we’ve declared the font-size twice, the cascade will automatically pick the clamp() version if it understands that function. If not it will completely ignore the second rule and move on. It’s because CSS is a declarative programming language which means we specify what the outcome is rather than specifically how to achieve that outcome. In short, it means CSS is very forgiving and flexible because it won’t crap out if it doesn’t understand something, like an imperative programming language like JavaScript does.
By setting a reasonably sensible fallback value of 2rem as the font-size, we are providing a decent minimum viable experience. It might not be our ideal experience, but for users who’s browsers don’t support newer CSS technologies like clamp(), they will get the ideal experience for them.
Use the newest stuff today instead of waiting!permalink
Let’s take a look at another reasonably new technology in CSS: container queries. We’re lucky that they now have really high support but still treat them as an enhancement rather than crucial.
For example, for the post layout on this site, we used a container query to apply a border to the correct side of the sidebar, based on whether is was on the right, or stacked to the bottom. You can read about that here.
- Code language
- css
@container (width > 50vi) { .post__meta { border-inline-start: 0; border-block-start: 1px solid; padding-inline-start: 0; } }
What happens if container queries aren’t supported? Not much really, aside from the sidebar will have a border on the side rather than the top.
That is completely acceptable because it isn’t broken. As long as you prioritise as little friction as possible for users, regardless of their browser, device and connection speed, you will be building excellent user interfaces.
Let’s look at something more relevant that doesn’t have much support across the board: text-box-trim. This is a new CSS capability that allows you to account for the extra space that fonts have.
A classic example of how this is helpful, is buttons. Often when you apply vertical padding, the text label isn’t quite in the centre, so we end up doing this sort of thing.
- Code language
- css
.button { padding: 1.5em 2em 1.6em 2em; }
What’s happening here is we’re adjusting the bottom padding by .1em to create the illusion of centred text. The text-box-trim property allows us to effectively remove the extra space that fonts give us, which in turn removes the need for magic numbers in our padding values.
- Code language
- css
.button { text-box-trim: trim-both; text-box-edge: cap alphabetic; }
What we’re saying is, “trim the space at the cap height for alphabetic characters”, which will result in this CSS.
- Code language
- css
.button { padding: 1.5em 2em; text-box-trim: trim-both; text-box-edge: cap alphabetic; }
At the time of writing, only Chromium and Safari support this feature, but it’s no drama. The trick is to let go and accept that slight oddity with alignment and apply the text-box-trim rules today, because before you know it, it will be a baseline feature!
See the Pen by piccalilli (@piccalilli) on CodePen.
If you’re desperate to get things perfect (honestly, life is too short), then you could use a @supports query:
- Code language
- css
.button { padding: 1.5em 2em 1.6em 2em; } @supports (text-box-trim: both) { .button { padding: 1.5em 2em; text-box-trim: trim-both; text-box-edge: cap alphabetic; } }
The problem I personally see with this is, you’re working against the grain of the browser which we learned not to do in the Be the browser’s mentor, not its micromanager lesson. CSS moves so fast now that your @supports query will be technical debt before you know it.
By accepting an end experience that’s not quite perfect you’re going to provide a much better experience for everyone. Remember:
We build for everyone. Not just for ourselves or our peer groups.
Wrapping up this module
Oof, that was quite an intense start to the course, right? It’s all really important stuff to start with though, because we’re all on the same page now and we’re ready to get cracking with our project.
The next module is all on planning and feedback: crucial stuff to get right before you even think about authoring production CSS. Make yourself a cup of whatever you like drinking (I’m making a cup of tea) and get ready to get practical!