Accessibility

Spark's WCAG 2.2 AA conformance target, what is automatically verified, and what a consuming portal must still provide.

Spark targets WCAG 2.2 AA. This page states what that means in practice, what is verified automatically on every change, and — importantly — what it does not cover.

What Spark is, for conformance purposes

Spark is a component library, not a website. Conformance obligations for a customer portal (including BFSG / EN 301 549 duties) land on the portal operating it, not on this package. Spark ships no user-facing strings — not even aria-label="Close", because portals are multi-language and the library cannot translate.

So a green Spark build does not make a portal conformant. These remain the consumer's:

The portal must provideWhy Spark cannot
Accessible names for icon-only controlsSpark ships no strings; names arrive as props
lang on the documentOutside any component
Page titles, heading orderDetermined by page composition
Skip links / landmark structureDetermined by page composition
Focus management across route changesOwned by the portal's router

What is verified automatically

Verification is split by what each layer can actually observe. jsdom has no layout engine and no computed colour, so contrast, geometry and motion are invisible to it — which is why a browser layer exists at all.

LayerVerifiesRuns
Palette (pure functions)contrast ratios for arbitrary customer brand hexes — 1320 comparisons over a fixed 165-hex sample gridevery test run
Semantic (jsdom + axe)roles, accessible names, hidden decoration, disabled semanticsevery test run
Rendered (Chromium + Playwright)contrast, target size, focus ring, reduced motion, focus-not-obscured26 state pages per run
Static (oxlint jsx-a11y)keyboard operability — an element taking a click without a role or key handlerevery push

Automated tooling detects roughly a third of WCAG failures. It cannot judge whether an accessible name is meaningful or a focus order logical, so each component's documentation carries a human verdict alongside the automated result.

Two different target-size numbers

  • 24×24px (2.5.8 Target Size (Minimum), AA) — the conformance bar. Fails the build.
  • 44×44px (2.5.5 Target Size (Enhanced), AAA) — Spark's house target. Reported, never blocking.

A control between the two is above the bar and below our intent. Findings in that band are listed in each run's report artifact — tracked, never blocking.

Known deviations

Recorded rather than hidden. Each is gated by a baseline that can only shrink: a new violation fails the build, and a fixed one must be removed from the baseline explicitly.

Total baselined at generation time: 3727 instances across 8 rules.

RuleComponents affectedInstancesNote
button-name51344Demo authoring: docs demos render bare controls with no accessible name. A defect in the examples, which readers copy — not in the components.
color-contrast141063Concentrated in data-style × data-high-contrast combinations — the fill-leakage trap the theming contract §123 predicts.
focus-ring-contrast12900These cases apply no Spark focus ring at all and fall back to the browser’s outline: auto, whose appearance is platform-dependent — a contract §12 gap in those components and demos. (The scale-level cause that used to dominate this rule — the focus token sitting on a step solved to be a solid fill, which measured 2.63:1 on the dark canvas for the default accent and worse for warm ones — was fixed on 2026-08-11 by moving --color-focus-root to --spark-11, clearing 400 baselined keys.)
aria-progressbar-name2168Progress elements in demos rendered without a name.
scrollable-region-focusable3120A scroll container that is not keyboard reachable. Note ScrollArea’s viewport is deliberately not focusable — see breaking-changes.
target-size-24360Controls under the 24×24px AA floor (2.5.8).
target-size148Reported by axe’s own target-size rule.
label124

Two further deviations are behavioural rather than measurable by the sweep:

  • 2.5.7 Dragging Movements — zoomable charts. LineChart / PowerHistoryChart can only zoom in by drag or pinch; the reset control only zooms out. 2.5.1 is met (drag is a single-pointer alternative to the pinch) but 2.5.7 needs a non-dragging path, and "dragging is essential" does not apply.
  • Coverage debt. 30 components still lack an automated a11y test and 34 documentation pages still lack their ## Accessibility section. Both are gated by allowlists that can only shrink, so this number cannot grow.

Reporting an accessibility problem

Open an issue against this repository. Include the component, the theme state (appearance, style, high-contrast), and the assistive technology used — the state matters, since several findings appear only in specific data-style combinations.

On this page