// guide
ARIA: When to Use It, When to Avoid It, and How to Get It Right
ARIA (Accessible Rich Internet Applications) is one of the most misused tools in web development. Used well, it makes complex widgets accessible. Used poorly, it makes things worse than no ARIA at all. This guide covers when ARIA is the right choice, when native HTML is better, and how to use ARIA attributes correctly.
// 01 · what is aria?
What Is ARIA?
ARIA stands for Accessible Rich Internet Applications. It is a set of HTML attributes that supplement native HTML to communicate widget roles, states, and properties to assistive technology. ARIA was created by the W3C to bridge the gap between complex web interfaces and the accessibility information that screen readers and other tools need to describe them.
ARIA does not add behavior. It only adds semantics. Adding role="button" to a <div> tells screen readers that the element is a button, but the element still won't be focusable or respond to keyboard events. You still need to add tabindex="0" and handle keydown events for Enter and Space yourself. This is why native HTML is almost always the better choice — a <button> element gives you all of that for free.
ARIA attributes fall into three categories:
- Roles define what an element is:
role="tablist",role="switch",role="dialog" - States describe dynamic conditions:
aria-expanded="true",aria-checked="false",aria-selected="true" - Properties provide additional information:
aria-labelledby="id",aria-describedby="id",aria-haspopup="true"
<button> already has the button role, keyboard support, and click handling built in. ARIA is a last resort for when HTML alone can't express the semantics you need.
// 02 · the five rules of aria
The Five Rules of ARIA
The W3C's "Using ARIA" document defines five rules that every developer should follow. These rules exist because ARIA misuse is one of the most common sources of accessibility failures on the web.
<button> is always better than <div role="button" tabindex="0"> because the native element gives you keyboard support, focus management, and form submission for free. Similarly, use <select> before building a custom listbox, <details>/<summary> before an ARIA disclosure, and <dialog> before role="dialog".
role="heading" on a <button>. Don't put role="button" on a heading. If you need a heading that looks like a button, style a heading. If you need a button that looks like a heading, style a button. Overriding native semantics confuses assistive technology users because the element behaves one way but is announced differently.
role="switch", it must respond to Space. If you use role="tab", it must respond to arrow keys. ARIA only provides the semantic label — you must implement all the keyboard interaction yourself. See the Switch, Tabs, and Dropdown Menu patterns for examples.
role="presentation" or aria-hidden="true" on focusable elements
If an element can receive keyboard focus, it must not be hidden from assistive technology. Adding aria-hidden="true" to a link or button means screen reader users can tab to it but have no idea what it is. Either remove focus capability or remove the ARIA hiding — never combine them.
<label>, aria-label, or aria-labelledby. An icon-only button without a label is announced as just "button" — unusable for screen reader users. See the Accessible Forms pattern for proper labeling techniques.
// 03 · decision tree: aria vs native html
Decision Tree: ARIA vs Native HTML
Before reaching for ARIA, walk through this decision process:
<button>, <select>, <details>/<summary>, <dialog>, <input>, <a>, <nav>, <table>. If yes, use the native element.
<button> exists for interactive controls but doesn't convey "expanded" or "collapsed" state on its own. If the native element has the right semantics as-is, use it without ARIA.
<button> with aria-expanded="false" for a disclosure widget. A <table> with aria-sort="ascending" on a column header. A <form> input with aria-invalid="true" and aria-describedby for error messages. If this solves the problem, stop here.
The table below shows how A11yPath patterns fall across this spectrum:
| Category | Patterns | ARIA Used |
|---|---|---|
| Native HTML only | Skip Links, Breadcrumb, Accordion | None or minimal (aria-current) |
| Native + enhancement | Modal, Forms, Data Table, Responsive Nav | aria-expanded, aria-sort, aria-invalid |
| ARIA required | Tabs, Switch, Dropdown Menu, Combobox, Toast, Tooltip | role, aria-selected, aria-checked, etc. |
// 04 · aria roles you will actually use
ARIA Roles You Will Actually Use
ARIA defines dozens of roles, but in practice you'll use a small subset. Here are the roles that come up most often in interactive web applications, along with the A11yPath pattern that demonstrates each one.
| Role | When to Use | A11yPath Pattern |
|---|---|---|
tablist / tab / tabpanel |
Tab interfaces where selecting a tab shows its associated panel | Tabs |
menu / menuitem |
Application-style action menus (not site navigation) | Dropdown Menu |
combobox / listbox / option |
Autocomplete inputs and filterable select components | Combobox |
switch |
Binary on/off toggle controls | Switch |
status |
Non-urgent live updates that don't interrupt the user | Toast |
alert |
Urgent announcements that need immediate attention | Toast |
dialog |
Modal dialogs that trap focus and require user action | Modal |
tooltip |
Supplementary information shown on hover or focus | Tooltip |
menu role is not for navigation
The menu and menuitem roles are for application-style action menus — things like context menus, file menus, and action dropdowns. Site navigation uses <nav> with links. Putting role="menu" on your site's navigation bar will confuse screen reader users because they expect menu keyboard behavior (arrow keys, type-ahead) rather than link behavior. See the Responsive Navigation pattern for the correct approach.
// 05 · aria states and properties
ARIA States and Properties
States and properties are ARIA attributes that provide dynamic information about an element. States change frequently in response to user interaction (expanded, selected, checked). Properties tend to be more static (labelledby, describedby, haspopup). Here are the ones you'll use most often.
| Attribute | Purpose | Values | Pattern Example |
|---|---|---|---|
aria-expanded |
Shows whether a collapsible section is open or closed | true / false |
Dropdown Menu, Accordion, Responsive Nav |
aria-checked |
Indicates the on/off state of a toggle or switch | true / false |
Switch |
aria-selected |
Marks the currently selected item in a group | true / false |
Tabs, Combobox |
aria-activedescendant |
Manages virtual focus within a composite widget | ID reference | Combobox |
aria-invalid |
Indicates a form field has a validation error | true / false |
Forms |
aria-describedby |
Links an element to its descriptive text | ID reference | Tooltip, Forms |
aria-labelledby |
Provides an accessible name via a referenced element | ID reference | Modal, Forms |
aria-live |
Creates a live region that announces dynamic content | polite / assertive / off |
Toast |
aria-current |
Identifies the current item in a set or navigation | page / step / true |
Breadcrumb |
aria-sort |
Indicates the sort direction of a table column | ascending / descending / none |
Data Table |
aria-haspopup |
Tells assistive technology a button triggers a popup | true / menu / dialog |
Dropdown Menu |
// 06 · common aria mistakes
Common ARIA Mistakes
ARIA misuse is worse than no ARIA at all. The WebAIM Million report consistently finds that pages with more ARIA have more accessibility errors, not fewer. Here are the mistakes to avoid.
<div role="button" tabindex="0">Save</div> instead of <button>Save</button>. The div version requires you to manually add keyboard handling for Enter and Space, focus styles, form submission behavior, and disabled state management. The native button gives you all of this for free. The same applies to <div role="checkbox"> instead of <input type="checkbox">, and <span role="link"> instead of <a href>.
role="switch" on an element but not implementing Space key toggling. Using role="tab" without arrow key navigation. Using role="menu" without arrow key, Home, End, and type-ahead support. ARIA tells the screen reader what keyboard behavior to expect — if that behavior is missing, the experience is worse than having no role at all. See the Switch and Tabs patterns for correct keyboard implementations.
role="button" to a <button>, role="navigation" to a <nav>, or role="heading" to an <h2>. These elements already have implicit roles. The redundant ARIA doesn't cause errors in most screen readers, but it signals a misunderstanding of how ARIA works and clutters the codebase with unnecessary attributes.
aria-label on non-interactive elements
Adding aria-label to a <div>, <span>, or <p>. Most screen readers ignore aria-label on elements without a role or landmark. It works on <button>, <a>, <input>, <nav>, and elements with explicit ARIA roles, but not on generic containers. If you need to label a section, use a heading or aria-labelledby on a landmark region.
aria-live usage
Wrapping entire page sections in aria-live="polite" regions, which causes the screen reader to re-announce all content whenever anything inside changes. Or using aria-live="assertive" for non-urgent updates like "Item added to cart," which interrupts whatever the user is currently doing. Use polite for most notifications and reserve assertive for genuine errors or time-sensitive alerts. See the Toast pattern for correct live region implementation.
aria-hidden
Assuming that aria-hidden="false" is the same as removing aria-hidden. In practice, setting aria-hidden="false" on an element whose parent has aria-hidden="true" does not make the child visible to assistive technology — the parent's hiding cascades. The safest approach is to completely remove the aria-hidden attribute when you want an element to be visible to screen readers, rather than toggling between "true" and "false".
// 07 · next steps
Next Steps
The first rule of ARIA is to avoid it when a native HTML element will do — so start from the semantic HTML guide and reach for ARIA only to fill genuine gaps. Once you've added ARIA to a component, verify the result against the Developer Accessibility Checklist and test it with a real screen reader, since ARIA changes what assistive technology announces — and wrong ARIA is worse than none.