// 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.

intermediate reference

// 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"
The first rule of ARIA Don't use ARIA if you can use native HTML. A <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.

Rule 1: Use native HTML whenever possible If you can use a native HTML element or attribute with the semantics and behavior you need, use it instead of adding ARIA. A <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".
Rule 2: Do not change native semantics Don't put 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.
Rule 3: All interactive ARIA controls must be keyboard accessible If you give an element 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.
Rule 4: Do not use 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.
Rule 5: All interactive elements must have an accessible name Every interactive element must have a name that assistive technology can announce. Use visible text content, <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:

Step 1: Does a native HTML element exist for this? Before building a custom widget, check whether HTML already has what you need. Native elements to consider: <button>, <select>, <details>/<summary>, <dialog>, <input>, <a>, <nav>, <table>. If yes, use the native element.
Step 2: Does the native element have the semantics you need? Sometimes a native element exists but doesn't quite express your intent. For example, <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.
Step 3: Can you enhance native HTML with minimal ARIA? Often the best approach is to start with a native element and add one or two ARIA attributes. A <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.
Step 4: Is there no native equivalent? Some UI patterns have no native HTML element. Tabs, comboboxes, action menus, toggle switches, and tooltips all require ARIA roles and states because HTML has no built-in semantics for them. This is where ARIA is the right choice — and where the patterns on this site come in.

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
The 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.

Using ARIA when native HTML works Writing <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>.
Adding ARIA roles without keyboard support Using 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.
Redundant ARIA Adding 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.
Using 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.
Incorrect 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.
Boolean confusion with 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.