Skip to content
Explanation

Accessibility

Atelier UI targets WCAG 2.1 Level AA out of the box — contrast, non-color indicators, keyboard operability, and screen-reader semantics are baked into the primitives. This page is a map of what the library does for you, and what you are still responsible for.

Stance

ARIA-first. Every interactive surface follows the matching WAI-ARIA pattern, and keyboard interactions are standards-aligned — not bolted on after the fact.

  • Every interactive component ships with a keyboard path that matches WAI-ARIA Authoring Practices.
  • Semantic state (success / warning / danger / info / invalid) is expressed in at least two channels — color + glyph or color + text — so it survives greyscale, high-contrast modes, and color-vision deficiencies.
  • Design tokens are calibrated for WCAG AA against the surfaces they appear on. If you stay within the token set, you stay in spec.
  • Focus is visible at all times in keyboard navigation, and no component suppresses the platform focus ring without replacing it.

Non-color indicators

WCAG 1.4.1 requires that color never be the sole carrier of meaning. Atelier UI satisfies this at the primitive level:

  • LlmBadge and LlmAlert render a semantic glyph next to the label for every non-default variant.
  • LlmInput and LlmTextarea render a glyph in the invalid state in addition to the red border.
  • LlmStepper steps expose their status (active, completed, error) as text to assistive tech, not only via color.
  • LlmProgress has an accessible name and exposes its value to assistive tech via aria-valuenow.

Contrast & tokens

The token layer at /tokens is the source of truth. Every --ui-color-* pair (foreground on background) has been measured against WCAG AA in both light and dark themes. If you override tokens, re-run your contrast checker — if you stick to the defaults, you are in spec.

The two most common foot-guns:

  • Using a custom background under a component. The component still uses its token foreground, which was calibrated against our background, not yours.
  • Using opacity on text to make it look muted. Blended opacity is not guaranteed to preserve AA — prefer var(--ui-color-text-muted) instead.

Keyboard navigation

Every interactive primitive is reachable and operable from the keyboard without a mouse.

Component Key Action
Button Space / Enter Activate. Focus ring always visible in keyboard navigation.
Input, Textarea Native text editing. Invalid state also shown via a glyph (WCAG 1.4.1).
Checkbox Space Toggle. Indeterminate state supported.
Toggle Space / Enter Toggle on/off.
Radio Group Arrow keys Move selection within the group. Tab moves to the next field.
Select, Combobox Enter / Space Open. Arrow keys move between options. Enter confirms, Escape closes without change.
Dialog Escape Close. Focus is trapped inside while open and restored to the trigger on close.
Drawer Escape Same as Dialog. Closes on backdrop click when closeOnBackdrop is enabled.
Menu Enter / Space / Arrow Down Open from trigger. Arrow keys move between items, Enter activates, Escape closes.
Tabs Arrow Left / Right Move between tabs. Home / End jump to first / last.
Accordion Enter / Space Expand or collapse a section. Tab moves between headers.
Stepper Tab Move between step controls. Steps report status to assistive tech.
Pagination Tab, Enter Focus each page control, activate to navigate.
Tooltip Tab (focus) Appears on focus for keyboard users, not only on hover.
Toast Tab Dismiss button reachable by keyboard. Announced via aria-live=polite.

Screen readers

  • Form fields expose their label, hint, and error slots through aria-labelledby / aria-describedby automatically — you don't wire them by hand.
  • LlmDialog and LlmDrawer set role="dialog" and aria-modal="true". The header is announced as the accessible name.
  • LlmToast announces messages in a polite live region. Error toasts use role="alert".
  • LlmMenu, LlmTabGroup, LlmAccordionGroup set the ARIA roles and relationships required by the WAI-ARIA pattern for each widget.
  • LlmTooltip uses aria-describedby, not aria-label — the tooltip supplements the trigger's name, it does not replace it.

Focus management

  • Visible focus. Every focusable element has a visible focus indicator in both themes. The ring uses --ui-color-focus-ring and does not disappear on custom backgrounds.
  • Focus trapping. Dialog and Drawer trap Tab focus while open and restore focus to the original trigger on close.
  • Initial focus. Menus and Comboboxes move focus to the first option on open. Dialogs move focus to the first tabbable element inside the content.
  • Skip links. Long lists (Tables, Pagination, Menu) stay Tab-navigable without a skip link — but you can add one above a complex composition if the page has multiple such regions.

What's still your job

The library cannot guess your content. You still own:

  • Labels. Every form field needs a label prop — placeholder is not an accessible name.
  • Alt text. Every <img> inside LlmAvatar or user content needs a meaningful alt, or alt="" if decorative.
  • Heading hierarchy. Use <h1><h6> in source order. Don't skip levels for styling reasons.
  • Localized messages. Error text, empty states, and confirmation dialogs should be translated for your users' locale.
  • Motion preferences. Respect prefers-reduced-motion when adding your own transitions. Atelier UI's built-in animations already do.

Recent accessibility improvements

2026-04-26 audit Three-source audit cycle (axe + Figma + spec)

Median Figma component-a11y score 94 → 100, worst 77 → 92, 22 of 28 component-sets now perfect. Triangulated audit across axe-core (DOM-time), figma_audit_component_accessibility (design-time), and a static spec read (API-time); 8 P-critical findings landed across 4 phases. Headlines: LlmProgress gained a label prop, LlmAccordionItem gained headingLevel, LlmButton gained a discriminated-union aria-label requirement, LlmTable wrapper got tabindex=0 + role=region, --ui-color-text-muted darkened past the protanopia AA threshold, and LlmButton danger picked up a 1px darker border for non-color differentiation.

v0.0.21 Invalid-state glyph on Input and Textarea (WCAG 1.4.1)

Invalid form fields now show a non-color glyph in addition to the red border, so users who cannot distinguish red from the surrounding palette still get the signal.

v0.0.20 Unicode indicator glyphs on Badge and Alert variants

Semantic variants (info / success / warning / danger) carry a matching glyph alongside the color. The meaning survives greyscale and high-contrast modes.

v0.0.18 AA-safe semantic background + text token pairs

Badge and Alert now pull their foreground from paired semantic-text tokens calibrated against their backgrounds — AA in both light and dark mode.

v0.0.15 – v0.0.19 Contrast calibration pass

Primary, on-primary, secondary, placeholder, and error-text tokens were darkened and re-paired to clear WCAG AA on every surface they appear on.

Reporting accessibility issues

Atelier is a teaching artifact, not a production library, but accessibility regressions matter all the same. If you find a component that fails AA contrast, a keyboard path that dead-ends, or an ARIA role that misrepresents state, that's a bug. Open an issue on GitHub with the component name, the assistive tech you're using, and the expected vs. actual behavior. A11y fixes are treated as regressions, not feature requests.