---
title: Accessibility
description: The accessibility features the primitives provide, and what your application still has to supply.
---

The primitives handle ARIA attributes, roles, keyboard navigation, and focus
management. Because the package renders no styles, anything visual — contrast,
focus indication — is your application's responsibility.

## Keyboard navigation

The primitives follow the [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/).
The composer's editor interprets keys by mode: an open command list, or an active
request flow, takes priority over normal typing. Each primitive page documents
its own key table.

Command lists and ask options move a highlight rather than DOM focus, so the
caret stays in the editor while you navigate. Arrowing past the end of a list
keeps focus in place; arrowing up past the first ask option returns to the
editor.

## Focus management

Focus is managed automatically after interaction — the composer restores the
caret after selecting a command item, and the request flow recovers arrow navigation if a
click elsewhere drops focus to the body.

In command lists and ask options the highlight *is* the focus indication —
focus follows it rather than moving separately, so styling the highlight faintly
removes the focus indicator rather than softening a hover effect.

## Roles and relationships

The composer's editor is wired to its command list as a combobox: `aria-expanded`,
`aria-controls`, and `aria-activedescendant` reference a `listbox`, and the active
row carries `aria-selected`. The ask widget renders a labelled `radiogroup`, or
checkboxes with `aria-checked` for multi-select questions. Disabled options are
announced but skipped by highlight and selection.

`Steps` marks the active item with `aria-current="step"`. `Reasoning.Root` carries
`aria-busy` while streaming.

## Accessible labels

Interactive parts carry default accessible names; containers do not, since they
are not controls. `Composer.Submit` announces as "Stop generating" while
generating rather than "Send message". `Attachments.Remove` takes a `filename` so
each remove button announces its own item.

All defaults are overridable via `aria-label` or `aria-labelledby`. Anything
announced as content rather than as a name — status text, validation messages —
is text you render, so it is localised wherever your copy lives.

## Testing

The package runs an axe-based suite over the composer combobox wiring, the
request flows, the display primitives' status attributes, and accessible names
across controls. Automated checks can't evaluate contrast or focus visibility in
your styling, so those need your own pass.
