Skip to content

Automated WCAG 2.1 AA a11y testing across all themes/modes#38

Draft
sjoerdbeentjes wants to merge 11 commits into
mainfrom
feat/a11y-testing
Draft

Automated WCAG 2.1 AA a11y testing across all themes/modes#38
sjoerdbeentjes wants to merge 11 commits into
mainfrom
feat/a11y-testing

Conversation

@sjoerdbeentjes

@sjoerdbeentjes sjoerdbeentjes commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Adds an automated WCAG 2.1 AA pipeline that sweeps every Storybook story across all themes and modes from @surfnet/tokens, in both React and Angular.

What

  • Shared config in @surfnet/storybook-config: browser-safe a11yParameters (merged into both previews, also scopes the addon-a11y panel) and a node-only runStoryA11yAudit behind a ./test-runner subpath so it never enters a preview bundle.
  • Builder-agnostic runner: @storybook/test-runner + axe-playwright against a served Storybook, so one setup covers React (Vite) and Angular (webpack). Each story is audited once per theme × mode (11 themes × 2 modes), writing a per-story JSON report.
  • Wiring: per-package test:a11y / test:a11y:ci scripts, a turbo task, a root script, and a CI step that runs report-only (continue-on-error) and uploads the JSON as an artifact.

Notes

  • CI is intentionally report-only until the token contrast backlog is triaged; flip to blocking by removing continue-on-error.

@sjoerdbeentjes sjoerdbeentjes force-pushed the feat/a11y-testing branch 5 times, most recently from b563368 to 871c744 Compare June 22, 2026 14:18
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

♿ Accessibility audit — WCAG 2.1 AA

⚠️ 41 finding(s) — 41 contrast (per theme·mode), 0 theme-independent · across 7 stories / 154 audited combinations. Report-only — does not block merge.

@surfnet/react — 41 finding(s)

Button — 41 finding(s)

As Link — 10 finding(s)
  • default
    • dark#04389a on #0a0a0a · ratio 1.92 — color-contrast (1.4.3)
  • groenvermogen-nkph2
    • light#84cc16 on #ffffff · ratio 1.97 — color-contrast (1.4.3)
  • studielink-aii
    • dark#2a3795 on #0a0a0a · ratio 1.95 — color-contrast (1.4.3)
  • surf-blue-purple
    • dark#04389a on #0a0a0a · ratio 1.92 — color-contrast (1.4.3)
  • surf-blue-turquoise
    • dark#04389a on #0a0a0a · ratio 1.92 — color-contrast (1.4.3)
  • surf-green
    • light#00d37f on #ffffff · ratio 1.97 — color-contrast (1.4.3)
  • surf-orange
    • light#ff883f on #ffffff · ratio 2.37 — color-contrast (1.4.3)
  • surf-purple
    • dark#420984 on #0a0a0a · ratio 1.51 — color-contrast (1.4.3)
  • surf-purple-orange
    • dark#420984 on #0a0a0a · ratio 1.51 — color-contrast (1.4.3)
  • surf-yellow
    • light#ffe500 on #ffffff · ratio 1.27 — color-contrast (1.4.3)
Default — 1 finding(s)
  • groenvermogen-nkph2
    • light#fafafa on #84cc16 · ratio 1.89 — color-contrast (1.4.3)
Sizes — 1 finding(s)
  • groenvermogen-nkph2
    • light#fafafa on #84cc16 · ratio 1.89 — color-contrast (1.4.3)
Variants — 20 finding(s)
  • default
    • light#000000 on #2e6ee5 · ratio 4.47 — color-contrast (1.4.3)
    • dark#04389a on #0a0a0a · ratio 1.92 — color-contrast (1.4.3)
  • groenvermogen-nkph2
    • light#fafafa on #84cc16 · ratio 1.89 — color-contrast (1.4.3)
  • shadcn-default
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
  • studielink-aii
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#2a3795 on #0a0a0a · ratio 1.95 — color-contrast (1.4.3)
  • surf-blue-purple
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #9d5de7 · ratio 4.07 — color-contrast (1.4.3)
  • surf-blue-turquoise
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #39bcb1 · ratio 2.33 — color-contrast (1.4.3)
  • surf-green
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #31e19b · ratio 1.69 — color-contrast (1.4.3)
  • surf-orange
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #ff883f · ratio 2.37 — color-contrast (1.4.3)
  • surf-purple
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #9d5de7 · ratio 4.07 — color-contrast (1.4.3)
  • surf-purple-orange
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #ff883f · ratio 2.37 — color-contrast (1.4.3)
  • surf-yellow
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #ffe84a · ratio 1.24 — color-contrast (1.4.3)
With Icon — 9 finding(s)
  • default
    • light#000000 on #2e6ee5 · ratio 4.47 — color-contrast (1.4.3)
  • groenvermogen-nkph2
    • light#fafafa on #84cc16 · ratio 1.89 — color-contrast (1.4.3)
  • surf-blue-purple
    • dark#ffffff on #9d5de7 · ratio 4.07 — color-contrast (1.4.3)
  • surf-blue-turquoise
    • dark#ffffff on #39bcb1 · ratio 2.33 — color-contrast (1.4.3)
  • surf-green
    • dark#ffffff on #31e19b · ratio 1.69 — color-contrast (1.4.3)
  • surf-orange
    • dark#ffffff on #ff883f · ratio 2.37 — color-contrast (1.4.3)
  • surf-purple
    • dark#ffffff on #9d5de7 · ratio 4.07 — color-contrast (1.4.3)
  • surf-purple-orange
    • dark#ffffff on #ff883f · ratio 2.37 — color-contrast (1.4.3)
  • surf-yellow
    • dark#ffffff on #ffe84a · ratio 1.24 — color-contrast (1.4.3)

