Accessible Ariel

What is ARIA?

Roles, Properties & States

ARIA lets you influence the accessibility tree directly — but only use it when native HTML isn't enough.

The Rules

Click any rule to expand it. Tags show which W3C WAI section it covers.

In This Tutorial

What We'll Cover

A quick overview of the three concepts ARIA gives you control over.

What is ARIA?

Breaking Down the Name

ARIA Stands For…

Accessible Rich Internet Applications — a W3C specification for enriching the accessibility tree.

Accessible
Rich
Internet
Applications

A set of HTML attributes maintained by the W3C — giving assistive technologies more information about what's on the page.

How Screen Readers See Your Page

The Accessibility Tree

Screen readers don't see your page visually. They navigate a parallel structure called the accessibility tree.

Accessibility Tree
  • heading "Welcome"
  • nav
  • link "Home"
  • link "About"
  • link "Blog"
  • article
  • text "Article text…"
  • button "Submit"

ARIA lets you influence the accessibility tree directly.

The Three ARIA Concepts

Roles, Properties & States

Everything ARIA can do falls into one of these three categories.

Roles

What is this element?

  • role="dialog"
  • role="tab"
  • role="listbox"

Properties

What are its characteristics?

  • aria-required="true"
  • aria-haspopup="true"
  • aria-label="Close"

States

What's its current condition?

  • aria-expanded="false"
  • aria-checked="true"
  • aria-disabled="true"
The Most Important Rule

The First Rule of ARIA

Native HTML elements already carry roles, properties, and states for free. Prefer them.

The First Rule of ARIA

Don't use ARIA
if you don't have to.

  • <button>
  • <input type="checkbox">
  • <h2>
ARIA Adds Semantics, Not Behavior

div[role=button] vs <button>

Using ARIA on a div doesn't give it keyboard support. You still have to wire everything yourself.

Keyboard focus
no
yes
Enter / Space handling
no
yes
Announced as "button"
yes
yes
Event wiring needed
yes
no
Free
no
yes
Demo: Custom Interactive Components

Custom Dropdown

No native HTML element covers a styled listbox. Use aria-haspopup, aria-expanded, and role=listbox to describe the pattern — then watch the state inspector as you interact.

Live demo

aria-expandedfalsearia-selectednone

The markup

<button
  aria-haspopup="listbox"
  aria-expanded={open}
  aria-controls={listboxId}
>
  Choose an option
</button>

<ul
  id={listboxId}
  role="listbox"
  aria-labelledby={buttonId}
  hidden={!open}
>
  <li role="option" aria-selected={selected === "Option A"}>
    Option A
  </li>
</ul>
Demo: Dynamic Content Updates

Live Regions

When content changes without a page reload, use aria-live so screen readers announce the update. Type in the search box — a screen reader would announce the result count automatically.

Live demo — type to filter

  • aria-expanded
  • aria-hidden
  • aria-label
  • aria-labelledby
  • aria-describedby
  • aria-live
  • aria-atomic
  • aria-required
  • aria-invalid
  • aria-checked
  • aria-selected
  • aria-haspopup
  • aria-controls
  • aria-disabled
aria-live region will announce:

The markup

// polite — waits for current speech to finish
<div role="status" aria-live="polite" aria-atomic="true">
  {count !== null && `Showing ${count} results`}
</div>

// assertive — interrupts immediately (use sparingly)
<div role="alert" aria-live="assertive">
  {errorMessage}
</div>
Use a screen reader to hear the announcement as you type. The result count is read aloud without you navigating to it.
Demo: Labeling Icon-Only Controls

Icon Button Labels

An icon button with no visible text needs aria-label. Without it, a screen reader just says 'button' with no context.

✗ No label

Screen reader announces: “button”

// ✗ No label — SR just says "button"
<button>
  <svg aria-hidden="true">
    <!-- × icon -->
  </svg>
</button>

✓ aria-label

Screen reader announces: “Close dialog, button”

// ✓ aria-label — SR says "Close dialog, button"
<button aria-label="Close dialog">
  <svg aria-hidden="true">
    <!-- × icon -->
  </svg>
</button>
Describing Relationships

aria-describedby

Point to any element on the page and its text becomes a description — read automatically by screen readers when the associated input is focused.

Live demo

Must be at least 8 characters and include a number.

Focus the input above to see what a screen reader would announce.

The markup

<input
  type="password"
  aria-describedby="pw-hint"
/>
<p id="pw-hint">
  Must be at least 8 characters and include a number.
</p>
Common Mistakes to Avoid

What Not to Do

Four rapid-fire patterns that break accessibility — with the correct alternative for each.

Don't use ARIA to fix bad HTML

Slapping roles onto div soup Write semantic HTML first
// ✗ Don't do this
<div role="list">
  <div role="listitem">Item 1</div>
  <div role="listitem">Item 2</div>
</div>

// ✓ Just use the right element
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
</ul>

Don't duplicate semantics

Redundant roles that already exist on native elements Trust the native element's built-in semantics
// ✗ Redundant — <button> is already role="button"
<button role="button">Submit</button>

// ✗ Redundant — <h1> is already role="heading"
<h1 role="heading" aria-level="1">Title</h1>

// ✓ Just use the element
<button>Submit</button>
<h1>Title</h1>

Never aria-hidden a focusable element

Keyboard users can still reach it — SR users can't Only aria-hidden decorative, non-focusable content
// ✗ Focus still lands here — SR skips it — confusing
<button aria-hidden="true">Submit</button>

// ✓ aria-hidden is fine on decorative SVGs
<svg aria-hidden="true" focusable="false">
  <!-- decorative icon -->
</svg>

// ✓ To truly hide from everyone, use hidden
<button hidden>Submit</button>

Keep aria state in sync

aria-expanded stays 'false' after the menu opens Update aria state whenever visual state changes
const [open, setOpen] = useState(false);

// ✗ aria-expanded hardcoded — always wrong after first click
<button aria-expanded="false" onClick={() => setOpen(o => !o)}>
  Menu
</button>

// ✓ aria-expanded mirrors state
<button aria-expanded={open} onClick={() => setOpen(o => !o)}>
  Menu
</button>
Watch the Tutorial

Full Video Tutorial

Watch the complete What is ARIA? tutorial on YouTube.

Full ARIA Checklist

Full ARIA Checklist

Every rule from this tutorial, mapped to the pattern it covers.

  • Prefer native HTML elements over ARIA whenever possible
  • role defines what the element is to assistive technology
  • aria-label / aria-labelledby gives every interactive element an accessible name
  • aria-describedby links hints, instructions, and errors to their input
  • aria-expanded, aria-checked, aria-selected stay in sync with visual state
  • aria-live regions announce dynamic content changes
  • aria-hidden never applied to focusable elements
  • Custom widgets implement full keyboard interaction (arrow keys, Enter, Escape)
  • ARIA does not add behavior — keyboard handlers must be coded manually