ARIA Roles
Categories: Accessibility & Inclusive Design, Front-End Development
What are ARIA Roles?
ARIA roles are attributes that tell assistive technologies what an element is or does on a web page. Think of them as labels that help screen readers and other assistive technologies understand the purpose of different parts of your interface.
It's like putting name tags on different areas of a building - someone who can't see the building layout would still know where the kitchen, bathroom, and living room are because of the labels. ARIA roles do the same thing for web content.
ARIA roles are implemented using the role attribute in HTML and are particularly important for assistive technology users because they communicate the purpose and function of elements that might otherwise be unclear.
Why ARIA Roles Matter
ARIA roles help you create more accessible web experiences by extending native HTML semantics for custom elements, fixing accessibility issues when native HTML semantics aren't available, and communicating state, property, and relationship information to assistive technologies.
They also help you meet accessibility standards, avoid legal issues, and reach a broader audience while creating more inclusive, professional applications.
Unlike native HTML semantics, ARIA roles don't change how elements look or behave in browsers - they only affect how elements are interpreted by assistive technologies. Remember that ARIA should only be used when native HTML semantics can't provide the necessary accessibility information.
Types of ARIA Roles
The WAI-ARIA specification organizes roles into several distinct categories:
Landmark Roles
Landmark roles identify large, navigable regions of a page, helping users of assistive technologies navigate and understand the structure of web content.
role="banner" identifies primary header content, typically the site header.
role="navigation" marks collections of navigational elements.
role="main" identifies the main content of the document.
role="complementary" marks supporting content for the main content.
role="search" identifies search functionality.
role="form" marks regions containing forms.
role="contentinfo" identifies information about the parent document, typically the footer.
<header role="banner">
<h1>Company Name</h1>
</header>
<nav role="navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
</ul>
</nav>
<main role="main">
<h2>Welcome to our website</h2>
<p>Main content goes here.</p>
</main>
<aside role="complementary">
<h3>Related Information</h3>
<p>Additional content related to the main content.</p>
</aside>
<footer role="contentinfo">
<p>Copyright © 2023</p>
</footer>
Note: Many landmark roles have HTML5 semantic equivalents (<header>, <nav>, <main>, etc.) that should be used instead when possible. The ARIA landmarks are primarily needed for backward compatibility or when semantic HTML elements cannot be used.
Document Structure Roles
Document structure roles describe the structural purpose of elements within a document that aren't landmarks.
role="article" identifies self-contained compositions like articles or blog posts.
role="definition" marks definitions of terms.
role="directory" identifies lists of references to members of a group.
role="list" and role="listitem" mark list and list item elements.
role="region" identifies perceivable sections containing content relevant to a specific purpose.
role="heading" with aria-level marks headings for sections.
<div role="article">
<h2>Blog Post Title</h2>
<p>Content of the blog post...</p>
</div>
<dl>
<dt>HTML</dt>
<dd role="definition">HyperText Markup Language is the standard markup language for documents designed to be displayed in a web browser.</dd>
</dl>
Widget Roles
Widget roles define interactive elements that are common in user interfaces but might not have direct HTML equivalents.
role="button" identifies clickable buttons.
role="checkbox" marks checkable inputs.
role="tablist", role="tab", and role="tabpanel" create tab interfaces.
role="combobox" identifies combo box inputs.
role="menu" and role="menuitem" create menu interfaces.
role="dialog" and role="alertdialog" identify dialog boxes.
role="tooltip" marks contextual popups providing additional information.
<div role="tablist">
<div id="tab1" role="tab" aria-selected="true" aria-controls="panel1">Tab 1</div>
<div id="tab2" role="tab" aria-selected="false" aria-controls="panel2">Tab 2</div>
</div>
<div id="panel1" role="tabpanel" aria-labelledby="tab1">
<p>Content for Tab 1</p>
</div>
<div id="panel2" role="tabpanel" aria-labelledby="tab2" hidden>
<p>Content for Tab 2</p>
</div>
Live Region Roles
Live region roles indicate sections of a page that are likely to change dynamically, allowing assistive technologies to announce updates.
role="alert" identifies important, time-sensitive information.
role="log" marks chat logs, error logs, and similar content.
role="marquee" identifies scrolling information.
role="status" marks status information updated in response to user actions.
role="timer" identifies countdown or elapsed time displays.
<div role="alert">
Your session will expire in 5 minutes.
</div>
<div role="status" aria-live="polite">
Form successfully submitted.
</div>
<div role="log" aria-live="polite">
<p>User1: Hello everyone!</p>
<p>User2: Hi there!</p>
</div>
5. Abstract Roles
Abstract roles are the foundation for other roles and should not be used directly in HTML code. They form the taxonomy upon which the role model is built.
Examples include:
role="command"role="composite"role="input"role="landmark"role="range"role="section"role="widget"
Best Practices for ARIA Roles
When implementing ARIA roles, follow these best practices to ensure maximum accessibility:
HTML First
Always prefer native HTML elements with built-in semantics over ARIA roles when possible. This approach is often referred to as the "first rule of ARIA":
If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of repurposing an element and adding an ARIA role, state or property to make it accessible, then do so.
For example, use <button> instead of <div role="button">, or <nav> instead of <div role="navigation">.
Match Semantics with Behavior
When using ARIA roles, ensure the element's behavior matches the semantic role you've assigned. If you use role="button", the element must be focusable using tabindex="0", respond to both mouse clicks and keyboard interactions (Enter and Space), and be properly labeled for assistive technologies.
<div role="button" tabindex="0" aria-pressed="false" onclick="toggleButton(this)" onkeydown="handleKeydown(event, this)">
Toggle Setting
</div>
<script>
function toggleButton(el) {
const isPressed = el.getAttribute('aria-pressed') === 'true';
el.setAttribute('aria-pressed', !isPressed);
// Additional logic...
}
function handleKeydown(event, el) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
toggleButton(el);
}
}
</script>
3. Use Role-Specific ARIA Attributes
Many ARIA roles have associated states and properties that should be used together with the role for complete accessibility.
For example:
role="checkbox"requiresaria-checkedrole="tab"works witharia-selectedandaria-controlsrole="combobox"needs attributes likearia-expandedandaria-controls
<div role="checkbox" aria-checked="false" tabindex="0">
Accept terms and conditions
</div>
4. Maintain Accessible Names
Ensure all interactive elements with ARIA roles have accessible names that assistive technologies can announce. Use:
- Visible text content inside the element
aria-labelfor invisible labelsaria-labelledbyto reference another element as the label
<!-- Using element content -->
<div role="button">Submit Form</div>
<!-- Using aria-label -->
<div role="button" aria-label="Close dialog">✕</div>
<!-- Using aria-labelledby -->
<h2 id="dialogTitle">Confirm Deletion</h2>
<div role="dialog" aria-labelledby="dialogTitle">
<!-- Dialog content -->
</div>
5. Update ARIA States Dynamically
When the state of a component changes due to user interaction, update the relevant ARIA attributes to reflect the new state.
// When a tab is activated:
function activateTab(tabId) {
// Deactivate all tabs
document.querySelectorAll('[role="tab"]').forEach(tab => {
tab.setAttribute('aria-selected', 'false');
document.getElementById(tab.getAttribute('aria-controls')).hidden = true;
});
// Activate selected tab
const selectedTab = document.getElementById(tabId);
selectedTab.setAttribute('aria-selected', 'true');
document.getElementById(selectedTab.getAttribute('aria-controls')).hidden = false;
}
Common ARIA Role Patterns
Here are some common UI patterns and the appropriate ARIA roles for implementing them:
Tabs
<div class="tabs-container">
<div role="tablist" aria-label="Content tabs">
<button id="tab1" role="tab" aria-selected="true" aria-controls="panel1">Tab 1</button>
<button id="tab2" role="tab" aria-selected="false" aria-controls="panel2">Tab 2</button>
</div>
<div id="panel1" role="tabpanel" aria-labelledby="tab1">
<p>Content for tab 1</p>
</div>
<div id="panel2" role="tabpanel" aria-labelledby="tab2" hidden>
<p>Content for tab 2</p>
</div>
</div>
Accordion
<div class="accordion">
<h3>
<button id="accordion1" aria-expanded="true" aria-controls="sect1">
Section 1
</button>
</h3>
<div id="sect1" role="region" aria-labelledby="accordion1">
<p>Content for section 1.</p>
</div>
<h3>
<button id="accordion2" aria-expanded="false" aria-controls="sect2">
Section 2
</button>
</h3>
<div id="sect2" role="region" aria-labelledby="accordion2" hidden>
<p>Content for section 2.</p>
</div>
</div>
Modal Dialog
<div id="modalOverlay" role="presentation" aria-hidden="true" hidden>
<div role="dialog" aria-labelledby="dialogTitle" aria-describedby="dialogDesc" aria-modal="true">
<h2 id="dialogTitle">Confirmation</h2>
<p id="dialogDesc">Are you sure you want to delete this item?</p>
<div class="dialog-buttons">
<button>Cancel</button>
<button>Delete</button>
</div>
</div>
</div>
Menu
<div role="menubar" aria-label="Main Menu">
<div role="menuitem" tabindex="0" aria-haspopup="true" aria-expanded="false">
File
<div role="menu" hidden>
<div role="menuitem" tabindex="-1">New</div>
<div role="menuitem" tabindex="-1">Open</div>
<div role="menuitem" tabindex="-1">Save</div>
</div>
</div>
<div role="menuitem" tabindex="0" aria-haspopup="true" aria-expanded="false">
Edit
<div role="menu" hidden>
<div role="menuitem" tabindex="-1">Cut</div>
<div role="menuitem" tabindex="-1">Copy</div>
<div role="menuitem" tabindex="-1">Paste</div>
</div>
</div>
</div>
Common Pitfalls and Errors
Avoid these common errors when implementing ARIA roles:
Redundant Roles
Applying ARIA roles that duplicate the semantics of HTML elements:
<!-- Incorrect: Redundant role -->
<button role="button">Submit</button>
<!-- Correct -->
<button>Submit</button>
Incorrect Role Usage
Using roles in ways that don't match their intended purpose:
<!-- Incorrect: Misused role -->
<ul role="navigation">
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
<!-- Correct -->
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
Missing Required Attributes
Not including required attributes for certain roles:
<!-- Incorrect: Missing required attributes -->
<div role="checkbox">Accept terms</div>
<!-- Correct -->
<div role="checkbox" aria-checked="false" tabindex="0">Accept terms</div>
Static ARIA Attributes
Not updating ARIA attributes when the interface state changes:
<!-- Incorrect: Static aria-expanded value -->
<button aria-expanded="false" onclick="toggleMenu()">Menu</button>
<!-- Correct approach in JavaScript -->
function toggleMenu() {
const menuButton = document.getElementById('menuButton');
const isExpanded = menuButton.getAttribute('aria-expanded') === 'true';
menuButton.setAttribute('aria-expanded', !isExpanded);
// Toggle menu visibility...
}
Overusing ARIA
Adding unnecessary ARIA roles and attributes when native HTML semantics would suffice:
<!-- Incorrect: Overusing ARIA -->
<div role="heading" aria-level="2">Section Title</div>
<!-- Correct -->
<h2>Section Title</h2>
Testing ARIA Roles
To ensure proper implementation of ARIA roles, use a combination of automated testing and manual testing with assistive technologies.
Automated Testing
Tools like Axe DevTools, Lighthouse, WAVE, and HTML_CodeSniffer can identify basic issues such as missing required attributes or invalid role values.
Manual Testing with Assistive Technologies
Test with popular screen readers like NVDA, JAWS, or VoiceOver, ensure all interactive elements can be accessed and operated with a keyboard, and check that visual states match programmatic states.
Testing Checklist
Verify each role has all required attributes, check that interactive elements are keyboard accessible, ensure dynamic updates properly modify ARIA attributes, confirm that accessible names are clear and descriptive, and test the page with screen reader navigation to validate proper announcement.
Getting Started
If you want to improve your ARIA roles implementation, begin with these fundamentals:
Start by using semantic HTML elements like <button>, <nav>, or <header> whenever possible.
Only add ARIA roles when you need to enhance native HTML semantics or create complex widgets.
Test your implementation with screen readers and other assistive technologies.
Make sure all interactive elements work with keyboard navigation.
Remember that ARIA roles are a powerful tool for enhancing web accessibility, particularly for custom components and dynamic content. When used correctly, respecting the "HTML first" principle and ensuring behavior matches semantics, they significantly improve the experience for users of assistive technologies.
However, ARIA roles are not a substitute for building with accessible design principles from the start. The most effective approach to web accessibility combines semantic HTML, appropriate ARIA usage, and thorough testing with assistive technologies to ensure all users can perceive, understand, navigate, and interact with web content.