Automated axe covers ~30–50% of WCAG 2.1 AA. Keyboard, screen-reader and reflow checks still need a manual pass. Full per-story JSON is in the run’s a11y-reports artifact.

@sjoerdbeentjes

sjoerdbeentjes commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator Author

Example report from earlier test run with forced violations:

♿ Accessibility audit — WCAG 2.1 AA

⚠️ 87 finding(s) — 82 contrast (per theme·mode), 5 theme-independent · across 10 stories / 220 audited combinations. Report-only — does not block merge.

@surfnet/react — 87 finding(s)

Button — 87 finding(s)

A11Y Violation Check — 3 finding(s)
  • Buttons must have discernible text — button-name (4.1.2) · all themes/modes
  • Images must have alternative text — image-alt (1.1.1) · all themes/modes
  • Links must have discernible text — link-name (4.1.2) · all themes/modes
As Link — 10 finding(s)
  • default
    • dark#04389a on #0a0a0a · ratio 1.92 — color-contrast (1.4.3)
  • groenvermogen-nkph2
    • light#84cc16 on #ffffff · ratio 1.97 — color-contrast (1.4.3)
  • studielink-aii
    • dark#2a3795 on #0a0a0a · ratio 1.95 — color-contrast (1.4.3)
  • surf-blue-purple
    • dark#04389a on #0a0a0a · ratio 1.92 — color-contrast (1.4.3)
  • surf-blue-turquoise
    • dark#04389a on #0a0a0a · ratio 1.92 — color-contrast (1.4.3)
  • surf-green
    • light#00d37f on #ffffff · ratio 1.97 — color-contrast (1.4.3)
  • surf-orange
    • light#ff883f on #ffffff · ratio 2.37 — color-contrast (1.4.3)
  • surf-purple
    • dark#420984 on #0a0a0a · ratio 1.51 — color-contrast (1.4.3)
  • surf-purple-orange
    • dark#420984 on #0a0a0a · ratio 1.51 — color-contrast (1.4.3)
  • surf-yellow
    • light#ffe500 on #ffffff · ratio 1.27 — color-contrast (1.4.3)
Default — 1 finding(s)
  • groenvermogen-nkph2
    • light#fafafa on #84cc16 · ratio 1.89 — color-contrast (1.4.3)
Link In Text Block Check — 22 finding(s)
  • default
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
  • groenvermogen-nkph2
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
  • shadcn-default
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
  • studielink-aii
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
  • surf-blue-purple
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
  • surf-blue-turquoise
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
  • surf-green
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
  • surf-orange
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
  • surf-purple
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
  • surf-purple-orange
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
  • surf-yellow
    • light — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
    • dark — Links must be distinguishable without relying on color — link-in-text-block (1.4.1)
Mixed Violations Check — 21 finding(s)
  • Buttons must have discernible text — button-name (4.1.2) · all themes/modes
  • Images must have alternative text — image-alt (1.1.1) · all themes/modes
  • default
    • light#d6d3d1 on #ffffff · ratio 1.48 — color-contrast (1.4.3)
    • dark#232323 on #0a0a0a · ratio 1.25 — color-contrast (1.4.3)
  • groenvermogen-nkph2
    • light#e5e5e5 on #ffffff · ratio 1.25 — color-contrast (1.4.3)
  • shadcn-default
    • light#e5e5e5 on #ffffff · ratio 1.25 — color-contrast (1.4.3)
  • studielink-aii
    • light#e5e5e5 on #ffffff · ratio 1.25 — color-contrast (1.4.3)
  • surf-blue-purple
    • light#d6d3d1 on #ffffff · ratio 1.48 — color-contrast (1.4.3)
    • dark#232323 on #0a0a0a · ratio 1.25 — color-contrast (1.4.3)
  • surf-blue-turquoise
    • light#d6d3d1 on #ffffff · ratio 1.48 — color-contrast (1.4.3)
    • dark#232323 on #0a0a0a · ratio 1.25 — color-contrast (1.4.3)
  • surf-green
    • light#d6d3d1 on #ffffff · ratio 1.48 — color-contrast (1.4.3)
    • dark#232323 on #0a0a0a · ratio 1.25 — color-contrast (1.4.3)
  • surf-orange
    • light#d6d3d1 on #ffffff · ratio 1.48 — color-contrast (1.4.3)
    • dark#232323 on #0a0a0a · ratio 1.25 — color-contrast (1.4.3)
  • surf-purple
    • light#d6d3d1 on #ffffff · ratio 1.48 — color-contrast (1.4.3)
    • dark#232323 on #0a0a0a · ratio 1.25 — color-contrast (1.4.3)
  • surf-purple-orange
    • light#d6d3d1 on #ffffff · ratio 1.48 — color-contrast (1.4.3)
    • dark#232323 on #0a0a0a · ratio 1.25 — color-contrast (1.4.3)
  • surf-yellow
    • light#d6d3d1 on #ffffff · ratio 1.48 — color-contrast (1.4.3)
    • dark#232323 on #0a0a0a · ratio 1.25 — color-contrast (1.4.3)
