Screen readers generally follow this order of precedence:
- Look for explicitly set ARIA attributes (roles, states, properties), and in the absence of those:
- Interpret any implicit roles from markup semantics (form elements, paragraphs, lists, etc).
- Read any text available in the markup.
If no discernable semantics exist (think ARIA-less
<span>tags), it skips to #3. So when you omit ARIA, you’re rolling the dice on whether users will understand that your
<ul>is a menu and not simply a list. When you omit ARIA and semantic tags, you’re leaving it up to your users to figure things out (and potentially abandon your product because it doesn’t work).
TIL that all modern browsers now support complex selectors in :not()! 😍
So you can do things like:
.foo :not(.foo .foo *)to match things inside one .foo wrapper but not two
.container :not(.content *)to get simple (shallow) “donut scope”
Weekly Timber is a client of mine that provides logging services in central Wisconsin. For them, a fast website is vital. Their business is located in Waushara County, and like many rural stretches in the United States, network quality and reliability isn’t great.
Wisconsin has farmland for days, but it also has plenty of forests. When you need a company that cuts logs, Google is probably your first stop. How fast a given logging company’s website is might be enough to get you looking elsewhere if you’re left waiting too long on a crappy network connection.
I initially didn’t believe a Service Worker was necessary for Weekly Timber’s website. After all, if things were plenty fast to start with, why complicate things? On the other hand, knowing that my client services not just Waushara County, but much of central Wisconsin, even a barebones Service Worker could be the kind of progressive enhancement that adds resilience in the places it might be needed most.
The first Service Worker I wrote for my client’s website—which I’ll refer to henceforth as the “standard” Service Worker—used three well-documented caching strategies:
- Serve static assets out of
CacheStorageif available. If a static asset isn’t in
CacheStorage, retrieve it from the network, then cache it for future visits.
- For HTML assets, hit the network first and place the HTML response into
CacheStorage. If the network is unavailable the next time the visitor arrives, serve the cached markup from
These are neither new nor special strategies, but they provide two benefits:
- Offline capability, which is handy when network conditions are spotty.
- A performance boost for loading static assets.
A better, faster Service Worker
The web loves itself some “innovation,” which is a word we equally love to throw around. To me, true innovation isn’t when we create new frameworks or patterns solely for the benefit of developers, but whether those inventions benefit people who end up using whatever it is we slap up on the web. The priority of constituencies is a thing we ought to respect. Users above all else, always.
There are certainly other challenges, but it’ll be up to you to weigh the user-facing benefits versus the development costs. In my opinion, this approach has broad applicability in applications such as blogs, marketing websites, news websites, ecommerce, and other typical use cases.
All in all, though, it’s akin to the performance improvements and efficiency gains that you’d get from an SPA. Only the difference is that you’re not replacing time-tested navigation mechanisms and grappling with all the messiness that entails, but enhancing them. That’s the part I think is really important to consider in a world where client-side routing is all the rage.