ARIA Roles and Properties
Understanding ARIA Roles and Properties
ARIA (Accessible Rich Internet Applications) provides a way to make web content more accessible to people using assistive technologies. It adds semantic meaning to elements that lack native accessibility. However, ARIA is powerful and can cause harm when misused. This guide provides a comprehensive overview of ARIA and best practices for its use.
The First Rule of ARIA
> "If you can use a native HTML element with the semantics and behavior you require, do so. Don't repurpose an element and add ARIA."
Native HTML elements have built-in accessibility that ARIA can't fully replicate:
<!-- Native (preferred) -->
<button>Click me</button>
<input type="checkbox">
<a href="/about">About</a>
<!-- ARIA (only when native isn't possible) -->
<div role="button" tabindex="0">Click me</div>
<div role="checkbox" tabindex="0" aria-checked="false"></div>
<span role="link" tabindex="0">About</span>ARIA Roles
Roles define what an element is. They're divided into several categories:
Landmark Roles
Help users navigate page structure:
<header role="banner">Site header</header>
<nav role="navigation">Main navigation</nav>
<main role="main">Main content</main>
<aside role="complementary">Sidebar</aside>
<footer role="contentinfo">Site footer</footer>
<form role="search">Search form</form>Note: HTML5 sectioning elements already have implicit roles:
<header>→ banner (when not inside article/section)<nav>→ navigation<main>→ main<aside>→ complementary<footer>→ contentinfo (when not inside article/section)
Widget Roles
For interactive components:
<!-- Single-purpose widgets -->
<div role="button">Button</div>
<div role="checkbox" aria-checked="true">Checked</div>
<div role="link">Link</div>
<div role="switch" aria-checked="false">Toggle</div>
<div role="slider" aria-valuenow="50">Slider</div>
<!-- Composite widgets -->
<div role="tablist">
<div role="tab">Tab 1</div>
<div role="tab">Tab 2</div>
</div>
<div role="tabpanel">Content</div>
<ul role="menu">
<li role="menuitem">Item 1</li>
<li role="menuitem">Item 2</li>
</ul>
<ul role="listbox">
<li role="option">Option 1</li>
<li role="option">Option 2</li>
</ul>Document Structure Roles
<div role="article">Article content</div>
<div role="heading" aria-level="2">Heading</div>
<div role="list">
<div role="listitem">Item</div>
</div>
<div role="img" aria-label="Description">...</div>
<div role="table">
<div role="row">
<div role="cell">Cell</div>
</div>
</div>ARIA States and Properties
States and properties provide additional information about elements.
States (Change with user interaction)
<!-- Boolean states -->
<button aria-pressed="true">Toggle (pressed)</button>
<button aria-expanded="false">Menu (collapsed)</button>
<div role="checkbox" aria-checked="true">Checked</div>
<input aria-invalid="true">
<div aria-hidden="true">Hidden from AT</div>
<button aria-disabled="true">Disabled</button>
<!-- Tristate -->
<div role="checkbox" aria-checked="mixed">Indeterminate</div>Properties (Generally don't change)
<!-- Labeling -->
<button aria-label="Close dialog">X</button>
<input aria-labelledby="label-id">
<input aria-describedby="hint-id">
<!-- Relationships -->
<button aria-controls="menu-id">Open menu</button>
<li aria-owns="submenu-id">Has submenu</li>
<div aria-flowto="next-section-id">...</div>
<!-- Live regions -->
<div aria-live="polite">Updates announced</div>
<div aria-live="assertive">Urgent updates</div>
<div role="alert">Error message</div>
<div role="status">Status update</div>Practical Examples
Accordion
<div class="accordion">
<h3>
<button
aria-expanded="true"
aria-controls="section1-content"
>
Section 1
</button>
</h3>
<div
id="section1-content"
role="region"
aria-labelledby="section1-header"
>
Content here...
</div>
</div>Modal Dialog
<div
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
aria-describedby="dialog-description"
>
<h2 id="dialog-title">Confirm Action</h2>
<p id="dialog-description">Are you sure you want to proceed?</p>
<button>Confirm</button>
<button>Cancel</button>
</div>Custom Dropdown
<div class="dropdown">
<button
aria-haspopup="listbox"
aria-expanded="true"
aria-controls="options-list"
>
Select option
</button>
<ul
id="options-list"
role="listbox"
aria-label="Available options"
>
<li role="option" aria-selected="true">Option 1</li>
<li role="option" aria-selected="false">Option 2</li>
<li role="option" aria-selected="false">Option 3</li>
</ul>
</div>Progress Indicator
<div
role="progressbar"
aria-valuenow="75"
aria-valuemin="0"
aria-valuemax="100"
aria-label="Upload progress"
>
<div style="width: 75%"></div>
</div>Common ARIA Mistakes
Mistake 1: Overriding Native Semantics
<!-- Bad: button is already a button -->
<button role="button">Click me</button>
<!-- Bad: Changing the semantics inappropriately -->
<button role="heading">Not actually a heading</button>Mistake 2: Missing Required States
<!-- Bad: checkbox needs aria-checked -->
<div role="checkbox">Check me</div>
<!-- Good -->
<div role="checkbox" aria-checked="false" tabindex="0">Check me</div>Mistake 3: Using aria-hidden incorrectly
<!-- Bad: Hiding focusable elements -->
<div aria-hidden="true">
<button>This button is still focusable!</button>
</div>
<!-- Good: Also prevent focus -->
<div aria-hidden="true" inert>
<button tabindex="-1">Now properly hidden</button>
</div>ARIA Checklist
- Native HTML elements used when possible
- Roles match the element's purpose
- Required ARIA states are included
- aria-label or aria-labelledby on interactive elements
- aria-hidden doesn't hide focusable elements
- Live regions used for dynamic content
- Tested with screen readers
Was this article helpful?