# Role, State, and Property

> Canonical HTML version: https://rocketvalidator.com/glossary/aria-roles-states-properties
> Attribution: Rocket Validator (https://rocketvalidator.com)
> License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

In WAI-ARIA, roles, states, and properties are the three categories of attributes that define what a UI element is, what condition it is in, and what characteristics it has, enabling assistive technologies to present and interact with web content accurately.

WAI-ARIA (Web Accessibility Initiative – Accessible Rich Internet Applications) provides a vocabulary of **roles**, **states**, and **properties** that authors can add to HTML elements to bridge the gap between custom widgets and the accessibility information that assistive technologies need. Together, these three categories form the backbone of how the browser's accessibility tree describes every interactive or informational element on the page.

A **role** tells assistive technologies *what* an element is (e.g., a button, a tab, a navigation landmark). A **state** communicates the element's *current condition* — something that typically changes during user interaction (e.g., `aria-expanded="true"`, `aria-checked="false"`). A **property** describes a more *persistent characteristic* of the element that is less likely to change frequently (e.g., `aria-label`, `aria-describedby`, `aria-required`). While the line between states and properties can be subtle, understanding the distinction helps authors build interfaces that keep assistive technologies informed as users interact with the page.

## Why Role, State, and Property matters

When native HTML semantics are insufficient — for instance, when building a custom combobox, tree view, or drag-and-drop interface — ARIA roles, states, and properties fill in the missing accessibility information. Without them:

- **Screen reader users** cannot tell what a custom widget is or how to operate it, because the accessibility tree contains only generic elements like `<div>` or `<span>`.
- **Voice-control users** may be unable to target or activate controls that lack recognizable roles.
- **Automated validators** flag missing or incorrect ARIA usage, signaling that the content may be inaccessible.
- **Keyboard-only users** lose orientation cues such as expanded/collapsed states and current-item indicators.

Correct use of roles, states, and properties ensures that the dynamic behaviour visible on screen is also conveyed programmatically, fulfilling WCAG success criteria like **4.1.2 Name, Role, Value** and **1.3.1 Info and Relationships**.

## How Role, State, and Property works

### Roles

A `role` attribute overrides an element's implicit semantics. Roles fall into several groups:

- **Landmark roles** — `banner`, `navigation`, `main`, `contentinfo`, etc. — define page regions.
- **Widget roles** — `button`, `tab`, `slider`, `dialog`, `menu`, etc. — describe interactive controls.
- **Document-structure roles** — `heading`, `list`, `img`, `table`, etc. — mirror structural HTML elements.
- **Live-region roles** — `alert`, `log`, `status`, `timer` — tell assistive technologies to announce dynamic content updates.

It is best practice to rely on **semantic HTML** first (`<button>`, `<nav>`, `<main>`) and only add explicit roles when native elements cannot provide the required semantics.

### States

States reflect conditions that change as users interact with the interface. Common states include:

- `aria-expanded` — indicates whether a collapsible section is open or closed.
- `aria-selected` — marks the currently selected item in a list or set of tabs.
- `aria-checked` — mirrors the checked/unchecked/mixed condition of checkboxes.
- `aria-disabled` — signals that an element is visible but not operable.
- `aria-hidden` — removes an element from the accessibility tree entirely.

Authors must keep state attributes in sync with the visual presentation by updating them via JavaScript whenever the UI changes.

### Properties

Properties tend to be set once (or change rarely) and describe structural relationships or labels:

- `aria-label` and `aria-labelledby` — provide an accessible name.
- `aria-describedby` — links an element to supplementary descriptive text.
- `aria-required` — indicates a required form field.
- `aria-controls` — identifies which element is controlled by the current one.
- `aria-live` — sets the politeness level for a live region (`polite`, `assertive`, `off`).

### Interaction between all three

A single element often combines a role with several states and properties. The browser compiles this information into the accessibility tree, which assistive technologies query to present content to the user.

## Code examples

### Bad example — custom disclosure button with no ARIA

In this example, a `<div>` is styled to look like an expandable button, but assistive technologies see only a generic container with no role, no state, and no accessible name.

```html
<div class="toggle" onclick="togglePanel()">
  Show details
</div>
<div class="panel" id="details" style="display:none;">
  <p>Here are the extra details.</p>
</div>
```

A screen reader will announce this as plain text with no indication that it is interactive or that it controls a collapsible panel.

### Good example — custom disclosure button with roles, states, and properties

```html
<button
  type="button"
  aria-expanded="false"
  aria-controls="details"
  onclick="togglePanel(this)">
  Show details
</button>
<div class="panel" id="details" hidden>
  <p>Here are the extra details.</p>
</div>

<script>
  function togglePanel(btn) {
    const panel = document.getElementById("details");
    const isExpanded = btn.getAttribute("aria-expanded") === "true";
    btn.setAttribute("aria-expanded", String(!isExpanded));
    panel.hidden = isExpanded;
  }
</script>
```

Here the native `<button>` element already carries the implicit **role** of `button`. The **state** `aria-expanded` tells screen readers whether the panel is open or closed and is toggled via JavaScript. The **property** `aria-controls` communicates which element the button operates. This gives assistive technology users the complete picture: *what* the element is, *what it controls*, and *what condition it is in*.

### Bad example — incorrect role and missing state on tabs

```html
<ul>
  <li class="active" onclick="showTab(0)">Tab 1</li>
  <li onclick="showTab(1)">Tab 2</li>
</ul>
```

### Good example — proper tab pattern with roles, states, and properties

```html
<div role="tablist" aria-label="Settings">
  <button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1">
    General
  </button>
  <button role="tab" id="tab-2" aria-selected="false" aria-controls="panel-2" tabindex="-1">
    Privacy
  </button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
  <p>General settings content.</p>
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
  <p>Privacy settings content.</p>
</div>
```

In this pattern the **roles** (`tablist`, `tab`, `tabpanel`) describe the widget structure, the **state** `aria-selected` marks the active tab, and the **properties** `aria-controls` and `aria-labelledby` establish relationships between tabs and their panels. Together they form a fully accessible tab interface that any screen reader can navigate and announce correctly.
