Overview

Use a native HTML element first and add ARIA only where HTML cannot express the semantics. Full component patterns are in html-aria-patterns and accessibility.

Names and descriptions

An element’s accessible name comes from aria-labelledby, then aria-label, then native sources (<label>, alt, text content).

AttributeUse for
aria-labelledby="id1 id2"Naming from visible text already on the page. Preferred over aria-label.
aria-label="text"Naming when no visible text exists (icon buttons, landmarks).
aria-describedby="id"Supplementary hint or error text read after the name.
aria-details="id"Long or rich description, such as a figure’s explanation.
aria-roledescription="text"Custom role name that screen readers announce; use sparingly.
<button aria-label="Close dialog"><svg aria-hidden="true">...</svg></button>
 
<section aria-labelledby="billing"><h2 id="billing">Billing address</h2> ... </section>
 
<input id="email" aria-invalid="true" aria-describedby="email-err">
<span id="email-err">Enter a valid email address.</span>

State

Set state on the element the user interacts with, and update it from script.

AttributeValuesUse
aria-expandedtrue falseOn the trigger of an accordion, menu, or disclosure, not on the panel.
aria-pressedtrue false mixedToggle buttons.
aria-checkedtrue false mixedCustom checkboxes, switches, radios; not for native inputs.
aria-selectedtrue falseTabs, listbox options, grid cells.
aria-currentpage step location date time trueThe current item in a set, such as the active nav link.
aria-invalidtrue falseField in error; pair with aria-describedby.
aria-disabledtrueStays focusable, unlike HTML disabled.
aria-hiddentrueRemoves the subtree from the accessibility tree (decorative icons).
aria-busytrue falseRegion is updating; hold announcements.

Live regions

The live region must exist in the DOM before you change its text.

AttributeValuesBehavior
aria-livepolite assertive offAnnounce after the current speech / interrupt (use rarely).
aria-atomictrue falseRead the whole region / only the changed node.
aria-relevantadditions removals text allWhich changes announce (default additions text).
role="status" / role="alert"Implicit polite / assertive.
<div role="status" id="status"></div>   <!-- later: status.textContent = "Saved." -->

Relationships and widgets

Use these in custom widgets only; native elements already expose the relationships.

AttributeUse
aria-controls="id"Trigger controls a panel or listbox.
aria-owns="id1 id2"Treat non-child elements as children.
aria-activedescendant="id"Focus stays on the container while the active item changes.
aria-haspopupmenu listbox tree grid dialog or true.
aria-posinset / aria-setsizePosition in a virtualized list (“3 of 50”).
aria-levelHeading level for role="heading" (or tree item depth).
aria-valuenow / -min / -max / -valuetextSlider and progress values, with a readable text alternative.

Gotchas

Avoid these; each can make an interface less accessible than no ARIA at all.

GotchaFix
aria-label overrides visible text and native labels, so a button reading “Submit” labelled “Close” announces “Close”.Keep the label equal to or starting with the visible text.
aria-label is ignored on generic elements such as div and span.Put it on interactive elements, landmarks, images, or give the element a role.
aria-hidden="true" does not stop keyboard focus.Hide with hidden or inert, never on focusable elements.
A live region created and filled in one step is often not announced.Render it empty first, then update it.
aria-required duplicates native required.Use the native attribute.
aria-disabled does not block clicks.Also ignore the action in script.