ARIA Roles Overview
What ARIA roles do, first rule of ARIA, landmark/widget/document roles, and when native HTML already provides the role.
- accessibility
- aria-roles
ARIA roles tell assistive tech what a custom element is — button, dialog, navigation, tab. They don’t implement keyboard behavior or styling. Wrong roles are worse than none: a div with role="button" that isn’t keyboard-activatable is a defect.
Docs: ARIA roles, First Rule of ARIA.
First rule of ARIA
If you can use a native HTML element with the semantics and behavior you need already built in, do that instead.
<!-- Prefer -->
<button type="button">Save</button>
<!-- Avoid unless you must -->
<div role="button" tabindex="0">Save</div>
Native elements bring keyboard, form participation, and expected AT mapping.
Role categories (practical)
| Category | Examples | Notes |
|---|---|---|
| Landmark | banner, main, navigation, contentinfo |
Prefer <header>, <main>, <nav>, <footer> |
| Widget | button, checkbox, dialog, tab, menu |
Need full keyboard + state |
| Structure | list, heading, region |
Prefer <ul>, <h1–6> |
| Live | alert, status, log |
Dynamic announcements |
| Abstract | command, input |
Don’t use in content |
Landmarks
<header>…</header>
<nav aria-label="Primary">…</nav>
<main>…</main>
<footer>…</footer>
Multiple navs need unique labels (aria-label / aria-labelledby).
Widget roles need full implementation
If you set role="dialog", implement the dialog pattern. Role alone is a costume.
Related patterns:
Implicit roles
| HTML | Implicit role |
|---|---|
<button> |
button |
<a href> |
link |
<nav> |
navigation |
<input type="checkbox"> |
checkbox |
Don’t redeclare redundant roles unless fixing a known browser quirk.
States and properties go with roles
<button aria-expanded="false" aria-controls="filters">Filters</button>
aria-checked, aria-selected, aria-invalid, aria-disabled — keep them in sync with real UI.
Footguns
role="button"without Enter/Space.- Role conflicts (
<a role="button">— pick one interaction model). - Over-landmaking every
<div role="region">. - Using ARIA to “fix” bad HTML instead of correcting structure.
Interview out-loud answer
“Roles map elements to AT semantics. Prefer native HTML. If I use a widget role, I implement keyboard and ARIA states per APG. Landmarks structure the page; wrong roles are worse than missing ARIA.”
Role denial and fixes
If DevTools shows role generic on something you thought was a button, you used a non-semantic element. Fix the element type before sprinkling ARIA. Role denial (role="presentation") on interactive elements is almost always wrong.
Extra practice
Write a minimal demo in a scratch file or the playground: one happy path, one failure path, and one boundary input. If you cannot exhibit a bug that the pattern prevents, you do not own the concept yet — re-read the primary docs linked below and tighten the example until the failure is obvious.
Notes from real codebases
Teams that succeed here keep the rules mechanical: lint where possible, CI for the rest, and a short human checklist for what automation cannot see. Document exceptions with an owner name and a removal date so “temporary” escapes do not become permanent architecture.
Related on this site
- Accessible names computation
- ARIA labels and descriptions
- WCAG principles POUR
- What is the accessibility tree
- Keyboard accessibility checklist
Further reading
Related guides
- Accessible Combobox PatternBuild or evaluate comboboxes with APG keyboard behavior, aria-expanded/activedescendant, and filterable listbox pairing.
- Accessible Forms ErrorsLabel inputs, associate errors with fields, announce failures, and avoid placeholder-only forms that break accessibility.
- Accessible Menus PatternAPG menu and menubar keyboard model — arrow navigation, escape to close, aria-expanded, and when a disclosure is enough.
- Accessible Modals PatternsModal dialogs that work: focus trap, Escape, return focus, aria-modal, and why native dialog or APG patterns beat div soup.
- Accessible Names ComputationHow browsers compute accessible names from content, labels, aria-label, and labelledby — and how that powers Testing Library queries.