ARIA — When to Use It, and When Not To
AdvancedARIA changes what assistive technology reports without changing behaviour. That asymmetry is why incorrect ARIA is worse than none — it makes a promise the code does not keep.
Overview
ARIA adds roles, states and properties to the accessibility tree. What it does not add is behaviour: role="button" on a div does not make it focusable or make Enter activate it, it only makes a screen reader announce "button" for something that then does nothing. That gap is the whole risk, and it is why the first rule of ARIA is not to use ARIA. Where it earns its place is the small set of things HTML cannot express: live regions, expanded and selected states, and relationships between elements that are not parent and child.
The Rules
Five rules, in the order they matter.
/* 1. Use a native element if one exists.
<button> over <div role="button"> — every time. */
/* 2. Do not change native semantics without a very good reason. */
<h2 role="button"> // now it is not a heading in the outline
<button role="heading"> // now it is not in the buttons list
/* 3. Every interactive ARIA control must be keyboard operable.
A role is a promise; the key handler is keeping it. */
/* 4. Do not put aria-hidden on a focusable element — it creates a
control that can be reached but not perceived. */
/* 5. Every interactive element needs an accessible name. */
<button aria-label="Close dialog"><svg aria-hidden="true"/></button>
/* The measurable consequence: surveys of the top million sites
consistently find pages WITH ARIA average more detected errors
than pages without, because it is applied incorrectly. Reach for
it deliberately, not decoratively. */
/* If you are implementing a combobox, tree or complex menu, follow
the ARIA Authoring Practices Guide exactly — or use a headless
library that already has. These patterns have a lot of required
keyboard behaviour that is easy to get subtly wrong. */States and Relationships
The attributes that genuinely earn their place.
/* State — must be kept in sync with reality, in JavaScript */
<button aria-expanded="false" aria-controls="menu">Options</button>
<button aria-pressed="true">Bookmark</button> /* a toggle */
<div role="tab" aria-selected="true">
<input aria-invalid="true" aria-describedby="err">
<button aria-busy="true">Saving…</button>
<a aria-current="page" href="/problems">Problems</a> /* nav state */
/* A stale aria-expanded is worse than none: the user is told the
menu is closed while it is open. */
/* Naming and description */
aria-label="Search" /* replaces the visible name */
aria-labelledby="heading-id" /* points at existing text —
preferred, since it stays
translated and in sync */
aria-describedby="hint-id error-id" /* extra detail, multiple ids */
/* Bonus: these attributes double as CSS hooks, which keeps the
styling and the semantics from drifting apart: */
[aria-expanded="true"] .chevron { transform: rotate(180deg) }
[aria-current="page"] { font-weight: 600 }
[aria-invalid="true"] { border-color: var(--danger) }Live Regions
Announcing something that changed without moving focus.
/* A sighted user sees a toast appear. Without a live region, a
screen-reader user is told nothing at all. */
<div role="status" aria-live="polite">{message}</div>
/* polite: announced when the user pauses. For confirmations,
result counts, autosave notices. */
<div role="alert">{error}</div>
/* = aria-live="assertive": interrupts immediately.
For errors and anything time-critical only. Overuse makes a page
unusable — every interruption cuts off what was being read. */
/* The container must exist in the DOM BEFORE the text changes.
Inserting a whole element with the attribute already on it is
frequently not announced: */
{error && <div role="alert">{error}</div>} // unreliable
<div role="alert">{error}</div> // reliable — always
// present, text changes
/* Common uses:
"12 results found" after a search
"Saved" after an autosave
"Copied to clipboard" after a copy button
form validation summaries */
/* aria-live="off" is the default for everything else — do not make
ordinary content chatty. */Key Points to Remember
- 1ARIA changes what is announced but never adds behaviour — a role without keyboard support is a broken promise
- 2The first rule of ARIA is to use a native element instead; pages with ARIA average more errors than pages without
- 3aria-expanded, aria-current, aria-pressed and aria-describedby must be kept in sync, and a stale state is worse than none
- 4ARIA attributes double as CSS selectors, which keeps styling and semantics from drifting apart
- 5A live region must already exist in the DOM before its text changes; role="alert" interrupts and should be reserved for errors
Interview Questions
Sign in to ask AriaWhy is incorrect ARIA worse than no ARIA at all?
What is the difference between aria-live="polite" and role="alert"?
Why does inserting an element that already has role="alert" often fail to announce?
Ask Aria about ARIA — When to Use It, and When Not To
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.