Skip to content

Accessibility

WCAG 2.2 AA contrast by construction, keyboard support from React Aria.

Syntara targets WCAG 2.2 AA. Two parts of that are built in rather than checked afterwards: colour contrast, which the theme engine guarantees for every brand, and keyboard and screen-reader behaviour, which comes from React Aria.

Contrast by construction

A theme that fails WCAG 2.2 AA can’t be generated. For every brand, in light and dark, the engine checks each pair below. When one fails, it moves the colour along its ramp — or nudges it off the ramp — until it passes, and records why.

ForegroundChecked againstMinimum
text.defaultsurface.canvassurface.defaultsurface.raisedsurface.sunkensurface.selected4.5:1
text.subtlesurface.canvassurface.defaultsurface.raisedsurface.sunkensurface.selected4.5:1
text.brandsurface.canvassurface.defaultsurface.raisedsurface.sunkensurface.selected4.5:1
text.inversesurface.inverse4.5:1
action.primary.fgaction.primary.bgaction.primary.hoveraction.primary.pressed4.5:1
action.secondary.fgaction.secondary.bgaction.secondary.hoveraction.secondary.pressed4.5:1
accent.fgaccent.bg4.5:1
accent.textsurface.defaultaccent.subtle4.5:1
feedback.success.fgfeedback.success.bgsurface.canvassurface.defaultsurface.raisedsurface.selectedsurface.sunken4.5:1
feedback.warning.fgfeedback.warning.bgsurface.canvassurface.defaultsurface.raisedsurface.selectedsurface.sunken4.5:1
feedback.danger.fgfeedback.danger.bgsurface.canvassurface.defaultsurface.raisedsurface.selectedsurface.sunken4.5:1
feedback.info.fgfeedback.info.bgsurface.canvassurface.defaultsurface.raisedsurface.selectedsurface.sunken4.5:1
feedback.success.onSolidfeedback.success.solid4.5:1
feedback.warning.onSolidfeedback.warning.solid4.5:1
feedback.danger.onSolidfeedback.danger.solid4.5:1
feedback.info.onSolidfeedback.info.solid4.5:1
focus.ringsurface.canvassurface.defaultsurface.raisedsurface.selected3:1 (non-text)
border.strongsurface.canvassurface.default3:1 (non-text)

Ratios are the WCAG 2.x formula computed on the final 8-bit hex values, and they are never rounded up: 4.49:1 fails. Text needs 4.5:1; focus rings and input borders need 3:1 against the surfaces they sit on (1.4.11, non-text contrast).

Fuzz testing

Three hand-picked tenants prove little, so the solver is fuzzed with random brands — random primary, sometimes a random accent, random neutral, shape, type pair and density.

MetricValue
Random brands (seed 2026), each in light and dark1,000
Contrast checks (118 per brand)118,000
Passed118,000
Failed0
Pass rate100%
Solver adjustments per brand (min / median / max)0 / 4 / 7
Median generation time0.62 ms
p95 generation time0.95 ms
Reproduce with pnpm test:themes. Timing measured on Apple M1, Node v26.8.1; it varies by machine. Source: fuzz-report.md.

The closest calls the solver shipped in that run:

RequiredLowest shippedPair
3:13.0030:1light focus.ring #2e97a5 on surface.selected #daf4f8
4.5:14.5001:1light action.primary.fg #ffffff on action.primary.bg #6d7e29

The report also lists how often the solver stepped in and why. One rule in it isn’t WCAG: in dark mode, primary fills must reach 2.2:1 against the canvas so a near-black brand button doesn’t vanish. It is reported as a visibility adjustment, never as a WCAG pass.

Keyboard and screen readers

Components are built on React Aria Components (ADR-002). Focus management, roving focus in lists and grids, typeahead, focus trapping in dialogs and the ARIA patterns come from there; Syntara doesn’t re-implement them. Each component page lists its keyboard interactions, taken from its meta.json.

Built into every component

  • Visible focus. A 2px ring in --syntara-color-focus-ring, checked at 3:1 against the canvas, default and raised surfaces in every tenant.
  • Target size. Controls are at least 24 × 24 px (2.5.8), compact density included.
  • Not by colour alone. Status pairs colour with an icon and a word.
  • Reduced motion. Animations run only under prefers-reduced-motion: no-preference.
  • Names. Icon-only buttons require an aria-label in their types; decorative icons are hidden from assistive technology.
  • Direction. Right-to-left layouts and keyboard behaviour mirror from the locale — see RTL.

What isn’t proven yet

Contrast is guaranteed by the engine and fuzzed. Component-level checks — axe on every component state, per tenant and scheme — are part of Phase 2 and not reported here until they run.