development
What Is ARIA? Roles, Landmarks, and Live Regions
ARIA lets developers make dynamic web content accessible to screen readers. Learn how roles, landmarks, and live regions work — and when not to use them.
What ARIA is — and what it is not
ARIA stands for Accessible Rich Internet Applications. It is a set of attributes defined by the W3C’s Web Accessibility Initiative (WAI) that lets developers communicate the meaning and state of user interface elements to assistive technologies like screen readers.
The most important rule about ARIA is also the one most often ignored: do not use ARIA when native HTML can do the job. A <button> element already announces itself as a button and responds to keyboard events. A <nav> element already communicates landmark navigation to screen readers. Adding role="button" to a <div> and then scripting its behavior is harder to maintain, easier to break, and usually worse for accessibility than using the right element in the first place.
ARIA does not:
- Make content visible or interactive — it only changes what assistive technology announces
- Fix broken keyboard access — you still need
tabindexand event listeners - Substitute for well-structured semantic HTML
Where ARIA genuinely helps is when native HTML has no element for what you are building: a date picker, a live notification banner, a custom combobox, a tree view. In these cases, ARIA lets you communicate the semantics that HTML cannot.
ARIA roles
A role tells assistive technology what kind of element it is dealing with. Every interactive element has an implicit role derived from its HTML tag. The <a> element has the link role. The <input type="checkbox"> element has the checkbox role. The <h2> element has the heading role.
When you build a custom element that has no HTML equivalent, you assign it an explicit role:
<!-- A custom toggle built from a div -->
<div
role="switch"
aria-checked="false"
tabindex="0"
>
Dark mode
</div>
The screen reader will now announce this as a switch control and speak its checked state. Without the role, it would be announced as plain text and the user would have no idea it is interactive.
Common roles and when to use them
Widget roles describe interactive controls:
| Role | Use when |
|---|---|
button | A custom clickable element with no better HTML tag |
checkbox | Custom multi-select toggle |
combobox | A text input combined with a listbox dropdown |
dialog | A modal overlay (only when not using the native <dialog> element) |
listbox | A custom dropdown list |
slider | A custom range control |
switch | An on/off toggle |
tab, tablist, tabpanel | A tabbed interface |
tooltip | A short description shown on hover or focus |
Document structure roles describe non-interactive content:
article— a self-contained piece of contentfigure— an image with a captionlist,listitem— when list semantics are needed on non-list elementspresentation/none— removes an element’s implicit role (use rarely and carefully)
A critical mistake: adding roles without behavior
Each role carries a contract that screen readers and keyboard users expect to be honored. A role="button" must respond to both Enter and Space. A role="checkbox" must toggle on Space. A role="link" must navigate on Enter.
If you add the role but not the corresponding behavior, you actively mislead users with assistive technology. They hear a control announced, try to interact with it using the expected keyboard shortcut, and nothing happens. This is worse than no ARIA at all.
ARIA landmarks
Landmarks are the page’s navigational skeleton. They let screen reader users jump between major regions without reading everything in between — the equivalent of a sighted user’s eye scanning the page layout at a glance.
HTML5 introduced semantic elements that map directly to landmark roles. Use them and the landmarks come for free:
| HTML element | Landmark role | Purpose |
|---|---|---|
<header> | banner | Site-wide header (only when it is the top-level header, not inside <article>) |
<nav> | navigation | A navigation menu |
<main> | main | The primary page content |
<aside> | complementary | Secondary content related to the main content |
<footer> | contentinfo | Site-wide footer |
<form> | form | A form (only when it has an accessible name) |
<section> | region | A named section (only when it has an accessible name via aria-label or aria-labelledby) |
You do not need to add role="main" to a <main> element — it is redundant. ARIA landmark roles are needed only when you cannot use the semantic HTML element, for example in a legacy codebase generating <div class="sidebar">:
<div class="sidebar" role="complementary" aria-label="Related articles">
<!-- sidebar content -->
</div>
Labeling landmarks when you have more than one
When a page has multiple instances of the same landmark — two <nav> elements, two <section> elements with role="region" — each must have a unique accessible name so users can distinguish them:
<nav aria-label="Main navigation">...</nav>
<nav aria-label="Footer navigation">...</nav>
Without labels, a screen reader announces both simply as “navigation.” With labels, users hear “Main navigation, navigation landmark” and “Footer navigation, navigation landmark” and can choose the right one from a landmark list.
ARIA live regions
A live region is an area of the page whose content updates dynamically, and where those updates should be automatically announced to screen reader users without requiring them to move focus.
The core attribute is aria-live. It accepts three values:
off— updates are not announced (the default for all elements)polite— updates are announced after the user finishes their current taskassertive— updates interrupt whatever the screen reader is saying immediately
<!-- A status message area populated after a form submission -->
<div aria-live="polite" id="status-message"></div>
<script>
document.getElementById('status-message').textContent =
'Your message has been sent.';
</script>
When the text content changes, a screen reader with aria-live="polite" waits for a pause in speech and then announces the new content. Use polite for the vast majority of dynamic updates. Reserve assertive only for critical failures — a payment error, a session timeout warning — where the information is urgent enough to justify interrupting the user.
Role shortcuts for live regions
Two roles bundle aria-live semantics into a single attribute:
role="status"— equivalent toaria-live="polite". Use for success messages, loading states, and non-urgent updates.role="alert"— equivalent toaria-live="assertive"and also impliesaria-atomic="true". Use for error messages and critical failures.
<!-- Error announced immediately, interrupting current speech -->
<div role="alert" id="payment-error"></div>
<!-- Status update announced politely after current speech -->
<div role="status" id="cart-count">3 items in cart</div>
Common live region mistakes
Adding content before the element is in the DOM. The browser registers the live region when the element is first parsed. If you inject the element and set its text content simultaneously, some screen readers miss the announcement entirely. Always include the live region container in the initial HTML and update its content via JavaScript later.
Using assertive for everything. Assertive live regions interrupt whatever the user is doing, including other announcements. A search results count updating as the user types does not warrant role="alert". Overuse of assertive produces a hostile experience for screen reader users.
Updating the live region too frequently. If a progress indicator updates its live region every 100 milliseconds, the speech queue overflows and users hear nothing useful. Update live text only at meaningful milestones — 25%, 50%, 75%, complete — or debounce updates with a timer.
Forgetting aria-atomic. By default, only the changed text node inside a live region is announced. If you want the entire region re-read (not just the changed fragment), add aria-atomic="true":
<div aria-live="polite" aria-atomic="true">
<span id="count">2</span> items remaining
</div>
Without aria-atomic, a screen reader may announce only “2” when the count changes from 3 to 2. With it, the full phrase “2 items remaining” is spoken, which is almost always what you want.
Other essential ARIA attributes
aria-label — provides an accessible name when no visible text is appropriate:
<button aria-label="Close dialog">✕</button>
aria-labelledby — points to another element whose text serves as the accessible name. Preferred over aria-label when the label text is already visible on the page:
<h2 id="billing-heading">Billing address</h2>
<form aria-labelledby="billing-heading">...</form>
aria-describedby — points to supplementary text that describes an element beyond its name:
<input type="password" aria-describedby="pwd-hint">
<p id="pwd-hint">Must be at least 12 characters and include one symbol.</p>
aria-expanded — indicates whether a collapsible element (dropdown, accordion, menu) is open or closed. Update it in JavaScript whenever the state changes:
<button aria-expanded="false" aria-controls="nav-menu">Menu</button>
<ul id="nav-menu" hidden>...</ul>
aria-hidden="true" — removes an element from the accessibility tree. Use for decorative icons, duplicate text, or visual elements that would add noise for screen reader users:
<span aria-hidden="true">★★★★☆</span>
<span class="sr-only">4 out of 5 stars</span>
aria-disabled="true" — marks a control as disabled without removing it from the focus order. Useful when you want users to discover the control exists and understand why it is unavailable, rather than having it skipped silently:
<button aria-disabled="true">Submit (complete all fields first)</button>
Testing ARIA in practice
Writing ARIA attributes is straightforward. Getting them right requires testing. Automated scanners catch clearly broken patterns — a role="button" with no accessible name, an aria-labelledby that references a non-existent ID, a live region with an invalid aria-live value. They cannot tell you whether the announced text makes sense in context, or whether a complex custom widget behaves correctly when navigated by keyboard alone.
For real-world testing, combine at least two screen reader and browser pairings:
- NVDA + Chrome on Windows — free, widely used, close to the real-world screen reader population
- VoiceOver + Safari on macOS or iOS — built in, essential for mobile accessibility testing
- JAWS + Chrome or Edge on Windows — paid, but the most widely used screen reader in enterprise environments
Navigate your interactive components using only the keyboard. Listen carefully to what is announced when you open a menu, submit a form with a validation error, or trigger a live region update. If the announcement is ambiguous or misleading, the ARIA implementation is wrong — regardless of what any automated tool reports.
The rule that covers everything
ARIA’s specification includes five authoring rules. The first is the most important:
If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.
Build with semantic HTML. Reach for ARIA only when HTML runs out. Test with a real screen reader. That covers nearly every situation you will encounter.
For a practical check of how ARIA attributes are implemented across your site, our manual accessibility audits include screen reader testing by people who use assistive technology every day and can spot subtle ARIA issues that automated tools miss.
See how your site uses ARIA with a free scan