Skip to content
AgileLogics
7 min read

ARIA Roles, States, and Properties

Prerequisites: Accessible names and descriptions

ARIA (Accessible Rich Internet Applications) is a set of attributes that add accessibility information HTML can't express on its own — mainly for custom widgets built with JavaScript.

The first rule of ARIA

Don't use ARIA if a native HTML element does the job. A button already has the button role, is focusable, and responds to Enter and Space. This:

<!-- Avoid: rebuilding a button by hand -->
<div role="button" tabindex="0">Save</div>

still needs JavaScript for the Enter and Space keys — ARIA changes only what's announced, never behavior. WebAIM's yearly survey of the top million home pages keeps finding more detected errors on pages that use ARIA than on pages that don't.

Roles

role says what an element is. Roles you'll meet: button, link, tab, tablist, tabpanel, dialog, alert, status, menu, switch, region, img, presentation/none, search.

<div role="img" aria-label="Five star rating">★★★★★</div>

Don't add roles that elements already have (<nav role="navigation">, <button role="button">) — it's redundant, and the validator warns:

<!-- Wrong: redundant role -->
<nav role="navigation" aria-label="Main"><a href="/">Home</a></nav>
warning: The 'navigation' role is unnecessary for element 'nav'.

States and properties

aria-* attributes describe states that change:

<button type="button" aria-expanded="false" aria-controls="menu-list">Menu</button>
<ul id="menu-list" hidden>
  <li><a href="/">Home</a></li>
  <li><a href="/about">About</a></li>
</ul>

<button type="button" aria-pressed="true">Bold</button>

<button type="button" role="switch" aria-checked="false">Dark mode</button>

<a href="/cart" aria-current="page">Cart</a>
  • aria-expanded — whether the thing it controls is open. Your JavaScript must update it when the menu opens.
  • aria-pressed — a toggle button's state.
  • role="switch" + aria-checked — an on/off switch.
  • aria-current="page" — marks the current page in a navigation menu.

Hiding things

Technique Visible? Screen readers?
hidden attribute / CSS display: none No No
aria-hidden="true" Yes No
A "visually hidden" CSS class No Yes

Use aria-hidden="true" for decorative icons next to text. Never put it on something focusable — keyboard users would land on an element screen readers can't see.

A custom widget: tabs

When you do build a widget, follow the ARIA Authoring Practices Guide (APG) pattern for it — it lists the roles, states, and keyboard behavior users expect:

<div role="tablist" aria-label="Product information">
  <button type="button" role="tab" id="tab-desc" aria-selected="true" aria-controls="panel-desc">Description</button>
  <button type="button" role="tab" id="tab-rev" aria-selected="false" aria-controls="panel-rev" tabindex="-1">Reviews</button>
</div>
<div role="tabpanel" id="panel-desc" aria-labelledby="tab-desc">
  <p>A hearty rye loaf.</p>
</div>
<div role="tabpanel" id="panel-rev" aria-labelledby="tab-rev" hidden>
  <p>"Best bread in town."</p>
</div>

The markup is only half of it: JavaScript must switch aria-selected, hidden, and tabindex, and move between tabs with the arrow keys.

Practice

A "Show more" button reveals a paragraph with id="more". Write the markup for its closed state.

Show solution
<button type="button" aria-expanded="false" aria-controls="more">Show more</button>
<p id="more" hidden>Here is the rest of the story.</p>

The native details element (later in this level) does the same with no JavaScript at all.

Related developer tools