Understanding Accessibility Roles in HTML
Accessibility roles tell assistive technologies what an element is, not what it looks like. When a screen reader encounters a button, it needs to know it’s a button—whether that’s a native <button> element or a custom component with role="button". Getting this wrong means millions of users can’t navigate your interface effectively.
This guide explains how accessibility roles fit into the broader accessibility tree, when to use ARIA roles versus semantic HTML, and the best practices that keep your code both accessible and maintainable.
Key Takeaways
- Accessibility roles define what an element is for assistive technologies, not its visual appearance
- The accessibility tree contains name, role, and value/state for every element
- Semantic HTML provides implicit roles—use ARIA only when HTML falls short
- Testing with real assistive technologies ensures your implementation works for actual users
How Roles Work in the Accessibility Tree
Every web page has two trees: the DOM tree you’re familiar with, and the accessibility tree that assistive technologies actually read. The accessibility tree follows a simple model: every element has a name, role, and value/state.
<!-- DOM element -->
<button aria-pressed="true">Mute</button>
<!-- Accessibility tree representation -->
Name: "Mute"
Role: button
Value/State: pressed=true
The browser automatically builds this accessibility tree from your HTML, mapping semantic elements to their implicit roles. A <nav> element becomes a navigation landmark, a <button> becomes a button widget. ARIA roles let you modify this tree when native HTML isn’t enough—but they should be your last resort, not your first choice.
The Four Main Role Categories
Landmark Roles
Landmark roles define page regions for navigation. Modern HTML5 provides most of these natively:
<header>→ banner role (when not within<article>or<section>)<nav>→ navigation role<main>→ main role<aside>→ complementary role<footer>→ contentinfo role (when not within<article>or<section>)
Best practice: Use semantic HTML elements instead of <div role="navigation">. The redundancy of <nav role="navigation"> adds no value—the element already has that role implicitly.
Widget Roles
Widget roles describe interactive controls. These are crucial for custom components that can’t use native elements:
<!-- Good: Custom tab interface -->
<div role="tablist">
<button role="tab" aria-selected="true">Settings</button>
<button role="tab" aria-selected="false">Profile</button>
</div>
<!-- Bad: Unnecessary ARIA -->
<button role="button">Click me</button> <!-- Redundant -->
Document Structure Roles
These roles describe content organization: headings, lists, articles, and separators. Again, prefer semantic HTML:
<!-- Prefer this -->
<article>
<h2>Article Title</h2>
</article>
<!-- Over this -->
<div role="article">
<div role="heading" aria-level="2">Article Title</div>
</div>
Abstract Roles
Abstract roles like command or composite are foundational templates in WAI-ARIA. Never use these directly—they exist only for the specification to define other roles.
Discover how at OpenReplay.com.
Modern Best Practices for ARIA Roles
Prefer Semantic HTML First
The first rule of ARIA is don’t use ARIA. Native HTML elements come with built-in keyboard support, focus management, and accessibility semantics:
<!-- Always prefer this -->
<button onclick="submit()">Submit</button>
<!-- Over this anti-pattern -->
<div role="button" tabindex="0" onclick="submit()">Submit</div>
The <div> version requires additional JavaScript for keyboard support, focus styling, and proper activation—work the native button does automatically.
Avoid Redundant Landmark Roles
Since HTML5, adding explicit roles to semantic elements is unnecessary and can cause confusion:
<!-- Don't do this -->
<main role="main">
<nav role="navigation">
<header role="banner">
<!-- These elements already have implicit roles -->
<main>
<nav>
<header>
Never Override Native Interactive Roles
Changing a button’s role breaks its expected behavior:
<!-- Never do this -->
<button role="heading">Section Title</button>
<!-- This breaks keyboard interaction and screen reader expectations -->
When Custom Roles Make Sense
Custom components sometimes genuinely need ARIA roles. Here are valid use cases:
Custom Dialog Pattern
<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
<h2 id="dialog-title">Confirm Action</h2>
<button>Cancel</button>
<button>Confirm</button>
</div>
Custom Tab Interface
<div role="tablist" aria-label="User settings">
<button role="tab" aria-selected="true" aria-controls="panel-1">General</button>
<button role="tab" aria-selected="false" aria-controls="panel-2">Privacy</button>
</div>
<div role="tabpanel" id="panel-1">General settings content</div>
Handling Descriptions and Labels
For accessible names and descriptions, follow this hierarchy:
- Visible text (best for all users)
aria-labelledby(references visible text elsewhere)aria-describedby(adds supplementary information)aria-label(when visible text isn’t possible)
<button aria-describedby="help-text">Delete</button>
<span id="help-text">This action cannot be undone</span>
Note: aria-description is emerging in WAI-ARIA 1.3 but has limited support. Stick with aria-describedby for production code.
Common Anti-Patterns to Avoid
Div-based buttons: Creating <div role="button"> when <button> works perfectly
Role soup: Adding roles to every element “just in case”
Conflicting semantics: <h2 role="button"> mixes structure and interaction
Redundant roles: <nav role="navigation"> adds no value
Testing Your Implementation
Always verify roles with actual assistive technologies. Browser DevTools now include accessibility tree inspectors—use them to see exactly what screen readers perceive. Test with NVDA on Windows or VoiceOver on macOS to ensure your roles translate into a usable experience.
Conclusion
Accessibility roles exist to describe what something is, not how it looks. Native semantic HTML provides most roles implicitly—use it first. Reserve ARIA roles for genuinely custom components where HTML falls short. When you do use ARIA, ensure it aligns with WCAG guidelines and test with real assistive technologies. Remember: no ARIA is better than bad ARIA.
FAQs
No, each element can only have one role attribute. If you need multiple semantic meanings, consider restructuring your HTML to use nested elements or choosing the most appropriate single role that represents the element's primary purpose.
Absolutely not. Most HTML elements have implicit roles that work perfectly without ARIA. Only add roles when creating custom widgets or when semantic HTML cannot express the needed functionality. Overusing ARIA often creates more accessibility problems than it solves.
ARIA always wins over native HTML semantics, which can break expected behavior. For example, adding role heading to a button removes all button functionality for screen readers. This is why you should never override native interactive element roles.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.