The first rule of ARIA is: don't use ARIA. Native HTML semantics are always preferable. But when you must build custom components — tabs, comboboxes, tree views, sliders — ARIA is the mechanism for exposing their semantics to assistive technologies. Used correctly, ARIA enables complex interactive patterns. Used incorrectly, it produces components that are worse than no ARIA at all.
The ARIA model: roles, states, and properties
ARIA attributes divide into three categories. Roles define what a component is — role="button", role="dialog", role="tab". States communicate current condition — aria-expanded="true", aria-checked="false", aria-disabled="true". Properties provide information about the component — aria-label="Close", aria-describedby="error-id", aria-required="true". Roles are set once; states change as the component interacts; properties may be static or dynamic.
The name, role, value pattern (WCAG 4.1.2)
Every interactive UI component must expose three things programmatically: its accessible name (what is it called?), its role (what type of component is it?), and its value or state (what is its current condition?). This is WCAG success criterion 4.1.2. Missing any of these makes the component invisible or ambiguous to assistive technology users.
A minimal accessible tab pattern
<div role="tablist" aria-label="Settings sections">
<button role="tab" aria-selected="true" aria-controls="panel-general" id="tab-general">
General
</button>
<button role="tab" aria-selected="false" aria-controls="panel-security" id="tab-security">
Security
</button>
</div>
<div role="tabpanel" id="panel-general" aria-labelledby="tab-general" tabindex="0">
<!-- General settings content -->
</div>Live regions for dynamic content
When content updates without a page load — notifications, error messages, search results — screen readers need to be told to announce the change. Use aria-live="polite" for non-urgent updates (the announcement waits for the user to finish) and aria-live="assertive" for urgent alerts (interrupts immediately). ARIA role="status" is equivalent to aria-live="polite"; role="alert" is equivalent to aria-live="assertive".
Warning
aria-live="assertive" interrupts the user immediately. Reserve it for genuinely urgent situations like login errors or destructive action confirmations. Using it for routine notifications will make your application hostile to screen reader users.
Common ARIA mistakes
The most common ARIA errors that make accessibility worse:
- Adding role="presentation" or aria-hidden="true" to focusable elements — removes them from the accessibility tree but they remain keyboard-focusable, creating ghost focus.
- Using aria-label on non-interactive elements without a role — labels are ignored without a role to attach to.
- Redundant ARIA — adding role="button" to a <button> element adds noise without benefit.
- Static aria-live regions — the region must exist in the DOM before content is injected; dynamically inserted live regions are not reliably announced.
- Missing aria-expanded on disclosure widgets — toggles without state make screen reader users guess whether the content is shown or hidden.