HTML - WAI-ARIA
Craig Abbott
Head of Accessibility, Digital
@abbott567
What is WAI-ARIA?
WAI stands for Web Accessibility Initiative. They are part of the World Wide Web Consortium (W3C) who develop standards and support materials for accessibility.
ARIA stands for Accessible Rich Internet Applications.
Why is it important?
The internet is full of beautiful interfaces and exciting interactions. ARIA is used to make these experiences more inclusive by making them work with assitive technologies.
It is also important to pass WCAG 2.1 criterion 4.1.2 Name, Role, Value.
What does it do?
It adds additional context for assistive technology users, for dynamic content and advanced user interface controls.
How does it work?
ARIA modifies the Accessibility Tree, which is what assistive technology uses to present content to your user.
It does not change anything about an elements look, function or behaviour.
The Accessibility Tree
Assistive technology cannot see your HTML in the Document Object Model (DOM).
It can only read the Accessibility Tree which is built automatically by the browser using your HTML code.
You cannot edit the accessibility tree directly, you can only change it by the elements, roles and attributes you use.
The Chrome Accessibility Tree
- Open Chrome DevTools
- Choose the Accessibility Tab
- Select "Enable full-page accessibility tree
- Choose the accessibility icon at the top right of the DOM
A word of warning
It’s easy to get carried away when you first discover ARIA. But there’s a saying:
No ARIA is better than bad ARIA.
Pages with ARIA produce around 40% more accessibility errors than those without
This is mainly down to bad implementation. But it’s also because ARIA is used to translate complex interactions. And, the more complexity you add, the more likely it is to go wrong.
Use progressive enhancement
ARIA is often used in conjunction with JavaScript, but there are many reasons why JavaScript might not load.
You should always start with working native HTML and then layer the JavaScript over the top to modify the default behaviour.
The 5 rules of ARIA
They call them rules, but they’re more like guidelines.
Note: I’ve summarised them for the purpose of this talk
1: Try not to use ARIA
If you can use a native element to achive the behaviour you want, then do so. Only make custom elements if you absolutely have to.
2. Try not to change native semantics
Unless you have to, try not change native elements. Especially if you’re trying to use a non-interactive element to create an interactive one.
3. All interactive controls must work with a keyboard
Anything you create which you can click, tap, drag, drop, slide, or scroll must also work with a keyboard. All custom controls must be mapped to standard keystrokes.
For example, if you make a custom button using role="button"
then make it work using both the space and the enter key, because thats how the native <button>
element works.
4. Do not hide elements which can still be focused
Do not use role="presentation"
oraria-hidden="true"
on anything which is still interactive using a keyboard.
Note: aria-hidden is inherited, so this also applies to parent elements where children are still interactive
5. All elements must have an accessible name
An element may have a visible name, but it is only considered to have an accessible name when it is made available to assistive technology.
ARIA categories
There are 3 main categories.
We will cover:
- Roles
- States and properties
- Live regions
We will also look at:
- Accessible names
Roles
The role is the main identity of an element and sets the expectation for assistive technology users on how it should behave.
Custom elements should be consistent with native ones so that they are recognisable and familiar. For example, if you make it look like a button, it must behave like a button.
Implicit ARIA roles
Every element has an implicit ARIA role. This means you don’t usually need to assign it one, the browser will do it automatically.
Most of them are obvious:
<main> <!-- role="main" -->
<button> <!-- role="button" -->
<img> <!-- role="img" -->
<img alt=""> <!-- role="presentation" -->
Some implicit roles are a bit weird
Some elements have roles which don’t match their tags.
These are usually legacy elements which have been around since HTML was first invented. So don’t always assume the pattern is always role="<element>"
.
<header> <!-- role="banner" -->
<footer> <!-- role="contentinfo" -->
<a> <!-- role="link" -->
Presentation role
If you use role="presentation"
then you remove that element from the accessibilty tree, but leave it visible on the page.
This is very similar to aria-hidden
, but there are differences, which we will cover later.
<hr role="presentation">
Changing a role
Any element can have its role overidden, so it’s important that the role and the visual styling match.
<a href="/buy-it-now" class="button" role="button">
Buy it now
</a>
Having the correct role is important
Assistive technology does not know what an element looks like, only what role it is assigned. It cannot see the elements, it just reads their properties in the Accessibility Tree.
If there is a mismatch between the visual element and the accessibility tree, it causes issues for people.
Example of roles
Can be found at the following path:/demos/aria-roles
States and properties
The role is the main identifier for the element, and states and properties add additional context.
There are 3 main categories:
- Widget attributes
- Live region attributes
- Relationship attributes
There is a 4th category, but it is deprecated
The 4th category is ‘Drag and drop attributes’, but in the current version of ARIA both attributes are deprecated.
The attributes are:
aria-grabbed
- the element is currently being held by the cursoraria-dropeffect
- the grabbed element has been dropped onto the target
1. Widget attributes
A widget attribute helps a user to understand the current state of something. For example, an element can be:
- hidden (
aria-hidden="true"
) - pressed (
aria-pressed="true"
) - selected (
aria-selected="true"
) - expanded (
aria-expanded="true"
) - collapsed (
aria-expanded="false"
)
ARIA hidden
If you use CSS to visually hide an element using display:none
or visibility:hidden
then it will also be hidden to assistive technology automatically.
ARIA hidden can be used to hide elements from assistive technology but still keep them visible on the page.
<hr aria-hidden="true">
ARIA hidden vs Presentation
aria-hidden
and role="presentation"
are deceptively similar in that they remove elements from the accessibility tree. The difference is in the inheritance.
role="presentation"
will only remove the element you place it on. Whereas aria-hidden
will also remove all of it’s children.
ARIA hidden is inherited
If you use aria-hide
a parent, then all of it’s children will be hidden also.
<section aria-hidden="true">
<a href="/home">Home</a> <!-- aria-hidden is also true -->
</section>
Example of ARIA hidden and the Presentation role
Can be found at the following path:
/demos/aria-hidden-and-role-presentation.html
Example of ARIA-expanded
Can be found at the following path:/demos/aria-expanded
2. Live region attributes
A live region helps a user to understand what is happening in a section which has dynamic content. There are attributes you can use to let assistive technology know when it should announce changes.
For example:
aria-busy
- the content is still loadingaria-live
- inform the user if any text is added to the sectionaria-relevant
- supposed to inform the user when content is removedaria-atomic
- read all the content in the section again, not just the bit which changed
Do not use aria-relevant
Aria relevant is pretty broken. Voiceover and NVDA ignore it, and JAWS will just say ‘Removed’ with no other context.
Rather than using aria-relevant
to track removed content, such as somebody leaving a chat session, add a line of text to a regular aria-live
region which states what was removed.
For example:
Craig Abbott has left the chat.
ARIA live
ARIA live regions are necessary when you have content changing on the page away from the users focus. For example, if you receive a notification.
People who are sighted might see a notification banner pop up, or a red dot appear. Blind users will also need to be informed in a timely manner.
Different options for aria-live
ARIA live can be set to one of 3 options:
off
- implicit role on every element, nothing is read outpolite
- the screen reader will queue the changes and read them out when its finished what it is currently sayingassertive
- the screen reader will stop what it is saying and immediately announce the changes
Do not use assertive unless absolutely necessary
Assertive is jarring and it can be confusing. It will aggressively interrupt other information which might be important, and it increases cognitive load for the user.
Only use it in emergency situations or when there is something the user is required to do immediately. For example, if they’re about to be logged out and lose unsaved work.
Alert role
If you use role="alert"
on an element, it will automaticaly be assigned the attributes aria-live="assertive
and aria-atomic="true"
.
Example of ARIA-live
Can be found at the following path:/demos/aria-live
3. Relationship attributes
Relationship attributes help assistive technology to understand how elements work together. For example:
aria-controls
- attaches an interface control to another elementaria-labelledby
- allows you to use something else on the page as an accessible namearia-describedby
- adds additional context to elements by using another element on the page
Accessible names
Under WCAG 2.1 criterion 4.1.2 Name, Role, Value, every element needs to have an accessible name.
The accessible name is what the assistive technology reads from the tree, not what you can visually see on the page.
Using native elements
The easiest way to give something an accessible name is to use native elements.
<button>Continue</button> <!-- name === 'Continue' -->
<label>
Postcode
<input type="text"> <!-- name === 'Postcode' -->
</label>
Using aria-label
You can apply an accessible name directly by using aria-label
. But this wouldn’t show visually on the page. So be careful how you use it!
Bad example:
<input type="text" aria-label="Postcode">
Good example:
<label>
Search
<input type="search" aria-label="Search for people, posts, or pages">
</label>
Using aria-labelledby
aria-labelledby
works the same way as aria-label
, except it uses the text from another element to create the label, rather than providing it as a string.
<p>
You will need to provide your
<span id="an-label">account number</span>
</p>
<input type="text" aria-labelledby="an-label">
Using aria-describedby
aria-describedby
will append additional context to the label rather than acting as the label itself.
<label for="an-input">
What is your account number?
</label>
<p id="an-hint">
Must be between 6 and 8 digits long
</p>
<input id="an-input" type="text" aria-describedby="an-hint">
Demo of ARIA labels
Can be found at the following path:/demos/aria-labels
Thank you!
Craig Abbott
Head of Accessibility, Digital
@abbott567