ARIA Roles and Properties Reference
A practical reference for WAI-ARIA roles, states, and properties — when to use ARIA, which roles require keyboard patterns, and the five rules of ARIA.
The Five Rules of ARIA
The W3C's "Using ARIA" document defines five rules that should guide all ARIA usage. Violating these rules is the most common source of ARIA-caused accessibility regressions:
- If you can use a native HTML element or attribute with the semantics and behavior you require, use it instead of repurposing an element and adding ARIA. Native HTML is always preferred.
- Do not change native semantics unless you really have to. Do not add role="heading" to a <p> when you should use an <h2>.
- All interactive ARIA controls must be usable with the keyboard. If you add role="button" to a div, you must also add tabindex="0" and keyboard event handlers for Enter and Space.
- Do not use role="presentation" or aria-hidden="true" on a focusable element.
- All interactive elements must have an accessible name — either from their content, a label, aria-labelledby, or aria-label.
When to Use ARIA
ARIA should only be used when native HTML cannot provide the required semantics. The most legitimate use cases are:
- Custom interactive widgets that have no HTML equivalent — tabs, accordions, tree views, data grids, sliders, carousels.
- Landmark roles on elements that cannot use the native landmark elements — e.g., role="banner" on a <div> when you cannot change the HTML structure.
- Live regions — aria-live, aria-atomic, aria-relevant — for announcing dynamic content updates to screen readers.
- Relationships — aria-describedby, aria-controls, aria-owns — for associating related elements when the relationship cannot be expressed structurally.
- State disclosure — aria-expanded, aria-selected, aria-checked, aria-pressed — for communicating the current state of interactive components.
Core ARIA Roles Reference
Key roles and their usage:
<!-- role="button" on non-button element -->
<div
role="button"
tabindex="0"
aria-pressed="false"
onclick="toggleLike(this)"
onkeydown="handleButtonKey(event, this)"
>
Like
</div>
<!-- Note: prefer <button> unless impossible -->
<!-- role="tab" / "tablist" / "tabpanel" -->
<div role="tablist" aria-label="Guide categories">
<button role="tab" aria-selected="true" aria-controls="panel-seo" id="tab-seo">SEO</button>
<button role="tab" aria-selected="false" aria-controls="panel-geo" id="tab-geo">GEO</button>
</div>
<div role="tabpanel" id="panel-seo" aria-labelledby="tab-seo">SEO content</div>
<div role="tabpanel" id="panel-geo" aria-labelledby="tab-geo" hidden>GEO content</div>
<!-- role="dialog" -->
<div
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
aria-describedby="dialog-desc"
>
<h2 id="dialog-title">Confirm deletion</h2>
<p id="dialog-desc">This action cannot be undone.</p>
<!-- interactive controls -->
</div>
<!-- role="alert" for important dynamic messages -->
<div role="alert">Your form was submitted successfully.</div>
<!-- role="status" for non-urgent updates -->
<div role="status" aria-live="polite">Loading results...</div>
ARIA States and Properties
States (dynamic, change over time) and properties (relatively static) are the second dimension of ARIA:
<!-- aria-expanded: open/closed state of disclosures -->
<button aria-expanded="false" aria-controls="menu-1">Menu</button>
<ul id="menu-1" hidden>...</ul>
<!-- aria-selected: selection state in listboxes, tabs, tree items -->
<li role="option" aria-selected="true">WCAG 2.2</li>
<!-- aria-checked: for custom checkboxes and radio buttons -->
<div role="checkbox" aria-checked="false" tabindex="0">Include screenshots</div>
<!-- aria-disabled: for disabled elements (does not remove from tab order like HTML disabled) -->
<button aria-disabled="true">Submit (fill all fields first)</button>
<!-- aria-required: marks required form fields -->
<input type="text" aria-required="true" aria-describedby="name-hint">
<span id="name-hint">Full legal name, required</span>
<!-- aria-invalid: marks fields with errors -->
<input type="email" aria-invalid="true" aria-describedby="email-error">
<span id="email-error" role="alert">Please enter a valid email address.</span>
Live Regions
Live regions announce dynamic content changes to screen reader users without requiring focus movement. Use them for search results, form validation messages, loading indicators, and notification toasts.
<!-- aria-live="polite": announces after current speech finishes -->
<div aria-live="polite" aria-atomic="true">
<!-- Inject content here dynamically: "5 results found" -->
</div>
<!-- aria-live="assertive": announces immediately, interrupts current speech -->
<!-- Use ONLY for critical errors or time-sensitive alerts -->
<div role="alert" aria-live="assertive">
<!-- Inject: "Session expires in 2 minutes" -->
</div>
<!-- Common mistake: injecting aria-live AFTER the element is in the DOM -->
<!-- The element must be in the DOM BEFORE content is injected -->
<div aria-live="polite" class="sr-announcement"></div>
Accessible Names: The Hierarchy
Every interactive element needs an accessible name. The browser computes the accessible name using the Accessible Name and Description Computation (ANDC) algorithm, in this priority order:
- aria-labelledby — references another element's text. Highest priority.
- aria-label — provides a string directly. Overrides visible text.
- HTML label element — for form inputs, the associated <label> text.
- title attribute — tooltip-style label. Least recommended for primary labeling.
- Text content — for buttons and links, the text inside the element.
- alt attribute — for images, the alt text.
Resources
- W3C WAI-ARIA 1.2 Specification— W3C WAI
- W3C ARIA Authoring Practices Guide— W3C WAI
- MDN: ARIA reference— MDN
- Deque University: ARIA— Deque University
- W3C: Using ARIA (Five Rules)— W3C WAI