Entity reference — accessibility

Covers all three variants: default, mini and removable.

Keyboard

  • With onOpen the chip (default or mini) is a real <button type="button"> with tabIndex 0 (measured), so Tab reaches it and Enter or Space opens the record.
  • Without onOpen it is not a button and takes no tab stop — a reference that leads nowhere never pretends to.
  • There is no shortcut and no arrow-key navigation between chips in a row; each is its own stop.
  • Mini in prose: a sentence can contain several tab stops. That is correct — each reference is a separate destination — but keep the count per sentence low, because there is no way to skip them.
  • Removable: two tab stops per token, the chip (when onOpen is given) and the ×, in reading order. The × is hidden until :hover or :focus-within, so tabbing into a token reveals it. Enter or Space on the × removes the token. Focus then lands wherever the browser puts it — the component does not manage this, so a host removing the last token should move focus deliberately.

ARIA

  • No explicit role: the chip is a real button, and its accessible name is the visible name text.
  • Default: the identity mark is aria-hidden, so the announcement is the name alone — which is why the name must never be abbreviated to fit.
  • The record's type is not announced in any variant. In default it is carried only by the mark's shape and colour; mini draws no mark at all. A screen-reader user hears Brno Tech a.s. without hearing that it is a company; where the kind matters, the surrounding label has to say it (a rail row's key, a field label). In a sentence there is no such label, so for mini this limit weighs more.
  • date is inside the button, so it becomes part of the name (Schůzka s Brno Tech 26. 8.), which reads correctly.
  • Removable: the × carries an aria-label from removeLabel, defaulting to Odebrat {name} (measured Odebrat Josef Novák). A bare Odebrat on every token makes a row of identical unnamed controls — always include the name. The token wrapper has no role. Removal is not announced.
  • Nothing announces what opening the chip did — the host owns that.

Focus

  • The foundation ring from ds.css applies (outline: 2px solid var(--accent); outline-offset: 1px); .eref-chip does not switch it off. Focus ring --accent: 4.44:1 light, 3.83:1 dark.
  • Mini: the ring is drawn around a 19px inline box with 1px of horizontal padding, so it sits very close to the text. It is the only focus signal, because hover does not tint the ground.
  • Removable: .eref-x has its own :focus-visible rule in ds.css; :focus-within on the token keeps the × visible for as long as focus is inside.
  • The dashed underline is a permanent affordance, not a focus indicator — focus is the ring.

Contrast

Computed from packages/ui/src/tokens/tokens.css:

  • Default: name --ink-900 on the canvas — well above AA in both themes.
  • Mini: name --fg-secondary at 12px — 3.93:1 light, 6.20:1 dark. Below AA for normal text in the light theme, and this is a link. Hover lifts it to --ink-900, but hover does not reach keyboard or touch users.
  • The dashed underline is --border-strong at rest, darkening on hover (--ink-700 default, --ink-500 mini). As a 1px decoration it is what marks a mini reference at rest — the right fallback, but a thin one.
  • Removable: the name inherits the chip's tone; the 13px × inherits currentColor and needs 3:1 as a non-text control — met with the default chip, under AA with mini in the light theme.
  • The typed tiles of the default chip are solid grounds with white glyphs; they are decorative and aria-hidden.

Known limits

  • The record type reaches only sighted users, and only in default; it is shape and colour, never words.
  • Long names are clipped, and the full value survives only in title — unavailable to touch users and unreliable for screen readers.
  • The hover tint and the darkened underline are the only rest→hover signal; at 1px they are subtle.
  • Mini at rest is under AA in the light theme.
  • Removable: the × is invisible until hover or focus, so on touch it appears only after the first tap; removal is silent to assistive technology; focus after removal is left to the browser.
  • Nothing announces the result of activating the chip.

Source: docs/entity-reference/entity-ref.a11y.md · GitLab