Go Back

ARIA Roles

Definition

ARIA roles are a fundamental component of the Web Accessibility Initiative's Accessible Rich Internet Applications (WAI-ARIA) specification. They define what an element is or does in a web page by providing semantic meaning to elements that might otherwise lack it. ARIA roles are implemented using the role attribute in HTML and are particularly important for assistive technology users, as they communicate the purpose and function of elements to screen readers and other assistive technologies.

ARIA roles enable developers to create more accessible web experiences by:

  1. Extending native HTML semantics for custom elements
  2. Fixing accessibility issues when native HTML semantics are unavailable or cannot be used
  3. Communicating state, property, and relationship information to assistive technologies

Unlike native HTML semantics, ARIA roles do not change how elements look or behave in browsers—they only affect how elements are interpreted by assistive technologies. In web development, it's important to remember that ARIA should only be used when native HTML semantics cannot provide the necessary accessibility information.

Role Categories

The WAI-ARIA specification organizes roles into several distinct categories:

1. Landmark Roles

Landmark roles identify large, navigable regions of a page, helping users of assistive technologies to navigate and understand the structure of web content.

Examples include:

  • role="banner": Primary header content (typically the site header)
  • role="navigation": Collection of navigational elements
  • role="main": Main content of the document
  • role="complementary": Supporting content for the main content
  • role="search": Search functionality
  • role="form": Region containing a form
  • role="contentinfo": 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.

2. Document Structure Roles

Document structure roles describe the structural purpose of elements within a document that are not landmarks.

Examples include:

  • role="article": Self-contained composition (article, blog post, etc.)
  • role="definition": Definition of a term
  • role="directory": List of references to members of a group
  • role="list" and role="listitem": List and list item elements
  • role="region": Perceivable section containing content relevant to a specific purpose
  • role="heading" with aria-level: Heading for a section
<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>

3. Widget Roles

Widget roles define interactive elements that are common in user interfaces but might not have direct HTML equivalents.

Examples include:

  • role="button": Clickable button
  • role="checkbox": Checkable input
  • role="tablist", role="tab", role="tabpanel": Tab interface
  • role="combobox": Combo box input
  • role="menu", role="menuitem": Menu interface
  • role="dialog" and role="alertdialog": Dialog boxes
  • role="tooltip": Contextual popup 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>

4. Live Region Roles

Live region roles indicate sections of a page that are likely to change dynamically, allowing assistive technologies to announce updates.

Examples include:

  • role="alert": Important, time-sensitive information
  • role="log": Chat logs, error logs, etc.
  • role="marquee": Scrolling information
  • role="status": Status information updated in response to user actions
  • role="timer": Countdown or elapsed time display
<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"

Implementation Best Practices

When implementing ARIA roles, follow these best practices to ensure maximum accessibility:

1. 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">.

2. 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)
  • 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" requires aria-checked
  • role="tab" works with aria-selected and aria-controls
  • role="combobox" needs attributes like aria-expanded and aria-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-label for invisible labels
  • aria-labelledby to 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>
<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>
<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:

1. Redundant Roles

Applying ARIA roles that duplicate the semantics of HTML elements.

<!-- Incorrect: Redundant role -->
<button role="button">Submit</button>

<!-- Correct -->
<button>Submit</button>

2. 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>

3. 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>

4. 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...
}

5. 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:

1. Automated Testing

Tools like:

  • Axe DevTools
  • Lighthouse
  • WAVE
  • HTML_CodeSniffer

These tools can identify basic issues such as missing required attributes or invalid role values.

2. Manual Testing with Assistive Technologies

  • Screen readers: Test with popular screen readers like NVDA, JAWS, or VoiceOver
  • Keyboard navigation: Ensure all interactive elements can be accessed and operated with a keyboard
  • Visual inspection: Check that visual states match programmatic states

3. 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
  • Test the page with screen reader navigation to validate proper announcement

ARIA States and Properties

In addition to roles, ARIA includes states and properties that provide additional information about elements:

  • States: Attributes that change during interaction (e.g., aria-checked, aria-expanded)
  • Properties: Attributes that typically remain unchanged (e.g., aria-label, aria-controls)

Accessible Name Calculation

How browsers and assistive technologies determine the accessible name of an element:

  1. aria-labelledby (highest priority)
  2. aria-label
  3. Element content
  4. title attribute (lowest priority)

Focus Management

Proper keyboard focus handling is essential when implementing custom widgets with ARIA roles:

  • Managing focus order (tabindex)
  • Containing focus within composite widgets like dialogs
  • Restoring focus when components close

Conclusion

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.

By understanding and properly implementing ARIA roles, developers can create more inclusive web experiences that work for everyone, regardless of how they access the web.