Sizes — 1 finding(s)
  • groenvermogen-nkph2
    • light#fafafa on #84cc16 · ratio 1.89 — color-contrast (1.4.3)
Variants — 20 finding(s)
  • default
    • light#000000 on #2e6ee5 · ratio 4.47 — color-contrast (1.4.3)
    • dark#04389a on #0a0a0a · ratio 1.92 — color-contrast (1.4.3)
  • groenvermogen-nkph2
    • light#fafafa on #84cc16 · ratio 1.89 — color-contrast (1.4.3)
  • shadcn-default
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
  • studielink-aii
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#2a3795 on #0a0a0a · ratio 1.95 — color-contrast (1.4.3)
  • surf-blue-purple
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #9d5de7 · ratio 4.07 — color-contrast (1.4.3)
  • surf-blue-turquoise
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #39bcb1 · ratio 2.33 — color-contrast (1.4.3)
  • surf-green
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #31e19b · ratio 1.69 — color-contrast (1.4.3)
  • surf-orange
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #ff883f · ratio 2.37 — color-contrast (1.4.3)
  • surf-purple
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #9d5de7 · ratio 4.07 — color-contrast (1.4.3)
  • surf-purple-orange
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #ff883f · ratio 2.37 — color-contrast (1.4.3)
  • surf-yellow
    • light#dc2626 on #fce9e9 · ratio 4.13 — color-contrast (1.4.3)
    • dark#ffffff on #ffe84a · ratio 1.24 — color-contrast (1.4.3)
With Icon — 9 finding(s)
  • default
    • light#000000 on #2e6ee5 · ratio 4.47 — color-contrast (1.4.3)
  • groenvermogen-nkph2
    • light#fafafa on #84cc16 · ratio 1.89 — color-contrast (1.4.3)
  • surf-blue-purple
    • dark#ffffff on #9d5de7 · ratio 4.07 — color-contrast (1.4.3)
  • surf-blue-turquoise
    • dark#ffffff on #39bcb1 · ratio 2.33 — color-contrast (1.4.3)
  • surf-green
    • dark#ffffff on #31e19b · ratio 1.69 — color-contrast (1.4.3)
  • surf-orange
    • dark#ffffff on #ff883f · ratio 2.37 — color-contrast (1.4.3)
  • surf-purple
    • dark#ffffff on #9d5de7 · ratio 4.07 — color-contrast (1.4.3)
  • surf-purple-orange
    • dark#ffffff on #ff883f · ratio 2.37 — color-contrast (1.4.3)
  • surf-yellow
    • dark#ffffff on #ffe84a · ratio 1.24 — color-contrast (1.4.3)

Automated axe covers ~30–50% of WCAG 2.1 AA. Keyboard, screen-reader and reflow checks still need a manual pass. Full per-story JSON is in the run’s a11y-reports artifact.

@sjoerdbeentjes sjoerdbeentjes linked an issue Jun 23, 2026 that may be closed by this pull request
Dot-prefix the per-package report output (and the generated PR-comment
markdown) so a single .gitignore entry covers them and they read as
generated/hidden. Updates the audit default dir, the comment script's
read/write paths, turbo outputs, the CI artifact/comment paths, and
.gitignore.
Replace the flat findings table with a nested layout (component heading,
theme bullet, mode sub-bullet with the contrast detail) so reviewers can
scan by component and drill into a theme. Cap body length under GitHub's
comment size limit.
Show the component (e.g. Button) as an h4 and each story variation (e.g.
Variants, As Link) as an h5 beneath it, instead of merging them into one
"Button / Variants" heading. Theme/mode nesting is unchanged.
Only contrast rules (color-contrast, link-in-text-block) depend on the
theme/mode, so report those per theme·mode as before. DOM-structural
rules (missing alt, button/link name, ARIA, etc.) are identical across
themes — collapse each to a single finding per story variation, flagged
'all themes/modes', instead of repeating it for every combination.
Disable CSS transitions/animations and await fonts before the axe sweep so
contrast reads settled colors and correct large-text thresholds. Condense the
verbose comments added across the a11y tooling.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WCAG 2.1 AA-check op alle PoC-componenten

1 participant