Empty state — accessibility

Covers both tones: empty (default) and error.

Keyboard

  • The block itself holds no focus: it is text plus, optionally, one or two buttons.
  • Those buttons are real <Button>s, so Tab reaches them and Enter or Space fires them; the action is first in DOM order, the quieter secondary second.
  • When the block replaces a listing the user had focus in, the host must move focus somewhere sensible — the component does not do it. A host that replaces a focused region on failure should place focus on the action itself.

ARIA

  • Empty tone: no role and no live region. An empty state is a normal part of the page, not an announcement, and it is usually the result of a navigation the user just made.
  • Error tone: the wrapper carries role="alert", so the failure is announced when it appears rather than only drawn. This is the one state primitive that speaks. Because role="alert" is an implicit live region, do not render several error tones into one region at once — they queue up and read one after another.
  • The icon is decorative in both tones and adds no text of its own; the title and the sub-line are the announced content, which is why they must be plain sentences and not codes.
  • A listing that empties out after an action (a filter, a delete) is a change the user cannot see if they are not looking — announce it from the host if it matters. The component will not.

Focus

  • Buttons follow the foundation ring from ds.css (outline: 2px solid var(--accent); outline-offset: 1px); .acrm-empty does not override it.
  • Focus ring --accent: 4.44:1 light, 3.83:1 dark.

Contrast

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

  • Title .acrm-empty-t uses --fg-secondary at 15px (--h2) — 3.93:1 light, 6.20:1 dark: below AA for body text in the light theme, and 15px is far under the 24px "large text" threshold, so the V2 enlargement of 12 September 2026 did not move the verdict. It is the block's loudest line, so the wording must be short and unambiguous.
  • Sub-line .acrm-empty-s uses --fg-muted at 13px (--subtitle) — 2.85:1 light, 3.63:1 dark, i.e. below AA. It is a deliberately quiet second line and must never carry information the title does not already give.
  • The error icon uses --critical-ink — it meets AA for non-text, but it never carries meaning on its own: the title repeats the failure in words.
  • The icon inherits the muted tone in the empty tone and is decorative, so its contrast gates nothing.

Known limits

  • Title and sub-line sit below AA in the light theme; keep the sub-line genuinely secondary.
  • Nothing is announced when a listing becomes empty — a screen-reader user who applied a filter hears nothing change.
  • Nothing announces the result of pressing the action: a second failure re-fires the alert, a success is silent.
  • The severity of the error tone is signalled by a colour and a glyph; the title must repeat it in words, because the pair alone is not enough.
  • The block has no landmark of its own, so it is only findable by reading the region it sits in.

Source: docs/empty-state/empty-state.a11y.md · GitLab