As you expand your accessibility knowledge, you've probably heard the term ARIA a few times, maybe with an explanation, maybe not. Let's start there: ARIA is a standard from the World Wide Web Consortium (W3C) via the Web Accessibility Initiative (WAI).
ARIA is an acronym that stands for Accessible Rich Internet Applications, which is a bit of a mouthful, but it's important to understand what ARIA is for, and it's all right in the name. The first A is Accessible, which makes sense—we all want our work to be accessible. The last three letters in the acronym should be taken as a group, referring to Rich Internet Applications, which, as per Wikipedia, are “web application[s] that ha[ve] many of the characteristics of a desktop application… and may allow the user interactive features such as drag and drop, background menu, WYSIWYG editing, etc.” Over the history of the internet, we've seen the web evolve from static sites, to simple applications in the browser, to the complex, distributed web applications we have now. First introduced in 2015, ARIA has always been meant for complex web applications, not simple pages or sites like blogs.
Another way to think of ARIA is as a polyfill for HTML semantics, as per Sara Soueidan, a freelance developer and designer who teaches accessibility workshops around the world. As you may know, a polyfill is “code that implements a feature on browsers that don't support the feature.” HTML semantics are crucial for accessibility because they provide context and meaning to our code (refer to my previous articles for more information: POURing Over Your Website and Easy Accessibility Wins) and should always be the first tool you reach for. But what about when those semantics simply don't exist? There's no such thing as an HTML tab UI element, so how do you build one and ensure that it's accessible? That's where ARIA comes in: You use ARIA to provide the missing meaning and context, so someone using assistive technology knows that tab 1 is linked to content section A, and tab 2 is content section B, etc.
Context
Context is crucial to accessibility, and native HTML elements provide this context by default. For example, if you take an HTML button, you know that it's a specific object on the page, separate and distinct from other objects. And you know what a button's role is: to be clicked! A button element has focus and click events, which are sent to the browser and the accessibility tree; a button can be disabled and a button can have a name, among other features.
On the other hand, you have elements like divs (or spans) that don't provide any of this context—because they're not meant to. A div is a generic element, and although you can make visual changes to have it appear to be a button, it won't be clickable without JavaScript and it won't be accessible without ARIA.
ARIA can provide context to an element, such as a div, in three ways. ARIA can add a role onto a generic element (i.e., a div becomes a button) or override the role that is provided to an element by default. ARIA can indicate the relationship between two elements (such as with aria-labelledby). And ARIA can provide information as to the element's state (i.e., a div being used as a checkbox that's either checked or unchecked).
Semantic elements, such as buttons, automatically provide information on role, relationship, and state to the browser and accessibility tree, but for custom elements, it's up to you to provide that information (and update it when necessary). If you have text inside a <p>
tag and text inside a div, only one of those provides additional context and meaning to assistive technology. (It's the p tag!)
Role, Relationship, and State
There are three foundational elements to ARIA: role, relationship, and state, and each one is essential to using ARIA correctly.
Role
Think of ARIA roles like HTML elements, where you're declaring what an element is. A button has a role of button, a heading has a role of heading, and a tab would have a role of tab (but would be a div or list base element as there is no native tab element in HTML).
An easy trap to fall into is assuming that visual context and meaning—like styling a div to look like a tab—also provides accessible context and meaning, but of course it doesn't.
There are six categories of ARIA roles available to you.
Abstract roles can be confusing if you've seen them without context. The introduction from the ARIA spec says: “The following roles are used to support the WAI-ARIA role taxonomy for the purpose of defining general role concepts,” which clears things right up. All you need to know is that abstract roles are not for content at all; they're purely used by browsers and should not be used by developers. Phew!
Thankfully, widget roles are much clearer in both definition and use. Widgets are interactive standalone or composite elements. Some examples include our old friend the tab element or a tree navigation element. Composite widgets are containers that wrap and manage other widgets inside them. For example, a tablist widget is a composite widget that includes multiple tab widgets as children. A tree composite widget includes treeitem widget child elements.
Depending on the list of ARIA widget roles you find, you may see roles that overlap with HTML elements, such as button, checkbox, link, and radio. This is either because the ARIA role pre-dates the HTML element or because there are situations where the HTML element cannot or should not be used. When everything else is equal, always choose the semantic HTML element over a corresponding ARIA role.
Although widgets are interactive elements, document structures are (usually) not. As you'd expect, document structure roles apply to sections of your document or page. As with widgets, many previously common ARIA document structure roles should no longer be used, as they've since been created as semantic HTML elements. This includes roles such as article, figure, and table.
One interesting thing to note is that separator is both a widget and a document structure role. Any guesses as to why? It comes down to interactivity: If the separator is focusable, it's a widget. And if it's not focusable, it's a document structure (and you should be using the <hr>
element instead).
Landmark roles are exactly what they sound like: roles that are used for navigation. As you know, ARIA is about assistive technology, and one of the most common types of assistive technology is a screen reader. Although a sighted user can use visual cues to see a banner, article, and form on a page, an assistive technology user may not be able to do so. And, if you've created some divs on your page to encompass a banner and an article, although you might think the job is done, remember that divs carry no semantic meaning, so you're not providing the context you think you are.
Giving your structural divs the appropriate role allows assistive technology users to navigate the page much like their non-assistive technology using counterparts: by narrowing in on the banner to see information on the next event, by quickly skimming through multiple articles to find the one they're interested in, and to locate the search bar to find contact information.
Landmark roles give developers the ability to mimic the semantic meaning provided by the visual appearance of elements, which, in turn, allows assistive technology users to distinguish between multiple generic divs quickly and easily.
If you have content that changes dynamically—say a success or failure message after the user performs an action—you'll want to use live regions.
Look at Figure 1 and imagine you're navigating this form, filling out each field, and then hitting the Save button once you're done. So far so good right?
Once you've clicked on Save and the request has been processed, the form updates to look like Figure 2, with a success message at the very bottom of the page, under the Save and Cancel buttons.
If you're a visual user, finding a message or alert on a page is usually simple—they tend to be positioned either above or below the control you just interacted with, and provide information so you know whether the action was successful (or not). But what if you're a screen reader user and don't primarily navigate by looking at the screen? A screen reader user will likely still be focused (in the focus state sense) on the Save button, which provides no indication that a message has appeared. In another situation, a visually impaired user may have increased the zoom level on the page, pushing the alert past the bottom of their visible screen.
Maybe you're an experienced screen reader user and are expecting a message to appear somewhere. But where is it? Depending on the preferences and standards of the website or application, the message could be above the form, or below, like in my example. It could even be off to one side or another—it's completely dependent on the decisions made by the developers and designers, and nearly impossible for someone to guess correctly the first time they encounter a form.
Try to put yourself in the position of a screen reader user encountering a form on an unfamiliar website for the first time. Maybe most sites you typically use have messages above the form, so you navigate using your screen reader all the way back up through the form—my sample form here is very short and simple, but many forms aren't, so this could be very time consuming. And then the message isn't at the top of the form on this website, so you navigate all the way back down through the form—going through every single field for the third time—to see if the message was underneath the buttons instead. Even worse, what if these success and error messages dismiss themselves, automatically disappearing after a set period of time? A screen reader user may never actually be able to find and access any type of success or failure message unless live regions are implemented.
Typically, an alert message is implemented within a div, which, as you know, has no semantic meaning on its own. This div is then given a live region role (alert, log, marquee, status, or timer) to ensure that assistive technology can pick up on the information contained within it.
In the sample form in Figure 2, you'd want to use the alert role, which immediately announces the text within the div so the user is aware that the page has changed in some way. If you had a site with a countdown of some type (perhaps an online quiz with a time limit, or purchasing concert tickets where the process has to be finished within a specific timeframe), the timer role would be more appropriate. The timer role doesn't automatically announce all changes because that would be intensely annoying, but it does make it possible to navigate directly to the element to hear the time left, among other features.
Finally, there are window roles, which are used for anything that pops up, or acts as a window within the browser (like dialog boxes). There are only two window roles, alertdialog and dialog, and their only difference is that alertdialog is modal and dialog is (usually) non-modal. Typically, alertdialog is used when the user's workflow is being disrupted and a response is required, while dialog is less interruptive.
These roles are simple to use, but for modal dialogs in particular, there are other accessibility requirements that must be considered. For example, in situations where a response is required, it's essential that the user is “trapped” within the modal and prevented from accessing the underlying page (called a focus trap). Think about a session timeout modal dialog window: The user must log back into the application to continue using it, but if the user isn't first alerted to the presence of the dialog and then trapped within it, they may be able to continue to work “under” the modal and not realize that nothing they do is being saved because their session has expired.
Relationship
The next element of ARIA is relationship, which admittedly is a personal term. The ARIA spec combines properties and state together, but I prefer to separate them to better understand the details of how ARIA is implemented. In the ARIA spec, properties are defined as how two or more elements are connected, which, to me, means relationships.
Relationships are generally straightforward. For example, there's aria-owns, which creates a parent/child relationship between elements, like you might find in a menu. For example, you might have Contact Us as a top-level menu item, and then options for phone, email, and physical address as child items underneath. Using aria-owns, you can create these relationships that are then communicated to assistive technology users in the same way a visual menu structure would be.
You may be familiar with aria-describedby and aria-labelledby, which can be used to link descriptions and labels to elements such as charts and images. A label is usually short and to the point, where a description is more detailed and verbose. If you've included a chart on your site, having a description of the axes, the data points, the labels, etc., is necessary for the chart to be accessible to all users.
If you have an accordion with a button to expand and collapse the section, you may want to use aria-controls to indicate the relationship between the button and the section that can be expanded or collapsed. The button has an aria-controls attribute that references the ID of the associated section, again creating a relationship between seemingly unrelated items.
State
At last, we have state. As mentioned earlier, I see more of a distinction between state and properties (or relationships) than the ARIA spec does, and another point of difference is that states are dynamic, while relationships don't (usually) change after they're set.
ARIA states indicate what's currently happening with the element, but also, what could happen with or to it in the future. For example, if you think about a checkbox that's enabled (checked), you know that the option is currently ON and you can then infer that it can also be turned OFF.
Some examples of ARIA state include:
- aria-busy
- aria-checked
- aria-disabled
- aria-expanded
- aria-hidden
- aria-invalid
- aria-pressed
- aria-selected
As you can see, these are all dynamic: An element is either checked or not, expanded or not, hidden or not. A lot of these are very similar in use: aria-checked, aria-pressed, and aria-selected are all different versions of the same functionality, and you chose which one to use based on the type of control you're creating (checkbox, toggle button, and dropdown list/combo box respectively).
Some of these are less commonly used than others—for example, aria-busy is specific to live regions and indicates that the region is currently being updated (like a constantly scrolling log, for example).
Role, relationship, and state are the fundamentals of ARIA. If you're going to use ARIA (and that's a big if), you need to consider the roles, relationships, and states for all the non-standard/non-semantic elements you're creating or using. And that question, whether you're going to use it? That's a big one.
ARIA Rules
The thing about ARIA is that it's an amazing tool that you should never have to use. There should be standard and semantic HTML elements for all the functionality you want to include on your websites and apps and games. But the standardization process works slowly and tech moves quickly, so there are always going to be gaps between the proper and correct way of doing something and what's actually happening in the real world. This is where ARIA comes in, at least for accessibility.
The title of this article is a bit tongue in cheek, but it's also very serious. There are five rules in the official ARIA specification document, and the first one is:
“If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of repurposing an element and adding an ARIA role, state, or property to make it accessible, then do so.”
Which, fancy specification language aside, is really straightforward. If you need a button on a page, use the button element, not a div. If you need a link on the page, use the link element, not a div. If you need a select list on the page, say it with me, use the select element, not a div. Of course, there are exceptions to every rule…
Don't Do It*
You should not use ARIA if a semantic HTML element exists for your needs—except if the element is available but not fully implemented (i.e., browser support) or if it hasn't been implemented in an accessible way.
Figure 3 shows a snippet of the support for various types of HTML5 controls in the major browsers (Chrome, Edge, Firefox, Internet Explorer, and Safari, from left to right) from html5accessibility.com. A green checkmark indicates that the control is implemented and fully accessible; a red X indicates that it's not implemented in an accessible way; an orange circle shows partial support; and a grey dash is not applicable.
In Figure 3, you can see that Internet Explorer doesn't fully support the number and range inputs, Firefox only partially supports the color input (Safari doesn't support it at all), and datalist, progress, and meter are also partially supported in some of the browsers.
To reiterate, this doesn't mean that you can't use those HTML5 controls in your website, but you do need to be aware that they haven't been fully and accessibly implemented in all the browsers that you may support for your users. If you visit the HTML5 Accessibility page and see an unimplemented or partially implemented element, that's a good time to reach for ARIA to close the gaps.
The second exception when it's appropriate to use ARIA is when you need an element that simply doesn't exist in HTML, like a tab interface. There's literally nothing else you can do, so you must use ARIA in those situations.
Finally, the ARIA spec states a third exception: “if the visual design constraints rule out the use of a particular native element, because the element cannot be styled as required.” And I get it. We've all had a boss or stakeholder breathing down our neck about the look and feel of our sites and applications, and shouldn't that button be a slightly different shade of blue and what if it had rounded corners (but not too rounded), and and and. If you have a 100% must-do requirement to style an element in a certain way, and it's not possible with the built-in HTML element, you've got to do what you've got to do. But I do think that this exception offers people a bit too easy of a way out and everyone should really think about what they're doing before they drop a native element and reach for something custom.
We're in an age of massive and blazingly fast technological advancement and every single browser and device and app is doing things just a tiny bit differently. Frankly, there's no such thing as an experience that's 100% the same for all users, everywhere, in every situation. If you've got even the smallest bit of wiggle room to push back just a little about the drop-down in Firefox looking slightly different than in Chrome, do your best to advocate for a standardized control over a custom one.
Don't Do That Either
Alright, so I've thoroughly covered the first rule and its exceptions. The second rule is “Don't change native semantics, unless you really [really, really] have to.”
The key aspect of this rule is around combining elements and how you need to have a solid understanding of what you're trying to do—and why—before you go putting ARIA roles onto everything.
Say you're setting up a tab UI element. You're, of course, thinking about accessibility from the beginning, so you want to make sure that each tab is a heading for better organization, discoverability, and navigation.
You also know that there's no native HTML element for tabs, so you'll definitely need to use ARIA. With those two points in mind, you write this code:
<h2 role="tab">Tab 1</h2>
<h2 role="tab">Tab 2</h2>
And that's it, you've got headings that are also tabs, done! Well, not so fast. What you've actually done here is taken the semantic meaning of the heading elements and overridden it. You no longer have the headings you were using for organization, discoverability, and navigation because those headings are now tabs as far as the accessibility tree is concerned.
A fundamental piece of ARIA is that no piece or aspect of ARIA affects the visual display of an element, or the use of the element via standard means (i.e., when not using assistive technology). These tabs are going to look like headings on the screen, and you'll be able to target them in CSS or JavaScript as headings. The only effect of adding the role=“tab” piece is to make a screen reader or other assistive device announce these elements as tabs, not headings. Setting a role explicitly always overrides any existing roles or semantic meaning present on an element; you cannot add to what is already there, only replace it.
In a shocking twist, what's actually needed here is a couple of divs (or spans), not headings.
<div role="tab">
<h2>Tab 1</h2>
</div>
<div role="tab">
<h2>Tab 1</h2>
</div>
Now you have an element that started without any semantic meaning (the divs) that have been given meaning, via ARIA. You also still have your standard headings, with their role coming from the fact that they're semantic tags with inherent meaning. Someone using assistive technology on a page with this code will know that there's a tab element due to the role on the div, and will also still be able to navigate headings in the way they're most comfortable with (i.e., many screen readers have a hotkey that allows users to jump from heading to heading, like a table of contents).
Keyboards Exist
The third rule of ARIA reminds you that keyboards exist as more than an input device and that some people use them to navigate instead of a mouse. The official rule is: “All interactive ARIA controls must be usable with the keyboard.” There are, of course, additional details, but what it boils down to is that if you can do it with a mouse, you must be able to do the same thing (or an equivalent action) with the keyboard. And if there are standard keys or key combinations (think CTRL+S to save) for functionality, you must make sure those work as well.
This rule specifically calls out keyboards and that's because most assistive devices used to navigate mimic keyboard functionality. Most of us don't have a sip and puff device available to test with, but we all have a keyboard. ARIA, of course, refers to the accessibility of specific controls, but more broadly, your entire site and app need to be navigable via keyboard, even if there are few interactive controls.
The most common issues with keyboard navigation are the result of using a non-semantic element, such as a div, to create a custom control, like a button or drop-down. The developer (or designer) either doesn't know, or forgets to implement features like focus (by default, divs are not interactive and thus not focusable), or activation via keyboard instead of clicking/tapping (using the Enter/Return keys or space bar, which triggers the keydown event, instead of the click event).
Focus
The next ARIA rule is about ensuring that focusable elements aren't disabled or hidden from assistive technology when using ARIA. The rule states: “Do not use role=presentation or aria-hidden=true on a focusable element.”
The presentation role is interesting: If you have absolutely no choice but to use tables for your layout (in an email, for example), by adding the role of presentation to the table itself, you're indicating to assistive technology that the table doesn't contain data and should be ignored. When you apply role=“presentation” to an element, you're functionally removing all semantics from that element, as far as the accessibility tree is concerned.
If you investigated the different types of ARIA roles mentioned previously, you may have noticed the role=“none” option, which was introduced because developers didn't understand that role=“presentation” means no semantics. Browser support for role=“none” isn't very robust, so you should stick to using presentation instead. Living standards are fun!
Back to the fourth rule and aria-hidden, which works almost the same way as role=“presentation”. If aria-hidden is enabled (aria-hidden=“true”), the element is hidden from the accessibility tree in the browser. Again, I'm only talking about the accessibility tree here, not anything visual. If you're using role=“presentation” on a table or aria-hidden on a button, the table borders are still going to be visible unless you've removed them with CSS, and the button is still going to be present on the page and clickable. Let's look at some examples.
First, here's a table with a presentation role:
<table role="presentation">
<tr>
<td>Row 1, Col 1</td>
</tr>
<tr>
<td>
<button>My Button</button>
</td>
</tr>
</table>
Normally, assistive technology announces a table as a table, and if it's coded properly, the assistive technology is aware of the table headers and footers as well as row and column headers. As the user navigates through the contents of the table, each cell is announced individually, along with the row and cell headers for context.
By adding role=“presentation” in this example (or role=“none”), you've told the browser's accessibility tree that this table isn't really a table so it doesn't need to announce any of the standard table elements. Instead, the screen reader navigates as if the table wasn't even there, simply announcing elements within the table in the order they appear in the code—which, in this case, is the text in the first row and the button in the second row.
The presentation (and none) role effectively absorbs and negates any semantics that are related to the element they're used on, so in this case, anything to do with a table (thead, tfoot, tr, td, colspan, etc.) is removed when the accessibility tree is generated. This only affects the element it's used on (the table in the example), so any interactive elements inside the table are announced and work as expected—in this case, My Button in the second row. The first column is announced as and continues to function as a button. I cannot emphasize enough that ARIA has no visual effect. If you forget to remove the table's borders, visual users are still going to know it's a table, even if they're also using a screen reader and it's not announced as a table (which would be very confusing).
Next, an aria-hidden example:
<button aria-hidden="true">Spoon</button>
<div aria-hidden="true">
<button>Fork</button>
</div>
Here's a button that uses aria-hidden and a button inside a div that uses aria-hidden. Both of these have the same result: As far as assistive technology goes, there's no spoon (and no fork). Visually, of course, both buttons appear, but any assistive technology will not be able to interact with either button. Unlike role=“presentation”, aria-hidden is inherited by all child elements, making both buttons inaccessible, even though the second one is hidden indirectly.
Using aria-hidden is a bit of a nuclear option, but there are situations where it's appropriate. The general rule is to use aria-hidden when the content is decorative, such as a background image, or if it's duplicated, like an icon in a button that also has text. What you don't want to do with aria-hidden is to use it “just in case.” If you have content that needs to be hidden from everyone, display: none or visibility: hidden removes those elements visually and from the accessibility tree. And as you know, you should always take advantage of behavior that's provided by default rather than creating custom functionality.
Names Matter
The last of the ARIA rules is that “[a]ll interactive elements must have an accessible name.” If you're using the HTML label element (and you've set it up properly), then congratulations, you've got an accessible name for your element!
There are two ways to use the label element to properly and accessibly label your fields. The first is to use the for attribute on the label and link it to the correct input field:
<label for="userName">User Name:</label>
<input type="text" id="userName" />
Visually, you'll still need to place the label in an appropriate location but any assistive technology knows about the relationship between these two elements automatically because the ID of the input field (userName) has been used in the label's for attribute to connect these elements.
The second way to use a label is to wrap it around the input field:
<label>
User Name:
<input type="text" />
</label>
However, you can only use this option if the inner element (in this example, the input field) is labelable as per the HTML specification.
If, instead, you had a div inside the label instead of an input field:
<label>
User Name:
<div>This is a non-labelable element</div>
</label>
Then you'd have a visual label and name but not an accessible one. This is because divs aren't labelable, so the accessibility tree cannot create a relationship between the elements, even though the structure is the same as the previous example with the input field.
While we're talking about labelable elements, what are they?
- button
- input (except hidden inputs)
- meter
- output
- progress
- select
- textarea
That's basically a list of anything you might want to put a label on, of course!
And if, for some reason, you can't use the label element, there are aria-label and aria-labelledby to fall back on:
<input type="text" aria-label="User Name" />
<button aria-label="Save"></button>
Like with the label element, if an element is labelable, you can use aria-label directly on the element (which is a nice little tautology). Again, these are not visual labels, just accessible ones. These would pass accessibility checks, but they might not be very usable regardless. What if the icon in the button isn't clear? What if the input field is using a placeholder label?
- What do you do if you have an unlabelable element that, for some reason, needs a label/accessible name? In that case, you'd reach for aria-labelledby to create a relationship between two elements:
<div id="userName">User Name:</div>
<span aria-labelledby="userName"></span>
In this example, you have a div acting as the label, which is then linked to the span using aria-labelledby. Assuming that your visuals make sense, this span now has a visible label and an accessible name. But just because you can doesn't mean you should. What's the purpose of that span and why can't it be a standard input field? Always start with the standard HTML elements and only add in ARIA if absolutely necessary.
Just because you can doesn't mean you should.
Why?
I was always a bit scared of ARIA when I first started out with accessibility. It seemed like this huge, looming monster just over my shoulder and I wasn't sure if I should be using it, or if I was using it properly and actually making my sites more accessible or making them worse instead.
Now that I'm more comfortable with ARIA, it (thankfully) doesn't seem that bad, and while yes, it can be complex and somewhat opaque, the rules are straightforward and I have more confidence in what I'm seeing and doing. I hope you've gained a similar level of comfort from this article! But to wrap things up, here's one more interesting fact related to ARIA.
There's an organization called Web Accessibility in Mind (WebAIM) that does an annual automated accessibility evaluation of the top one million most visited websites. In February 2024, they found that 75% of the top one million most visited sites were using ARIA in some way (excluding ARIA landmarks). This number has grown in leaps and bounds over the last five years of the survey, starting at 60% in 2019 and increasing nearly every year since.
The really interesting bit, though, is that the pages in the survey with ARIA had, on average, 34.2% more errors than pages without ARIA. Again, if a page was using ARIA in some way, users experienced 15 additional errors compared to a similar page without ARIA.
Of course, correlation is not causation. A likely explanation is that pages with ARIA are probably more complex, which is why they were using ARIA in the first place, which also makes errors more likely. But ARIA is a standard for making rich internet applications accessible, so it seems to me like there's a pretty large disconnect between the intent and implementation of ARIA in the real world.
A project called Higher Ed in 4K did an accessibility analysis of up to 100 pages from the website of each college and university in the United States in 2019, with a follow up in 2020. One school in that survey had an average of 5,552 ARIA elements per page, with over 500,000 ARIA elements over the 96 pages that were sampled. Another school had more ARIA attributes on their pages than actual elements! As the report itself states, “this is a reminder that ARIA doesn't improve accessibility unless done correctly.”
It's almost impressive, in a scary kind of way. How did this even happen? Turns out it was a lot of tabindex, ARIA popups, and ARIA expanded, most of which you can safely assume weren't needed at all or could have been replaced with semantic HTML.
I'm Canadian, so I'm not very familiar with American colleges and universities, but based on my experience with Canadian versions of such sites, I cannot imagine that they were all such “rich internet applications” that they absolutely needed that much ARIA (especially when keeping in mind that these types of automated scans can only hit publicly available pages, so these tests wouldn't have had access to any student or instructor areas at all).
I wanted to highlight these studies because they're real-world examples of why we should always start from the position of not using ARIA. Most of the time, there will be a more simple, more semantic, more accessible way to code your site than trying to add in ARIA. And if there isn't, at least you've taken the time to really consider the necessity of ARIA, and probably won't end up with over five thousand duplicate and/or non-functional ARIA attributes on your website. Probably.
Validation
I haven't touched on it much, but you'll always want to test that you've used ARIA in an appropriate way, and there are a number of tools available to help you do so.
The ARIA Validator by Rick Brown is a Chrome-only extension that will validate the ARIA you've used on your site against the spec and let you know about any issues. As with all automated accessibility checking and validation, this tool cannot find everything and the best way to ensure that you've written an accessible website, app, or game is to test it with a variety of disabled users.
The Visual ARIA bookmarklet from Apex 4K is a similar tool to the ARIA Validator above, allowing you to check the ARIA used against the spec, but it can be easy to install because it's a simple JavaScript bookmarklet instead of an extension. Like with other types of accessibility checking, it's good to use more than one tool because they usually have their own strengths and weaknesses.
Landmarks is an extension available for Firefox, Chrome, Opera, and Edge that focuses on navigation via landmarks. This can be a great way to do an initial check of the landmarks on your page and see if they're improving navigation or making it worse.
I'm a big fan of an article called WAI-ARIA and its true impact on assistive technologies (https://tech.finn.no/2017/06/07/wai-aria-and-its-true-impact-on-assistive-technologies/) by Tor-Martin Storsletten. It's an older article but it contains an extensive, in-depth review of ARIA, how it's used, how it interacts with assistive technology like screen readers, and where it falls short.
Of course, I couldn't neglect the article where Sara Soueidan coined the phrase “ARIA is a polyfill for HTML semantics”, What a Year of Learning and Teaching Accessibility Taught Me (https://www.24a11y.com/2019/what-a-year-of-learning-and-teaching-accessibility-taught-me/). The site it's published on, 24 Accessibility, has many great accessibility resources, including a very thorough and well-researched one on why there's really no replacing the standard select element (https://www.24a11y.com/2019/select-your-poison/).
Use ARIA in your applications, but use it wisely, with consideration and planning. Accessibility is a broad and tricky topic, and you're going to make mistakes. At the very least, try to make informed mistakes, and of course, do better once you know better.