Drag this to your browser’s bookmark bar now!
Drag this to your browser’s bookmark bar now!
If you treat data as a constraint in your design and development process, you’ll likely be able to brainstorm a large number of different ways to keep data usage to a minimum while still providing an excellent experience. Doing less doesn’t mean it has to feel broken.
This post was originally written in 2015, but upon re-reading it today, it still (just about) holds up, so I finally hit publish.
But there’s a difference between something degrading gracefully (the result) and graceful degradation (the approach).
This is how it goes. We put a load of shit into a single web page. This makes the page slow. Slow to load, slow to render. Slow.
Instead of getting rid of the shit, we blame the page refresh.
I would urge front-end developers to take a step back, breathe, and reassess. Let’s stop over engineering for the sake of it. Let’s think what we can do with the basic tools, progressive enhancement and a simpler approach to building websites. There are absolutely valid usecases for SPAs, React, et al. and I’ll continue to use these tools reguarly and when it’s necessary, I’m just not sure that’s 100% of the time.
I got a message from a screen-reader user of The Session recently, letting me know of a problem they were having. I love getting any kind of feedback around accessibility, so this was like gold dust to me.
They pointed out that the drag’n’drop interface for rearranging the order of tunes in a set was inaccessible.
Of course! I slapped my forehead. How could I have missed this?
It had been a while since I had implemented that functionality, so before even looking at the existing code, I started to think about how I could improve the situation. Maybe I could capture keystroke events from the arrow keys and announce changes via ARIA values? That sounded a bit heavy-handed though: mess with people’s native keyboard functionality at your peril.
Then I looked at the code. That was when I realised that the fix was going to be much, much easier than I thought.
I documented my process of adding the drag’n’drop functionality back in 2016. Past me had his progressive enhancement hat on:
One of the interfaces needed for this feature was a form to re-order items in a list. So I thought to myself, “what’s the simplest technology to enable this functionality?” I came up with a series of
selectelements within a form.
The problem was in my feature detection:
There’s a little bit of mustard-cutting going on: does the
dragulaobject exist, and does the browser understand
querySelector? If so, the
selectelements are hidden and the drag’n’drop is enabled.
The logic was fine, but the execution was flawed. I was being lazy and hiding the
select elements with
display: none. That hides them visually, but it also hides them from screen readers. I swapped out that style declaration for one that visually hides the elements, but keeps them accessible and focusable.
It was a very quick fix. I had the odd sensation of wanting to thank Past Me for making things easy for Present Me. But I don’t want to talk about time travel because if we start talking about it then we’re going to be here all day talking about it, making diagrams with straws.
I pushed the fix, told the screen-reader user who originally contacted me, and got a reply back saying that everything was working great now. Success!
This is a really nice write-up of creating an accessible progressive disclosure widget (a show/hide toggle).
Where it gets really interesting is when Andy shows how it could all be encapsulated into a web component with a progressive enhancement mindset
53% of mobile visits leave a page that takes longer than 3 seconds to load. That means that a large number of visitors probably abandoned these sites because they were staring at a blank screen for 3 seconds, said “fuck it,” and left approximately half way before the page showed up. The fact that the next page interaction would have been quicker—assuming all the JS files even downloaded correctly in the first attempt—doesn’t amount to much if they didn’t stick around for the first page to load. What was gained by putting the business logic in the front end in this scenario?
Everything you need to know about hyphenation on the web today, from Rich’s galaxy brain.
Hyphenation is a perfect example of progressive enhancement, so you can start applying the above now if you think your readers will benefit from it – support among browsers will only increase.
A fascinating look at the web today with IE8. And it’s worth remembering who might be experiencing the web like this:
Whoever they are, you can bet they’re not using an old browser just to annoy you. Nobody deliberately chooses a worse browsing experience.
The article also outlines two possible coping strategies:
- Polyfilling Strive for feature parity for all by filling in the missing browser functionality.
- Progressive Enhancement Start from a core experience, then use feature detection to layer on functionality.
Take a wild guess as to which strategy I support.
There’s a bigger point made at the end of all this:
IE8 is today’s scapegoat. Tomorrow it’ll be IE9, next year it’ll be Safari, a year later it might be Chrome. You can swap IE8 out for ‘old browser of choice’. The point is, there will always be some divide between what browsers developers build for, and what browsers people are using. We should stop scoffing at that and start investing in robust, inclusive engineering solutions. The side effects of these strategies tend to pay dividends in terms of accessibility, performance and network resilience, so there’s a bigger picture at play here.
A good talk from from Chris Ferdinandi, who says:
Hui-Jing talks through her process of building a to-do app on Glitch using a progressive enhancement mindset:
Scott Jehl is speaking at An Event Apart in Seattle—yay! His talk is called Move Fast and Don’t Break Things:
Performance is a high priority for any site of scale today, but it can be easier to make a site fast than to keep it that way. As a site’s features and design evolves, its performance is often threatened for a number of reasons, making it hard to ensure fast, resilient access to services. In this session, Scott will draw from real-world examples where business goals and other priorities have conflicted with page performance, and share some strategies and practices that have helped major sites overcome those challenges to defend their speed without compromises.
The title is a riff on the “move fast and break things” motto, which comes from a more naive time on the web. But Scott finds part of it relatable. Things break. We want to move fast without breaking things.
This is a performance talk, which is another kind of moving fast. Scott starts with a brief history of not breaking websites. He’s been chipping away at websites for 20 years now. Remember Positioning Is Everything? How about Quirksmode? That one's still around.
In the early days, building a website that was "not broken" was difficult, but it was difficult for different reasons. We were focused on consistency. We had deal with differences between browsers. There were two ways of dealing with browsers: browser detection and feature detection.
The feature-based approach was more sustainable but harder. It fits nicely with the practice of progressive enhancement. It's a good mindset for dealing with the explosion of devices that kicked off later. Touch screens made us rethink our mouse and hover-centric matters. That made us realise how much keyboard-driven access mattered all along.
Browsers exploded too. And our data networks changed. With this explosion of considerations, it was clear that our early ideas of “not broken” didn’t work. Our notion of what constituted “not broken” was itself broken. Consistency just doesn’t cut it.
But there was a comforting part to this too. It turned out that progressive enhancement was there to help …even though we didn’t know what new devices were going to appear. This is a recurring theme throughout Scott’s career. So given all these benefits of progressive enhancement, it shouldn’t be surprising that it turns out to be really good for performance too. If you practice progressive enhancement, you’re kind of a performance expert already.
People started talking about new performance metrics that we should care about. We’ve got new tools, like Page Speed Insights. It gives tangible advice on how to test things. Web Page Test is another great tool. Once you prove you’re a human, Web Page Test will give you loads of details on how a page loaded. And you get this great visual timeline.
This is where we can start to discuss the metrics we want to focus on. Traditionally, we focused on file size, which still matters. But for goal-setting, we want to focus on user-perceived metrics.
The average time for this on the web right now is around six seconds. That’s broken. The render blockers are the problem here.
Consider assets like scripts. Can you get the browser to load them without holding up the rendering of the page? If you can add
defer to a
script element in the
head, you should do that. Sometimes that’s not an option though.
For CSS, it’s tricky. We’ve delivered the HTML that we need but we’ve got to wait for the CSS before rendering it. So what can you bundle into that initial payload?
You can user server push. This is a new technology that comes with HTTP2. H2, as it’s called, is very performance-focused. Just turning on H2 will probably make your site faster. Server push allows the server to send files to the browser before the browser has even asked for them. You can do this with directives in Apache, for example. You could push CSS whenever an HTML file is requested. But we need to be careful not to go too far. You don’t want to send too much.
Server push is great in moderation. But it is new, and it may not even be supported by your server.
Another option is to inline CSS (well, actually Scott, this is technically embedding CSS). It’s great for first render, but isn’t it wasteful for caching? Scott has a clever pattern that uses the Cache API to grab the contents of the inlined CSS and put a copy of its contents into the cache. Then it’s ready to be served up by a service worker.
By the way, this isn’t just for CSS. You could grab the contents of inlined SVGs and create cached versions for later use.
So inlining CSS is good, but again, in moderation. You don’t want to embed anything bigger than 15 or 20 kilobytes. You might want separate out the critical CSS and only embed that on first render. You don’t need to go through your CSS by hand to figure out what’s critical—there are tools that to do this that integrate with your build process. Embed that critical CSS into the
head of your document, and also start preloading the full CSS. Here’s a clever technique that turns a preload link into a stylesheet link:
<link rel="preload" href="site.css" as="style" onload="this.rel='stylesheet'">
Also include this:
<noscript><link rel="stylesheet" href="site.css"></noscript>
You can also optimise for return visits. It’s all about the cache.
In the past, we might’ve used a cookie to distinguish a returning visitor from a first-time visitor. But cookies kind of suck. Here’s something that Scott has been thinking about: service workers can intercept outgoing requests. A service worker could send a header that matches the current build of CSS. On the server, we can check for this header. If it’s not the latest CSS, we can server push the latest version, or inline it.
The neat thing about service workers is that they have to install before they take over. Scott makes use of this install event to put your important assets into a cache. Only once that is done to we start adding that extra header to requests.
Watch out for an article on the Filament Group blog on this technique!
With performance, more weight doesn’t have to mean more wait. You can have a heavy page that still appears to load quickly by altering the prioritisation of what loads first.
Web pages are very heavy now. There’s a real cost to every byte. Tim’s WhatDoesMySiteCost.com shows that the CNN home page costs almost fifty cents to load for someone in America!
Scott believes easier to make a fast website than to keep a fast website. And that’s down to all the third-party scripts that people throw in: analytics, ads, tracking. They can wreak havoc on all your hard work.
These scripts apparently contribute to the business model, so it can be hard for us to make the case for removing them. Tools like SpeedCurve can help people stay informed on the impact of these scripts. It allows you to set up performance budgets and it shows you when pages go over budget. When that happens, we have leverage to step in and push back.
Assuming you lose that battle, what else can we do?
These days, lots of A/B testing and personalisation happens on the client side. The tooling is easy to use. But they are costly!
A typical problematic pattern is this: the server sends one version of the page, and once the page is loaded, the whole page gets replaced with a different layout targeted at the user. This leads to a terrifying new metric that Scott calls Second Meaningful Content.
Assuming we can’t remove the madness, what can we do? We could at least not do this for first-time visits. We could load the scripts asyncronously. We can preload the scripts at the top of the page. But ideally we want to move these things to the server. Server-side A/B testing and personalisation have existed for a while now.
Scott has been experimenting with a middleware solution. There’s this idea of server workers that Cloudflare is offering. You can manipulate the page that gets sent from the server to the browser—all the things you would do for an A/B test. Scott is doing this by using comments in the HTML to demarcate which portions of the page should be filtered for testing. The server worker then deletes a block for some users, and deletes a different block for other users. Scott has written about this approach.
The point here isn’t about using Cloudflare. The broader point is that it’s much faster to do these things on the server. We need to defend our user’s time.
Another issue, other than third-party scripts, is the page weight on home pages and landing pages. Marketing teams love to fill these things with enticing rich imagery and carousels. They’re really difficult to keep performant because they change all the time. Sometimes we’re not even in control of the source code of these pages.
We can advocate for new best practices like responsive images. The
srcset attribute on the
img element; the
picture element for when you need more control. These are great tools. What’s not so great is writing the markup. It’s confusing! Ideally we’d have a CMS drive this, but a lot of the time, landing pages fall outside of the purview of the CMS.
Scott has been using Vue.js to make a responsive image builder—a form that people can paste their URLs into, which spits out the markup to use. Anything we can do by creating tools like these really helps to defend the performance of a site.
Another thing we can do is lazy loading. Focus on the assets. The BBC homepage uses some lazy loading for images—they blink into view as your scroll down the page. They use LazySizes, which you can find on Github. You use
data- attributes to list your image sources. Scott realises that LazySizes is not progressive enhancement. He wouldn’t recommend using it on all images, just some images further down the page.
But thankfully, we won’t need these workarounds soon. Soon we’ll have lazy loading in browsers. There’s a
lazyload attribute that we’ll be able to set on
<img src=".." alt="..." lazyload="on">
It’s not implemented yet, but it’s coming in Chrome. It might be that this behaviour even becomes the default way of loading images in browsers.
If you dig under the hood of the implementation coming in Chrome, it actually loads all the images, but the ones being lazyloaded are only sent partially with a 206 response header. That gives enough information for the browser to lay out the page without loading the whole image initially.
To wrap up, Scott takes comfort from the fact that there are resilient patterns out there to help us. And remember, it is our job to defend the user’s experience.
This aspect of Vue appeals to me more than the all-or-nothing vibe I get from React:
By enabling incremental adoption, Vue’s progressive nature means that individuals can start using it here and there, a bit at a time, without having to do massive rewrites.
This is a lovely write-up of the WorldWideWeb hack week at CERN:
The Web is a success story in open standards, natural and by-design progressive enhancement, and the future-proof archivability of human-readable code.