Overview

ARIA (Accessible Rich Internet Applications) attributes bridge the gap between visual semantics and the accessibility tree. Use ARIA only when native HTML semantics are insufficient. The first rule of ARIA is: use the right HTML element before reaching for ARIA. For full patterns and component examples, see html-aria-patterns and accessibility.

Naming and description

These attributes provide the accessible name and description for elements.

AttributeTakesWhen to use
aria-label="string"Plain text string.Icon buttons, close buttons, search inputs; use when no visible label text exists.
aria-labelledby="id1 id2"Space-separated ID list.When the label is visible text already on the page; reuse it instead of duplicating.
aria-describedby="id"Space-separated ID list.Secondary description (hint, error, constraint) that supplements the label.
aria-details="id"Single ID.Longer, rich description (can include markup); supplement to aria-describedby.
aria-placeholder="string"Plain text string.Hint text inside form fields; prefer the HTML placeholder attribute instead.
aria-roledescription="string"Plain text string.Custom role name announced by screen readers; use sparingly.
<!-- Icon button: no visible text, needs aria-label -->
<button aria-label="Close dialog">
  <svg aria-hidden="true">...</svg>
</button>
 
<!-- Label reuse: visible heading is the group label -->
<section aria-labelledby="billing-heading">
  <h2 id="billing-heading">Billing address</h2>
  ...
</section>
 
<!-- Described by error message -->
<input id="email" aria-describedby="email-error" />
<span id="email-error" role="alert">Enter a valid email address.</span>

State attributes

Communicate interactive state to assistive technology.

AttributeValuesUse case
aria-expandedtrue, falseAccordions, dropdowns, tree nodes. Set on the trigger, not the panel.
aria-checkedtrue, false, mixedCustom checkboxes, toggle buttons. mixed for indeterminate state.
aria-selectedtrue, falseTabs, listbox options, tree items within a selection container.
aria-pressedtrue, false, mixedToggle buttons (bold, mute, like). Use on <button> elements.
aria-disabledtrue, falseVisually disabled but focusable element; differs from HTML disabled which removes focus.
aria-hiddentrueRemove element and all children from the accessibility tree. Use on decorative icons.
aria-invalidtrue, false, grammar, spellingForm field in an error state; pair with aria-describedby pointing to the error message.
aria-requiredtrueField is required; prefer HTML required attribute.
aria-currentpage, step, location, date, time, trueCurrent item in a set (active nav link, wizard step).
<!-- Accordion trigger -->
<button aria-expanded="false" aria-controls="panel1" id="btn1">
  Section 1
</button>
<div id="panel1" role="region" aria-labelledby="btn1" hidden>
  ...
</div>

Live region attributes

Announce dynamic content changes to screen readers without moving focus.

AttributeValuesBehavior
aria-livepolite, assertive, offpolite: announce after the user finishes current action. assertive: interrupt immediately (use sparingly).
aria-atomictrue, falsetrue: read the entire region on any change. false: read only the changed node.
aria-relevantadditions, removals, text, allWhich changes trigger an announcement. Default is additions text.
aria-busytrue, falseSet true while updating a live region; suppresses announcements until set back to false.
<!-- Status message live region -->
<div role="status" aria-live="polite" aria-atomic="true" id="status"></div>
 
<!-- After form submit -->
<script>
  document.getElementById("status").textContent = "Form submitted successfully.";
</script>
 
<!-- Loading indicator -->
<div aria-live="polite" aria-busy="true" id="results">
  Loading...
</div>

Prefer role="status" (polite) over role="alert" (assertive) for non-critical updates.

Widget roles and relationship attributes

AttributeValuesPurpose
aria-controls="id"ID of controlled element.Connects a trigger to the region it controls (panel, listbox, tree).
aria-owns="id1 id2"Space-separated IDs.Declare DOM elements as logical children when they are not DOM children.
aria-activedescendant="id"ID of focused item.Composite widgets (listbox, grid, tree) where focus stays on the container but active item changes.
aria-haspopuptrue, menu, listbox, tree, grid, dialogTrigger opens a popup; screen reader announces the type.
aria-setsize / aria-posinsetInteger / Integer.Announce item position in a virtual list (e.g., “3 of 50”).
aria-levelInteger.Heading level for elements using role="heading".
aria-valuemin / aria-valuemax / aria-valuenowNumbers.Slider or progress bar range and current value.
aria-valuetextPlain text.Human-readable value for sliders where a number is insufficient.

Common gotchas

  • aria-label overrides all other naming mechanisms including aria-labelledby and visible text content. A button with aria-label="Close" but visible text “Submit” announces as “Close.”
  • aria-hidden="true" removes the element from the accessibility tree but does not prevent keyboard focus. Focusable elements inside a hidden container must also have tabindex="-1" or display: none.
  • aria-live regions must exist in the DOM before content is injected. Dynamically creating the live region and inserting text in the same frame is often not announced.
  • aria-disabled="true" keeps the element in the tab order and the accessibility tree; HTML disabled removes both. Use aria-disabled when you want the element focusable but not activatable.
  • aria-expanded belongs on the trigger button, not on the panel. The panel itself uses hidden or display: none to show/hide.
  • aria-required is redundant when the native HTML required attribute is present; browsers expose it automatically.