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:
LlmBadgeandLlmAlertrender a semantic glyph next to the label for every non-default variant.LlmInputandLlmTextarearender a glyph in the invalid state in addition to the red border.LlmSteppersteps expose their status (active, completed, error) as text to assistive tech, not only via color.LlmProgresshas an accessible name and exposes its value to assistive tech viaaria-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
backgroundunder a component. The component still uses its token foreground, which was calibrated against our background, not yours. - Using
opacityon text to make it look muted. Blended opacity is not guaranteed to preserve AA — prefervar(--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, anderrorslots througharia-labelledby/aria-describedbyautomatically — you don't wire them by hand. LlmDialogandLlmDrawersetrole="dialog"andaria-modal="true". The header is announced as the accessible name.LlmToastannounces messages in a polite live region. Error toasts userole="alert".LlmMenu,LlmTabGroup,LlmAccordionGroupset the ARIA roles and relationships required by the WAI-ARIA pattern for each widget.LlmTooltipusesaria-describedby, notaria-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-ringand 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
labelprop —placeholderis not an accessible name. - Alt text. Every
<img>insideLlmAvataror user content needs a meaningfulalt, oralt=""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-motionwhen adding your own transitions. Atelier UI's built-in animations already do.
Recent accessibility improvements
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.
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.
Semantic variants (info / success / warning / danger) carry a matching glyph alongside the color. The meaning survives greyscale and high-contrast modes.
Badge and Alert now pull their foreground from paired semantic-text tokens calibrated against their backgrounds — AA in both light and dark mode.
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.