Using ARIA Correctly: Roles, States, Live Regions
WAI-ARIA (Web Accessibility Initiative - Accessible Rich Internet Applications) is a powerful technique for making interactive components accessible to assistive technologies. At the same time, it is one of the most frequently misused techniques on the web. The first rule of the ARIA Authoring Practices essentially states: no ARIA is better than bad ARIA (W3C WAI-ARIA Authoring Practices Guide, 2024). The WebAIM Million Report (2024) finds that homepages using ARIA have on average more detectable errors than those without, because ARIA is often added without the necessary understanding. This article shows how to use roles, states and live regions correctly, which WCAG requirements they relate to and how a professional audit uncovers broken ARIA.
What ARIA Is and What It Is Not
ARIA is a set of HTML attributes that provide assistive technologies with additional information about the role, state and properties of an element. This information feeds into the browser's accessibility tree, a representation of the page independent of visual presentation that screen readers read out. ARIA only changes how elements are described to assistive technologies. It adds no functionality: no focus behavior, no keyboard operation, no click handling.
This is exactly where the most common misunderstanding lies. Writing a brings all of that automatically. The W3C documentation puts it plainly: ARIA describes, HTML implements (W3C WAI, ARIA in HTML, 2024).
ARIA falls into three categories: roles define what an element is, such as a tab, a dialog or a navigation region. States describe the current, changeable condition, such as whether an accordion is open or closed. Properties describe more static characteristics, such as a label or a relationship between elements. The boundary between state and property is fluid in practice, both are set via aria-* attributes.
The Five Core Rules of Using ARIA
The W3C has defined five rules for using ARIA (W3C, Using ARIA, 2024). They are the benchmark against which we measure the ARIA implementation in every WCAG audit. Internalizing these rules avoids the overwhelming majority of all ARIA errors. The order is deliberate: the first rule is the most important and resolves many problems at the root.
- Use a native HTML element with the desired semantics and behavior instead of repurposing a generic element with ARIA. A
is always preferable to a.- Do not change the native semantics of an element unless necessary. An
hides from screen readers that this is a heading and is usually wrong.- All interactive ARIA controls must be keyboard operable. An element with
role="button"needstabindexand keyboard handlers for enter and space.- Do not hide focusable elements with
aria-hidden="true". A focusable element hidden from screen readers creates a dead focus stop.- All interactive elements need an accessible name, via visible text,
aria-labeloraria-labelledby. - Do not change the native semantics of an element unless necessary. An
No ARIA is better than bad ARIA
Roles vs. Native HTML Semantics
Most ARIA roles have a native HTML equivalent that makes them unnecessary. The following comparison shows for typical use cases why the native variant is almost always the better choice. The underlying principle: native HTML brings role, focus behavior and keyboard operation in a single element that is tested across all browsers and assistive technologies.
| Use case | Native HTML | ARIA variant (avoid) |
|---|---|---|
| Button | <button> - focusable, enter/space, role=button automatic | <div role="button" tabindex="0"> - rebuild keyboard in JS |
| Link | <a href> - role=link, focusable, in history | <span role="link"> - no href, no keyboard operation |
| Navigation | <nav> - creates landmark navigation automatically | <div role="navigation"> - correct but unnecessary |
| Checkbox | <input type="checkbox"> - state and operation native | <div role="checkbox" aria-checked> - everything in JS |
| Heading | <h2> - role=heading with aria-level=2 | <div role="heading" aria-level="2"> - error prone |
However, there are components for which HTML offers no native equivalent. Tabs, a tree view, a slider with two handles, a tab panel system or a combobox pattern have no dedicated HTML element. Here ARIA is indispensable. The ARIA Authoring Practices Guide (W3C, 2024) documents the required roles, states and keyboard interactions for each of these patterns. Anyone building such a pattern should follow these specifications exactly rather than inventing their own concept.
<!-- Tabs: ARIA is correct here because HTML has no tab element -->
<div role="tablist" aria-label="Account settings">
<button role="tab" id="tab-1" aria-selected="true"
aria-controls="panel-1">Profile</button>
<button role="tab" id="tab-2" aria-selected="false"
aria-controls="panel-2" tabindex="-1">Security</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">...</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>...</div>States and Properties: aria-expanded, aria-selected and More
States tell the screen reader the current changeable condition of a control. They are dynamic and updated via JavaScript whenever the component changes. The most common mistake is not a wrongly chosen attribute, but a state that is not updated after an interaction, so the screen reader reads out stale information.
aria-expanded
For collapsible elements like accordions, menus and disclosure buttons. Value true or false, must be updated on expand and collapse.
aria-selected
For selected options in tabs, listboxes and grids. Exactly one element per group carries true, the rest false.
aria-checked
For checkboxes, radios and switches in ARIA form. Values true, false or mixed (indeterminate tri-state).
aria-disabled
Marks an element as inactive without removing it from focus. Unlike native disabled, it stays focusable and readable.
aria-current
Marks the current element in a set, such as the active page in navigation (aria-current=page) or the current step.
aria-label / labelledby
Provide the accessible name. aria-labelledby references visible text by ID, aria-label sets an invisible name.
An accordion illustrates the principle. The triggering button gets aria-expanded, reflecting the open state, and aria-controls, pointing to the controlled panel. On every click, aria-expanded must toggle between true and false. If the implementation forgets this update, the screen reader permanently announces collapsed even though the content is visible. This is one of the most common findings we correct on existing components during accessible web development.
<!-- Accordion: aria-expanded MUST be updated on every click -->
<h3>
<button aria-expanded="false" aria-controls="sect-1" id="btn-1">
Shipping costs
</button>
</h3>
<div id="sect-1" role="region" aria-labelledby="btn-1" hidden>
<p>Within Germany we ship free from 50 euros.</p>
</div>
<script>
// JavaScript toggles state AND hidden in sync
const btn = document.getElementById('btn-1');
btn.addEventListener('click', () => {
const open = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', String(!open));
document.getElementById('sect-1').hidden = open;
});
</script>Wire up relationship attributes correctly
Live Regions: Announcing Dynamic Content
Modern interfaces constantly update content without a full page reload: a filter reduces the result count, form validation shows an error, a cart counter changes, a toast notification appears. Sighted users notice these changes visually. Screen reader users only catch them if the change sits inside a live region. Live regions instruct the screen reader to automatically read out changes in a specific DOM area without focus having to move there.
aria-live="polite" is the default for nearly all updates. The screen reader waits until the current output finishes, then reads the change without interrupting the user. Suitable for status messages, updated result counts, success notices, save confirmations and search suggestions. aria-live="assertive" interrupts the ongoing output immediately and should be reserved exclusively for urgent messages, such as an error requiring immediate action or a session timeout. The Nielsen Norman Group (2023) notes that frequent assertive interruptions significantly disrupt usage and are quickly perceived as stressful.
Instead of the attributes, you can also use the roles role="status" (equivalent to polite) and role="alert" (equivalent to assertive). They bring the matching live properties automatically and are often semantically clearer. For most status messages, role="status" is the simplest and most robust choice because it needs no additional attributes and is reliably supported by common screen readers (W3C WAI-ARIA, 2024).
The live region must exist in the DOM beforehand
<!-- Region exists from the start, empty -->
<div id="status" role="status" aria-live="polite"></div>
<script>
// Later only change the text content -> gets announced
function announce(message) {
document.getElementById('status').textContent = message;
}
// Example: announce('3 results for your filter selection');
</script>
<!-- WRONG: create region only on demand -> no announcement
const div = document.createElement('div');
div.setAttribute('aria-live', 'polite');
div.textContent = 'Saved';
document.body.appendChild(div); -->Common ARIA Mistakes in Practice
The WebAIM Million Report (2024) shows a counterintuitive finding: pages that use ARIA have on average more detectable WCAG errors than pages without ARIA. The reason is not that ARIA does harm, but that it is often deployed without sufficient understanding. The following error patterns appear in almost every project and can be identified and fixed systematically.
- Redundant roles:
orrepeat what the element already is. At best redundant, at worst confusing. - State not updated:
aria-expandedstays at false even though the panel is open. The screen reader permanently reads out the wrong value. - aria-hidden on focusable elements: an element is reachable by keyboard but hidden from the screen reader. This creates a silent, confusing focus stop.
- Orphaned relationships:
aria-labelledbyoraria-controlsreference IDs that do not exist in the DOM or are assigned more than once. - Missing accessible name: an icon button without text and without
aria-labelis only announced as an unlabeled button. - Excessive assertive: marking every status message as assertive turns the page into a constant staccato of interruptions for screen reader users.
ARIA can override HTML semantics. A wrong role hides what an element really is. That is why the safest strategy is to use ARIA only where native HTML genuinely falls short.
Testing ARIA with Screen Readers
Automated testing tools and browser developer tools detect a portion of ARIA errors, such as invalid attribute values, orphaned IDs or forbidden role combinations. But they cannot judge whether a state is correct in substance or whether a live region announces the right thing at the right time. Studies show that automated testing typically can only check a portion of the WCAG success criteria at all (W3C/WAI). The rest is uncovered only by manual testing with real screen readers.
We test ARIA components with at least two screen readers, because their behavior differs. The screen readers available in Windows, macOS, iOS and Android together cover a large share of real usage. A tab system that works flawlessly in a Windows screen reader may require different keyboard operation in a macOS screen reader, and only direct testing reveals that.
A systematic ARIA test includes: traversing each component by keyboard, checking that every state is announced correctly, triggering every dynamic change and verifying the live region announcement, and reconciling against the keyboard patterns of the ARIA Authoring Practices Guide. This structured review is a fixed part of our WCAG audit and complements automated analysis with the dimension that machines cannot cover.
Closely related to this topic are accessible forms, where ARIA attributes like aria-describedby, aria-invalid and aria-required play a central role in error communication. How these attributes interact with validation is described in detail in our article on accessible forms and validation.