When and How to Use WAI-ARIA Attributes
In the world of the internet today, not being accessible isn’t just poor UX and business practice, it can cause legal issues. To combat this, frontend developers write highly semantic HTML to help move people around a website using properly structured layouts, correct use of headings, clean and descriptive alt text, and other practices.
There are several good ways to go about making certain a site is properly accessible. Semantic HTML is great — it’s the rebar that a strong website is built upon. But what happens when that isn’t enough to clearly get the information across? One common approach to improve accessibility when markup alone is not enough is using WAI-ARIA attributes.
What is WAI-ARIA and WAI-ARIA attributes?
If HTML is a website’s rebar providing the underlying strength for accessibility, then WAI-ARIA, or ARIA for short, is the spackle to treat small remaining issues. WAI-ARIA stands for Web Accessibility Initiative -- Accessible Rich Internet Applications. Published by the World Wide Web Consortium (W3C), this spec spells out how to increase the accessibility of web pages. What that means is that by using ARIA attributes, a developer can take a web page (or at least portions of a web page), and have them declare themselves as applications rather than static documents. This brings increased accessibility to dynamic content and user interface components. This is done by attaching proper ARIA attributes to an HTML document to expand on their semantic meaning. ARIA can be broken down into two basic groups: ARIA Roles and ARIA Properties and States.
Roles define what an element or region does. The most commonly seen and used roles are known as Landmark Roles, which serve to duplicate a semantic value for structural elements in HTML5, but these aren’t the only roles. In fact, there are several other groups of roles that can be used throughout a website. I’ll start with Landmark Roles as they are the most prevalent and widely recognizable then move onto others.
Landmark Roles include banner, complementary, contentinfo, form, main, navigation, region, and search. Each of these can be set into an HTML template for assistive technology to quickly identify and navigate to large sections of the document. Here is an example of several of these in action:
<header role=”banner”> <img src=”best-logo-ever.svg” alt=”Best Company Ever"/> <nav role=”navigation”> <!-- list of links to website locations --> </nav> <div role=”form”> <form role=”search”> <!-- all the inputs --> </form> </div> </header> <main role=”main”> <!-- important stuff goes here --> <div role=”region”> <!-- this is significantly most important, yo --> </div> </main> <aside role=”complementary”> <!-- there’s still room for important stuff here as well --> </aside> <footer role=”contentinfo”> <!-- even more importance --></footer>
Seems familiar, right? There are several more roles that aren’t as familiar. These include Widget Roles, Composite Roles, Document Structure Roles, and Live Region Roles.
Widget Roles are roles attached to various interactive elements across the site. Buttons, form inputs, menus, links, sliders, scrollbars, radios, textboxes, and several other items may have one of these roles attached to them or nested inside them. As an example, let’s take the navigation from above and expand on it a bit:
<nav role=”navigation”> <ul> <li role=”menuitem”> <a href=”/your-awesome-link”>Critical Role</a> </li> <li role=”menuitem”> <a href=”/your-other-awesome-link”>Dice, Camera, Action</a> </li> </ul> </nav>
Now we have a fleshed out navigation with roles that help to explain what each part of the navigation does. Assistive technologies can move through our menu and know that it is a navigation menu, which parts are menu items, and which parts are the links inside them. This can be taken a bit further by using Composite Roles.
Composite Roles define containers for a widget’s UI components. With the appropriate role attributes, we can definitively tell is something is a combobox, listbox, grid, radiogroup, or something else. We can even use it to expand on our navigation a bit more:
<nav role=”navigation”> <ul role=”menu”> <li role=”menuitem”> <a href=”/your-awesome-link”>Critical Role</a> </li> <li role=”menuitem”> <a href=”/your-other-awesome-link”>Dice, Camera, Action</a> </li> </ul> </nav>
Note that now the unordered list has the menu role to designate the menu container. What we are doing here is expanding on interactive bits of our website giving them clearer meaning for assistive tools to help navigate people across the page which is cool, but we are not confined to only working on interactive elements.
Document Structure Roles add semantics to the content organization of a page and are usually not interactive. What this means is these roles give semantic value to something that normally is not interacted with such as images, lists, documents, or tables. An easy example would be adding the img role to a container for images:
<svg role=”img”> <!-- sv-gee this is a nice picture --> </svg> Or we can turn our navigation into a list.: <ul role=”list”> <li role=”listitem”> <a href=”/your-awesome-link”>Critical Role</a> </li> <li role=”listitem”> <a href=”/your-other-awesome-link”>Dice, Camera, Action</a> </li> </ul>
<button onclick=”triggerAlert”>Hey, click me! I’m a button!</button> <p class=”alert”>This has been a test of your clicking abilities.</p>
<button onclick=”triggerAlert”>Hey, click me! I’m a button!</button> <p class=”alert” role=”alert”>This has been a test of your clicking abilities.</p>
This will force accessibility tools to take notice of the new content showing up the page and read the recent change that took place.
Properties and States
While Roles define a component’s purpose in an interface, Properties and States refer to the characteristics and current conditions of those components. Properties and States have some overlap on what their purposes are making the definitions between the two a bit more subtle but at its clearest Properties define the characteristics of the component while States define the current condition of the component.
Like Roles, Properties and States are further broken down into separate groups of attributes that include Widget Attributes, Live Region Attributes, Drag & Drop Attributes, and Relationship Attributes. We have previously discussed a few of these in a previous blog about the accessibility of names and labels but that was just getting our toes wet. Let us really jump into the deep end of these starting with Widget Attributes.
Widget Attributes help to further define the semantic meaning of a component by saying if a component is hidden from accessibility tools or not, whether a component is required, if there is a trackable value, and more. For example, similar to how we may not want content viewable to a visual user, but we may want it present for screen readers, we may also want to hide content from a screen reader that is present to the viewer. To do this we can add aria-hidden to an item and tell the computer whether or not to hide it from assistive devices.
<p aria-hidden=”true”>In a game of hide and seek accessibility, the tools would lose.</p> <p aria-hidden=”false”>In the same game I would so easily be seen. Like seriously, no competition.</p>
What this is saying, when aria-hidden equals true, screen readers treat the content as if it isn’t there. The opposite happens if it is set to false allowing it to be read by screen readers. The aria-required attribute works similarly.
<form action=”post” role=”form”> <label for=”first-name”>First name:</label> <input id=”first-name” type=”text” aria-required=”true” required/> </form>
Unlike aria-hidden, HTML5 already has a required tag that can be added to an input to designate whether or not a component is in fact required. However, not all accessibility tools work the same on each browser and this sort of redundancy can be beneficial should an assistive tool fail to note what is and isn’t required by using the required tag alone. Using the aria-required attribute forces assistive tools to note when something is needed to move forward on a form or page.
<form action=”post” role=”form”> <div aria-live=”polite”> <label for=”first-name”>First name:</label> <input id=”first-name” type=”text” aria-required=”true” required/> <button id=”first-name-button” role=”button”>Best First Name Ever!</button> </div> </form
Using a Live Attribute would make the user aware the change has happened. We can do this by using aria-live in the div we’ve wrapped around the input, showing assistive technologies there was a change that needs to be made aware to the user.
Relationship Attributes define relationships between components making a clearer organization through the page and giving a better understanding of the grouped content. For example:
<div id=”languages”>Languages</div> <div> <label for=”language-input-1” id=”italian”>Italian</label> <input type=”text” id=”language-input-1” aria-labelledby=”languages italian"/> </div> <div> <label for=”language-input-2” id=”german”>German</label> <input type=”text” id=”language-input-2” aria-labelledby=”languages german"/> </div> </div>
The inputs call a relation back to their specific labels and the container that holds them. This keeps us from having to duplicate labels to add a clear relation between the input components, their label, and their container.
When to use ARIA
So after looking through this, you’re probably thinking “Let’s use ARIA on EVERYTHING!”. Well, remember when you were growing up how you used to be told too much of something can be bad? Same applies here. ARIA doesn’t make everything more accessible. In some cases it can actually hurt accessibility. ARIA overwrites semantic meaning that is already implied by HTML. While previous to HTML5 websites were constructed using a highly complex mess known as div soup, now HTML has custom, built in semantic meaning.
As the internet has grown an attempt has been made to make the spread of information across the web accessible to all those that use it. The web is now accessible by default. Let’s take the first example and expand on the navigation a bit more.
<nav role=”navigation”> <ul role=”menu”> <li role=”menuitem”> <a href=”/your-awesome-link”>Critical Role</a> </li> <li role=”menuitem”> <a href=”/your-other-awesome-link”>Dice, Camera, Action</a> </li> </ul>
It’s already implied to accessible devices that this is a navigation when it comes across a <nav> tag. What do we gain by adding role=”navigation” to this? Not a lot. So why go over all of this if we aren’t allowed to use them? Well, it’s not that you aren’t allowed, you just need to understand when the correct time to use them is. Take for example a listitem role. We as developers know that if we are going to use a list, generally we can use the <li> tag because the semantic meaning is already baked in but what if you need to give the same semantic meaning to a div instead? Well, that’s a good reason to use the role.
<div> <div role=”listitem”> <!-- all the lorems --> </div> <div role=”listitem”> <!-- and also the ipsums --> </div>
What if we have multiple Navigations on a single page? That’s when ARIA has your back.
<nav aria-label=”Awesome Things"> <!-- list your awesome things here --> </nav> <div role ="navigation" aria-label="Other Awesome Things"> <!-- other awesome things go here --> </div>
Earlier I spoke about how HTML is the rebar that helps put together a strong foundation for a website and ARIA is the spackle that fixes the smaller issues. As developers using ARIA, our job isn’t to try and rebuild from the ground up but rather to fill the holes that HTML leaves open in its natural semantic meaning with that of ARIA. At the end of the day, we want the web to be accessible to everybody, and we can do that by remaining aware of when HTML is not enough.