In the previous post I shared why accessibility properties of DOM elements might be the best way to target elements in tests. In this post let's see how can we practically do it.
Every DOM element has an "accessible name" and a "role", those are probably the most important accessibility-related properties of a DOM element, let's briefly review them.
Simply put, the accessible name would be inferred from the text content of an
HTML element, or from an "alt" attribute if it's an image - there's a
spec describing how the accessible name
should be calculated by a browser. aria-label
attribute should be specified
only when there's a need to override the otherwise inferred accessible name.
Roles are used to categorize elements of user interface. This means that knowing
the role, a screen reader would be able to explain to a blind user what kind of
a widget is in front of them. Be it an HTML element like <input type="range">
or, if we want to re-create the slider ourselves, a ridicilous soup of div
s with
the slider
role explicitly specified <div aria-role="slider">...</div>
, in
both cases disabled users will be able to reason about and interact with in the
slider the same way.
Key takeaway of this section: prefer the semantically appropriate HTML elements
and do not specify aria-*
attributes manually unless absolutely necessary;
semantic HTML markup already implies sane defaults. How do you know if it's
necessary or not? Check the "Accessibility" sub-tab of the "Elements" tab in
Chrome DevTools: "Computed Properties" section should contain meaningful
Name
and Role
properties.
Let's do a quick lab to consolidate what we've just discussed.
Start with the following HTML document, intentionally using non-semantic HTML elements. Save it as an HTML file and open in a browser.
<html>
<body>
<div style="border: 1px solid black; display: inline;">click me</div>
</body>
</html>
If you're on a Mac, trying this page in a screen reader takes 2 seconds: click the "Siri" button at the top right, ask Siri to turn on VoiceOver and refresh the web page. You will hear VoiceOver saying "click me", giving no hint that it's a button.
Let's improve the code by using the semantically suitable HTML element:
<html>
<body>
<input type="button" value="click me" />
</body>
</html>
Now VoiceOver says "click me, button". Way better!
Here's how the "Accessibility" sub-tab of the "Elements" tab looks in Chrome:
Notice Role: button
and Name: "click me"
are inferred in without us
specifying aria-role
and aria-label
.
If we replace <input type="button" value="click me" />
with
<button>click me</button>
, nothing changes in accessibility attributes, just
as nothing changes in the user experience.
Now we can even use an image in the button, and as long as alt
attribute is
filled in, the same tests continue to pass:
Note on the screenshot that the Computed Properties accordion section gives us a
hint where the accessible name was inferred from (the alt
attribute in this
case).
So accessibility attributes serve as a higher order abstraction, allowing refactoring without breaking tests. Sweet!
Unfortunately, as of late 2021, there's no way to query the accessibility tree. Accessibility Object Model specification which aims to enable that is still in early draft.
We can't select with document.querySelector("[aria-label='click me']")
if
aria-label
was not explicitly specified, and it's important from both
productivity and correctness perspectives not to specify the accessibility
attributes explicitly unless absolutely needed.
But we know from the ARIA spec which HTML elements correspond to which roles, that's a 1:1 mapping. And the algorithm of inferring accessible names which is described in the "Accessible Name and Description Computation" spec can also be implemented. In fact, both are already realized in the Testing library. Until the Accessibility Object Model describes the API to query DOM elements by computed accessibility properties and the browsers implement it, I suggest using the Testing library.
Accessibility attributes provide a nice higher order abstraction, which allows refactoring the implementation while keeping the tests. Just note that there's no standard API for querying DOM elements by their counterpart nodes in the accessibility tree yet, but Testing library can serve as a polyfill.
Blog posts can fill in some gaps or give initial idea, but to really wrap your head around accessibility, use should allocate time to read the specs: