An opinionated guide to accessibility testing /// Iain Bean
- First impressions
- The Tab key
- Automated testing tools
- Screen reader testing
- Next steps
- First impressions
- The Tab key
- Automated testing tools
- Screen reader testing
- Next steps
Accessibility on the web is easy. Accessibility on the web is also hard.
I think it’s one of those 80/20 situations. The most common accessibility problems turn out to be very low-hanging fruit. Take, for example, Holly Tuke’s list of the 5 most annoying website features she faces as a blind person every single day:
- Unlabelled links and buttons
- No image descriptions
- Poor use of headings
- Inaccessible web forms
- Auto-playing audio and video
None of those problems are hard to fix. That’s what I mean when I say that accessibility on the web is easy. As long as you’re providing a logical page structure with sensible headings, associating form fields with labels, and providing alt text for images, you’re at least 80% of the way there (you’re also doing way better than the majority of websites, sadly).
Ah, but that last 20% or so—that’s where things get tricky. Instead of easy-to-follow rules (“Always provide alt text”, “Always label form fields”, “Use sensible heading levels”), you enter an area of uncertainty and doubt where there are no clear answers. Different combinations of screen readers, browsers, and operating systems might yield very different results.
This is the domain of interaction design. Here be dragons. ARIA can help you …but if you overuse its power, it may cause more harm than good.
When I start to feel overwhelmed by this, I find it’s helpful to take a step back. Instead of trying to imagine all the possible permutations of screen readers and browsers, I start with a more straightforward use case: keyboard users. Keyboard users are (usually) a subset of screen reader users.
The pattern that comes up the most is to do with toggling content. I suppose you could categorise this as progressive disclosure, but I’m talking about quite a wide range of patterns:
In each case, there’s some kind of “trigger” that toggles the appearance of a “target”—some chunk of content.
The first question I ask myself is whether the trigger should be a button or a link (at the very least you can narrow it down to that shortlist—you can discount div
s, span
s, and most other elements immediately; use a trigger that’s focusable and interactive by default).
As is so often the case, the answer is “it depends”, but generally you can’t go wrong with a button. It’s an element designed for general-purpose interactivity. It carries the expectation that when it’s activated, something somewhere happens. That’s certainly true in all the examples I’ve listed above.
That said, I think that links can also make sense in certain situations. It’s related to the second question I ask myself: should the target automatically receive focus?
Again, the answer is “it depends”, but here’s the litmus test I give myself: how far away from each other are the trigger and the target?
If the target content is right after the trigger in the DOM, then a button is almost certainly the right element to use for the trigger. And you probably don’t need to automatically focus the target when the trigger is activated: the content already flows nicely.
<button>Trigger Text</button>
<div id="target">
<p>Target content.</p>
</div>
But if the target is far away from the trigger in the DOM, I often find myself using a good old-fashioned hyperlink with a fragment identifier.
<a href="#target">Trigger Text</a>
…
<div id="target">
<p>Target content.</p>
</div>
Let’s say I’ve got a “log in” link in the main navigation. But it doesn’t go to a separate page. The design shows it popping open a modal window. In this case, the markup for the log-in form might be right at the bottom of the page. This is when I think there’s a reasonable argument for using a link. If, for any reason, the JavaScript fails, the link still works. But if the JavaScript executes, then I can hijack that link and show the form in a modal window. I’ll almost certainly want to automatically focus the form when it appears.
The expectation with links (as opposed to buttons) is that you will be taken somewhere. Let’s face it, modal dialogs are like fake web pages so following through on that expectation makes sense in this context.
So I can answer my first two questions:
…by answering a different question:
It’s not a hard and fast rule, but it helps me out when I’m unsure.
At this point I can write some JavaScript to make sure that both keyboard and mouse users can interact with the interactive component. There’ll certainly be an addEventListener()
, some tabindex
action, and maybe a focus()
method.
Now I can start to think about making sure screen reader users aren’t getting left out. At the very least, I can toggle an aria-expanded
attribute on the trigger that corresponds to whether the target is being shown or not. I can also toggle an aria-hidden
attribute on the target.
When the target isn’t being shown:
aria-expanded="false"
,aria-hidden="true"
.When the target is shown:
aria-expanded="true"
,aria-hidden="false"
.There’s also an aria-controls
attribute that allows me to explicitly associate the trigger and the target:
<button aria-controls="target">Trigger Text</button>
<div id="target">
<p>Target content.</p>
</div>
But don’t assume that’s going to help you. As Heydon put it, aria-controls
is poop. Still, Léonie points out that you can still go ahead and use it. Personally, I find it a useful “hook” to use in my JavaScript so I know which target is controlled by which trigger.
Here’s some example code I wrote a while back. And here are some old Codepens I made that use this pattern: one with a button and one with a link. See the difference? In the example with a link, the target automatically receives focus. But in this situation, I’d choose the example with a button because the trigger and target are close to each other in the DOM.
At this point, I’ve probably reached the limits of what can be abstracted into a single trigger/target pattern. Depending on the specific component, there might be much more work to do. If it’s a modal dialog, for example, you’ve got to figure out where to put the focus, how to trap the focus, and figure out where the focus should return to when the modal dialog is closed.
I’ve mostly been talking about websites that have some interactive components. If you’re building a single page app, then pretty much every single interaction needs to be made accessible. Good luck with that. (Pro tip: consider not building a single page app—let the browser do what it has been designed to do.)
Anyway, I hope this little stroll through my thought process is useful. If nothing else, it shows how I attempt to cope with an accessibility landscape that looks daunting and ever-changing. Remember though, the fact that you’re even considering this stuff means you care more than most web developers. And you are not alone. There are smart people out there sharing what they learn. The A11y Project is a great hub for finding resources.
And when it comes to interactive patterns like the trigger/target examples I’ve been talking about, there’s one more question I ask myself: what would Heydon do?
A trashcan, a tyepface, and a tactile keyboard. Marcin gets obsessive (as usual).
I linked to the first of Ethan’s short videos on accessibility last week, but it’s well worth checking out all five:
I think this a solution worthy of Solomon. In this case, the Gordian knot is the select
element and its inevitable recreation in order to style it.
What if we instead deliver a native select by default and replace it with a more aesthetically pleasing one if possible? That’s where the “hybrid” select idea comes into action. It’s “hybrid” because it consists of two selects, showing the appropriate one at the right moment:
- A native select, visible and accessible by default
- A custom select, hidden until it’s safe to be interacted with a mouse
The implementation uses a genius combination of a hover
media query and an adjacent sibling selector in CSS. It has been tested on a number of device/platform/browser combinations but more tests are welcome!
What I love about this solution is that it satisfies the stakeholders insisting on a custom component but doesn’t abandon all the built-in accessibility that you get from native form controls.
Smart thinking from Sara to improve usability for keyboard users by using aria-hidden="true" tabindex="-1"
to skip duplicate links:
A good rule of thumb for similar cases is that if you have multiple consecutive links to the same page, there is probably a chance to improve keyboard navigation by skipping some of those links to reduce the number of tab stops to one. The less tab stops, the better, as long as it does not worsen or compromise on other aspects of usability.
I’ve cautiously implemented this pattern now over on The Session where snippets of comments had both a title link and a “more” link going to the same destination.
Here’s one simple, practical way to make apps perform better on mobile devices: always configure HTML input fields with the correct
type
,inputmode
, andautocomplete
attributes. While these three attributes are often discussed in isolation, they make the most sense in the context of mobile user experience when you think of them as a team.
This is an excellent deep dive with great advice:
You may think that you are familiar with the basic
autocomplete
options, such as those that help the user fill in credit card numbers or address form fields, but I’d urge you to review them to make sure that you are aware of all of the options. The spec lists over 50 values!
I think these are great habit-forming ideas for any web designer or developer: a day without using your mouse; a day with your display set to grayscale; a day spent using a different web browser; a day with your internet connection throttled. I’m going to try these!
Six steps that everyone can do to catch accessibility gotchas:
- Check image descriptions
- Disable all styles
- Validate HTML
- Check the document outline
- Grayscale mode
- Use the keyboard
In some situations, a date picker is overkill:
I have relied on plain text inputs as date fields with custom validation for the site, typically using the same logic on the client and the server. For known dates — birthdays, holidays, anniversaries, etc — it has tested well.
A deep dive into the :focus
pseudo-class and why it’s important.
Paul walks us through the process of making some incremental accessibility improvements to this year’s 24 Ways.
Creating something new will always attract attention and admiration, but there’s an under-celebrated nobility in improving what already exists. While not all changes may be visual, they can have just as much impact.
If you enjoyed reading Marcin’s serendipitous story on Twitter, here are the pictures to accompany it.
Ire rounds up a bunch of tools you can use to test accessibility, from dev tools to Tenon.
Choosing the right input type for your form field.
Jason breaks down the myths of inputs being tied to device form factors. Instead, given the inherent uncertainty around input, the only sensible approach is progressive enhancement.
Now is the time to experiment with new forms of web input. The key is to build a baseline input experience that works everywhere and then progressively enhance to take advantage of new capabilities of devices if they are available.
A useful primer on which combinations of attributes and values work best for which form fields: type
, autocomplete
, autocorrect
, and autocapitalize
.
It really isn’t hard to get the basics of accessibility right on the web …and yet those basics are often neglected.
Here’s a handy shortlist to run through, HIKE:
- H stands for headings and semantic markup.
- I stands for images and labels.
- K stands for keyboard navigation.
- E asks for you to ACT with a little extra love for custom components and more.
(ACT = ARIA, Colour Contrast, Text Size)
Jessica’s handy guide to writing the right quotes and accents on a Mac keyboard.
A great piece by Jason analysing the ever-blurring lines between device classes.
Mind you, there is one question he doesn’t answer which would help clear up his framing of the situation. That question is:
What’s a web app?