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 provide | Why Spark cannot |
|---|---|
| Accessible names for icon-only controls | Spark ships no strings; names arrive as props |
lang on the document | Outside any component |
| Page titles, heading order | Determined by page composition |
| Skip links / landmark structure | Determined by page composition |
| Focus management across route changes | Owned 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.
| Layer | Verifies | Runs |
|---|---|---|
| Palette (pure functions) | contrast ratios for arbitrary customer brand hexes — 1320 comparisons over a fixed 165-hex sample grid | every test run |
| Semantic (jsdom + axe) | roles, accessible names, hidden decoration, disabled semantics | every test run |
| Rendered (Chromium + Playwright) | contrast, target size, focus ring, reduced motion, focus-not-obscured | 26 state pages per run |
Static (oxlint jsx-a11y) | keyboard operability — an element taking a click without a role or key handler | every 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.
| Rule | Components affected | Instances | Note |
|---|---|---|---|
button-name | 5 | 1344 | Demo authoring: docs demos render bare controls with no accessible name. A defect in the examples, which readers copy — not in the components. |
color-contrast | 14 | 1063 | Concentrated in data-style × data-high-contrast combinations — the fill-leakage trap the theming contract §123 predicts. |
focus-ring-contrast | 12 | 900 | These 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-name | 2 | 168 | Progress elements in demos rendered without a name. |
scrollable-region-focusable | 3 | 120 | A scroll container that is not keyboard reachable. Note ScrollArea’s viewport is deliberately not focusable — see breaking-changes. |
target-size-24 | 3 | 60 | Controls under the 24×24px AA floor (2.5.8). |
target-size | 1 | 48 | Reported by axe’s own target-size rule. |
label | 1 | 24 | — |
Two further deviations are behavioural rather than measurable by the sweep:
- 2.5.7 Dragging Movements — zoomable charts.
LineChart/PowerHistoryChartcan 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
## Accessibilitysection. 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.