Skip to content

Foundations

Accessibility

Stable

Accessibility work that happens at review time is always late. In Foundry it lives in the primitive.

Updated in v1.0.0

No certification is claimed

Foundry implements published ARIA patterns and is tested against them. It has not been audited by a third party, and no WCAG conformance level is claimed. Treat the contracts below as a serious starting point, not as a compliance guarantee.

The contracts

These hold across every component in the library, without exception.

  • One focus treatment. Focus is drawn by a single :focus-visible rule in the token file. No component draws its own, so focus is identical everywhere and never appears for pointer users.
  • Focus goes in and comes back. Every overlay traps focus while open and returns it to the trigger on close. Trapping without restoring strands keyboard users at the top of the document.
  • Status is never colour alone. Every status carries an icon and a word. Every delta carries an arrow and a sign as well as a colour.
  • Live regions are polite by default. Only genuine errors interrupt. Form errors are announced once as a count, not per field on every keystroke.
  • Errors move focus. A failed submit moves focus to the first invalid control, so a keyboard user is taken to the problem rather than told one exists.
  • Reduced motion is honoured globally. Every animation in the library is disabled under prefers-reduced-motion, including the marquee and the spinner.

Keyboard reference

  • Component
    Dialog / Drawer
    Keys
    Tab, Shift+Tab, Escape
    Behaviour
    Focus trapped inside, Escape closes, focus returns to the trigger.
  • Component
    Dropdown menu
    Keys
    ↑ ↓ Home End Enter Escape Tab
    Behaviour
    Roving focus between items; ArrowUp opens on the last item.
  • Component
    Tabs
    Keys
    ← → Home End Enter Space
    Behaviour
    Manual activation — arrows move focus, Enter or Space selects.
  • Component
    Combobox
    Keys
    ↑ ↓ Home End Enter Escape
    Behaviour
    Active descendant moves; DOM focus stays in the input.
  • Component
    Command menu
    Keys
    ⌘K, ↑ ↓ Home End Enter Escape
    Behaviour
    Modal combobox; focus stays in the input for the whole interaction.
  • Component
    Slider
    Keys
    ← → ↑ ↓ Home End PgUp PgDn
    Behaviour
    Native range semantics with a formatted aria-valuetext.
  • Component
    OTP field
    Keys
    ← → Backspace, paste
    Behaviour
    Auto-advance, step-back on empty, full-code paste fills every box.
  • Component
    Tooltip
    Keys
    Focus, Escape
    Behaviour
    Opens on keyboard focus as well as hover; Escape dismisses.

Decisions worth explaining

Manual tab activation

Tabs use manual activation: arrow keys move focus, Enter or Space activates. Automatic activation feels responsive with a mouse and quietly punishes keyboard users, who mount every panel they arrow past.

Ids derived from names, not useId

Form field ids come from the field’s name rather than useId. That keeps Field renderable from a Server Component, and makes the markup byte-identical between server and client — so a form is fully labelled before any JavaScript runs.

Native controls wherever they work

Select, date field, checkbox, radio, switch and slider all keep a real native control in the DOM. Native brings keyboard support, mobile pickers and assistive-tech semantics for free; only the appearance is custom.

Disclosures over menus for navigation

The mega menu is a disclosure, not role="menu". Its contents are links and headings, so normal Tab order is both simpler and more usable than forcing roving menuitem focus across a page of navigation.

How to verify it yourself

  1. Tab through a page without touching the mouse. Every interactive element should show a ring, in order.
  2. Open a dialog, press Escape, and check that focus is back on the button that opened it.
  3. Submit an empty form and confirm focus lands on the first invalid field.
  4. Turn on reduced motion at the OS level and reload — nothing should animate.
  5. Set the display to greyscale and check that every status is still readable.

What is not covered

  • No third-party audit or certification.
  • Manual screen-reader passes are partial. Automated checks cover structure and naming; they cannot judge whether an announcement is useful.
  • No right-to-left layout support yet.
  • Colour contrast is designed to pass at the sizes used here, but a palette you author yourself needs its own check.

Every component page carries its own accessibility notes — see Dialog for the most involved example.