> ## Documentation Index
> Fetch the complete documentation index at: https://rive.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Semantics

> Make your Rive graphics accessible to screen readers by enabling semantics in the Web (JS) runtime.

<Note>
  Semantics is currently only available in [Early Access](https://rive.app/downloads?utm_source=docs\&utm_medium=content). Runtime support is in development or released as experimental.

  Have feedback? Join the [Early Access community](https://community.rive.app/c/early-access/) to share your thoughts and help shape the feature.
</Note>

This page covers enabling semantics in the Web (JS) runtime so your Rive graphics are accessible to screen readers. To learn what semantics are and how to add them to a graphic, see the [Semantics](/docs/editor/accessibility/semantics) editor documentation.

## Overview

In the Rive Editor, you can add semantic meaning to certain elements of your graphic-roles such as button, checkbox, tab, image, list, dialog, and more. Alongside these roles, you can add associated labels, values, states, and actions. These settings vary per role.

At runtime, the Web (JS) runtime reads those semantics from the running state machine and builds an invisible DOM tree next to your `<canvas>`, keeping it in sync as the state machine advances.

Because a `<canvas>` is opaque to assistive technologies (AT), this DOM overlay is what makes your graphic discoverable. Each semantic node becomes a real DOM element with the matching ARIA role, attributes, and keyboard handlers, positioned over the corresponding nodes in the Rive graphic.

<Warning>
  Semantics are **opt-in**. The default mode is `SemanticMode.Disabled`, so no semantic DOM is created until you enable semantics. As an experimental API, `semanticsMode` and `semanticsOptions` may change behavior without a major version bump.
</Warning>

<Note>
  Semantics must be defined in the editor to have any effect. If an element has no semantics, it is not exposed to screen readers, regardless of the mode you set. See [Feature Support](/docs/feature-support) for which runtimes currently support semantics.
</Note>

## Semantics Modes

Semantics are controlled by the `SemanticMode` enum, which you import from your Rive package and pass as a parameter when you instantiate Rive:

| Mode                    | Description                                                                                                          |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `SemanticMode.Disabled` | **Default.** Semantics are disabled. No semantic tree or accessibility DOM is created.                               |
| `SemanticMode.Enabled`  | Semantics are active. The accessibility DOM is created after load and kept up to date as the state machine advances. |

## Usage

Import `SemanticMode` and pass it as `semanticsMode` when you instantiate Rive.

```js highlight={1,9} theme={null}
import { Rive, SemanticMode } from "@rive-app/webgl2";

const rive = new Rive({
  canvas: document.getElementById("rive-canvas"),
  src: "login.riv",
  stateMachines: "State Machine 1",
  autoplay: true,
  autoBind: true,
  semanticsMode: SemanticMode.Enabled,
});
```

### Enabling Semantics After Load

If you want to programmatically control when semantics are enabled, construct `Rive` with the default `SemanticMode.Disabled` and call the `enableSemantics()` method when the user opts in.

```js highlight={12} theme={null}
const rive = new Rive({
  canvas: document.getElementById("rive-canvas"),
  src: "login.riv",
  stateMachines: "State Machine 1",
  semanticsMode: SemanticMode.Disabled,
  autoplay: true,
  autoBind: true,
});

accessibilityToggle.addEventListener("change", (event) => {
  if (event.target.checked) {
    rive.enableSemantics();
  }
});
```

### Labeling the Graphic

The semantic overlay's container element is a `role="region"` landmark. Use the `semanticsOptions.riveCanvasLabel` parameter when instantiating Rive to give it an `aria-label` that describes what the graphic is, so screen reader users know what they are entering.

```js highlight={8-10} theme={null}
const rive = new Rive({
  canvas: document.getElementById("rive-canvas"),
  src: "login.riv",
  stateMachines: "State Machine 1",
  autoplay: true,
  autoBind: true,
  semanticsMode: SemanticMode.Enabled,
  semanticsOptions: {
    riveCanvasLabel: "Login Experience",
  },
});
```

| Option            | Type     | Description                                                                    |
| ----------------- | -------- | ------------------------------------------------------------------------------ |
| `riveCanvasLabel` | `string` | `aria-label` applied to the overlay container. Defaults to `"Rive animation"`. |

## How Semantics Map to the DOM

The runtime translates each editor semantic role into an ARIA role, and each state and trait into the matching ARIA attribute. In most cases, these are attached to `<div>` elements to standardize
on common style/layout patterns across browsers. Text content is wrapped in an inner `<span>` to assist with AT's browsing text.

<Warning>
  This set of roles and traits may be subject to change while the semantic feature is in early access.
</Warning>

| Editor role                | DOM output                                                                                                                                      |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Button                     | `role="button"`                                                                                                                                 |
| Checkbox                   | `role="checkbox"`                                                                                                                               |
| Switch                     | `role="switch"`                                                                                                                                 |
| Slider                     | `role="slider"`                                                                                                                                 |
| Text                       | Text lives as DOM text content in an inner `<span>` with a parent `<div>`. For headers, the `<div>` has `role="heading"` with `aria-level` set. |
| Image                      | `role="img"` but must have a label                                                                                                              |
| Group / None               | `role="group"`                                                                                                                                  |
| List / List item           | `role="list"` / `role="listitem"`                                                                                                               |
| Tab / Tab list             | `role="tab"` / `role="tablist"`                                                                                                                 |
| Dialog / Alert dialog      | `role="dialog"` with `aria-modal` when modal / `role="alertdialog"` always with `aria-modal`                                                    |
| Radio group / Radio button | `role="radiogroup"` / `role="radio"`                                                                                                            |

States and traits map to ARIA attributes such as `aria-expanded`, `aria-selected`, `aria-checked`, `aria-pressed`, `aria-required`, and `aria-disabled`. An attribute is only set when the corresponding trait is present in the editor or if ARIA requires its presence, so ATs see "not applicable" rather than "false".

* **Label** becomes `aria-label` (or DOM text content for text nodes).
* **Hint** becomes a visually hidden description referenced by `aria-describedby`.
* **Value** on a slider becomes `aria-valuenow` and `aria-valuetext`.
* **Hidden** becomes `aria-hidden="true"`.
* **Live Region** becomes `aria-live="polite"`.

<Note>
  An image with no label is treated as decorative and hidden from assistive technologies, since a `role="img"` element without an accessible name is a WCAG failure. Add a label in the editor to any image that carries meaning.
</Note>

## Other Considerations

### Tab Index

Interactive, focusable, and list item overlay elements are given `tabindex="-1"`, which keeps them out of the browser's sequential Tab order; the rest carry no tabindex at all. These elements remain reachable by a screen reader's cursor, but a sighted keyboard-only user cannot Tab into individual elements inside the graphic.

### Semantic Overlay Positioning

Rive keeps the overlay matched to the canvas as it resizes or moves. The runtime watches the canvas, its parent, and the window for changes and re-syncs the overlay, so the common cases need no extra work.

The overlay container is inserted as a sibling of the `<canvas>` and positioned using the canvas's layout offsets. When possible, give the canvas's container `position: relative` so the overlay and the canvas resolve their positions against the same ancestor. If no ancestor of the canvas is positioned, the overlay resolves against the document instead, and the `<body>` margin offsets it from the canvas.

## Testing Semantics

Turn on a screen reader and navigate through your graphic after enabling semantics at runtime:

* **macOS / iOS**: VoiceOver
* **Android**: TalkBack
* **Windows**: Narrator or NVDA

Check that each element announces a clear label, the correct role, an accurate value and state, and that navigation follows a logical order. You can also inspect the generated DOM in your browser's devtools — look for the `<div id="rive-a11y-...">` element immediately after your canvas — or use the browser's accessibility tree inspector.

For a full checklist, see [Testing Semantics](/docs/editor/accessibility/semantics#testing-semantics) in the editor documentation.
