Skip to main content
Technology & EngineeringAccessibility162 lines

Aria Patterns

ARIA roles, states, and properties for building accessible custom widgets and UI components

Quick Summary27 lines
You are an expert in ARIA roles and patterns for building accessible web applications.

## Key Points

1. Use native HTML instead of ARIA whenever possible.
2. Do not change native semantics unless absolutely necessary.
3. All interactive ARIA controls must be keyboard operable.
4. Do not use `role="presentation"` or `aria-hidden="true"` on visible, focusable elements.
5. All interactive elements must have an accessible name.
- **Roles**: Define what an element is (e.g., `role="dialog"`, `role="tab"`, `role="alert"`).
- **States**: Describe the current condition (e.g., `aria-expanded="false"`, `aria-checked="true"`).
- **Properties**: Provide additional semantics (e.g., `aria-label`, `aria-describedby`, `aria-required`).
- Inspect the accessibility tree in Chrome DevTools (Elements > Accessibility) or Firefox (Accessibility Inspector) to verify computed roles and names.
- Use the axe browser extension to flag misused ARIA attributes.
- Test with a screen reader to confirm that roles and states are announced correctly during interaction.
- Validate that every `aria-controls`, `aria-labelledby`, and `aria-describedby` reference points to an existing element ID.

## Quick Example

```html
<!-- Automatically announced by screen readers when content appears -->
<div role="alert">
  <p>Error: Your password must be at least 8 characters.</p>
</div>
```
skilldb get accessibility-skills/Aria PatternsFull skill: 162 lines
Paste into your CLAUDE.md or agent config

ARIA Patterns — Web Accessibility

You are an expert in ARIA roles and patterns for building accessible web applications.

Core Philosophy

Overview

Accessible Rich Internet Applications (ARIA) is a set of attributes that supplement HTML semantics to communicate roles, states, and properties of UI elements to assistive technologies. ARIA does not change behavior or appearance—it only changes what the accessibility tree exposes. The first rule of ARIA is: do not use ARIA if a native HTML element with the desired semantics already exists.

Core Concepts

The five rules of ARIA

  1. Use native HTML instead of ARIA whenever possible.
  2. Do not change native semantics unless absolutely necessary.
  3. All interactive ARIA controls must be keyboard operable.
  4. Do not use role="presentation" or aria-hidden="true" on visible, focusable elements.
  5. All interactive elements must have an accessible name.

ARIA categories

  • Roles: Define what an element is (e.g., role="dialog", role="tab", role="alert").
  • States: Describe the current condition (e.g., aria-expanded="false", aria-checked="true").
  • Properties: Provide additional semantics (e.g., aria-label, aria-describedby, aria-required).

Landmark roles

RoleHTML EquivalentPurpose
banner<header>Site header
navigation<nav>Navigation links
main<main>Primary content
complementary<aside>Supporting content
contentinfo<footer>Site footer
search<search>Search functionality
region<section> (with label)Generic labeled section

Implementation Patterns

Disclosure (show/hide)

<button aria-expanded="false" aria-controls="details-panel">
  Show details
</button>
<div id="details-panel" hidden>
  <p>Additional information goes here.</p>
</div>
const button = document.querySelector('[aria-controls="details-panel"]');
const panel = document.getElementById('details-panel');

button.addEventListener('click', () => {
  const expanded = button.getAttribute('aria-expanded') === 'true';
  button.setAttribute('aria-expanded', String(!expanded));
  panel.hidden = expanded;
  button.textContent = expanded ? 'Show details' : 'Hide details';
});

Modal dialog

<div role="dialog" aria-modal="true" aria-labelledby="dialog-title" aria-describedby="dialog-desc">
  <h2 id="dialog-title">Confirm deletion</h2>
  <p id="dialog-desc">This action cannot be undone. Are you sure you want to delete this item?</p>
  <div>
    <button>Cancel</button>
    <button>Delete</button>
  </div>
</div>

Tabs

<div role="tablist" aria-label="Project sections">
  <button role="tab" id="tab-overview" aria-selected="true" aria-controls="panel-overview">
    Overview
  </button>
  <button role="tab" id="tab-tasks" aria-selected="false" aria-controls="panel-tasks" tabindex="-1">
    Tasks
  </button>
</div>

<div role="tabpanel" id="panel-overview" aria-labelledby="tab-overview">
  <p>Overview content here.</p>
</div>
<div role="tabpanel" id="panel-tasks" aria-labelledby="tab-tasks" hidden>
  <p>Tasks content here.</p>
</div>

Combobox (autocomplete)

<label for="city-input">City</label>
<div>
  <input
    type="text"
    id="city-input"
    role="combobox"
    aria-expanded="false"
    aria-autocomplete="list"
    aria-controls="city-listbox"
    aria-activedescendant=""
  >
  <ul role="listbox" id="city-listbox" hidden>
    <li role="option" id="opt-nyc">New York</li>
    <li role="option" id="opt-la">Los Angeles</li>
    <li role="option" id="opt-chi">Chicago</li>
  </ul>
</div>

Alert

<!-- Automatically announced by screen readers when content appears -->
<div role="alert">
  <p>Error: Your password must be at least 8 characters.</p>
</div>

Testing & Validation

  • Inspect the accessibility tree in Chrome DevTools (Elements > Accessibility) or Firefox (Accessibility Inspector) to verify computed roles and names.
  • Use the axe browser extension to flag misused ARIA attributes.
  • Test with a screen reader to confirm that roles and states are announced correctly during interaction.
  • Validate that every aria-controls, aria-labelledby, and aria-describedby reference points to an existing element ID.

Best Practices

  • Prefer semantic HTML (<button>, <nav>, <dialog>) over ARIA equivalents; ARIA is for when HTML falls short.
  • Always pair ARIA roles with the expected keyboard interaction pattern from the WAI-ARIA Authoring Practices Guide.
  • Keep ARIA attributes in sync with the visual state—if a panel looks expanded, aria-expanded must be "true".

Common Pitfalls

  • Adding role="button" to a <div> without also making it focusable (tabindex="0") and handling Enter/Space key events.
  • Using aria-label on non-interactive, non-landmark elements where screen readers may ignore it (e.g., a plain <div> or <span>).

Anti-Patterns

Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.

Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.

Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.

Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.

Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.

Install this skill directly: skilldb add accessibility-skills

Get CLI access →