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.
| Attribute | Takes | When 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.
| Attribute | Values | Use case |
|---|---|---|
aria-expanded | true, false | Accordions, dropdowns, tree nodes. Set on the trigger, not the panel. |
aria-checked | true, false, mixed | Custom checkboxes, toggle buttons. mixed for indeterminate state. |
aria-selected | true, false | Tabs, listbox options, tree items within a selection container. |
aria-pressed | true, false, mixed | Toggle buttons (bold, mute, like). Use on <button> elements. |
aria-disabled | true, false | Visually disabled but focusable element; differs from HTML disabled which removes focus. |
aria-hidden | true | Remove element and all children from the accessibility tree. Use on decorative icons. |
aria-invalid | true, false, grammar, spelling | Form field in an error state; pair with aria-describedby pointing to the error message. |
aria-required | true | Field is required; prefer HTML required attribute. |
aria-current | page, step, location, date, time, true | Current 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.
| Attribute | Values | Behavior |
|---|---|---|
aria-live | polite, assertive, off | polite: announce after the user finishes current action. assertive: interrupt immediately (use sparingly). |
aria-atomic | true, false | true: read the entire region on any change. false: read only the changed node. |
aria-relevant | additions, removals, text, all | Which changes trigger an announcement. Default is additions text. |
aria-busy | true, false | Set 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
| Attribute | Values | Purpose |
|---|---|---|
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-haspopup | true, menu, listbox, tree, grid, dialog | Trigger opens a popup; screen reader announces the type. |
aria-setsize / aria-posinset | Integer / Integer. | Announce item position in a virtual list (e.g., “3 of 50”). |
aria-level | Integer. | Heading level for elements using role="heading". |
aria-valuemin / aria-valuemax / aria-valuenow | Numbers. | Slider or progress bar range and current value. |
aria-valuetext | Plain text. | Human-readable value for sliders where a number is insufficient. |
Common gotchas
aria-labeloverrides all other naming mechanisms includingaria-labelledbyand visible text content. A button witharia-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 havetabindex="-1"ordisplay: none.aria-liveregions 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; HTMLdisabledremoves both. Usearia-disabledwhen you want the element focusable but not activatable.aria-expandedbelongs on the trigger button, not on the panel. The panel itself useshiddenordisplay: noneto show/hide.aria-requiredis redundant when the native HTMLrequiredattribute is present; browsers expose it automatically.