Skip to content

fix(react): raise the built-in screens' default colours to WCAG 2.2 AA contrast - #238

Merged
Bccorb merged 2 commits into
mainfrom
fix/react-contrast-wcag-aa
Nov 5, 2026
Merged

Bccorb merged 2 commits into
mainfrom
fix/react-contrast-wcag-aa

Conversation

@Bccorb

@Bccorb Bccorb commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #230: the bundled @seamless-auth/react screens' default colours now meet WCAG 2.2 AA contrast. The OpenID Provider's hosted pages are built on these screens, and AA is their acceptance bar (ADR 0001 D7).

Tests first

66bb9a5 adds packages/react/tests/accessibility.test.tsx and fails on the old CSS (7 of 11 tests). It runs axe-core (wcag2a, wcag2aa, wcag21a, wcag21aa, wcag22aa) on the passkey enrollment and recovery code screens in their states. Because axe in jsdom reports color-contrast as incomplete rather than as a violation, a small helper (tests/support/contrast.ts) computes WCAG contrast from the real .module.css: it resolves var(--x, fallback) to the default, inherits color, composites backgrounds with alpha up the tree, and checks at rest and on hover (3:1 for large text and component borders). It reproduces the issue's axe figures (3.76, 3.99). The test commit was made with --no-verify, since the hook runs the suite.

Before and after (default theme)

Screen Pair Before After
Passkey .button 3.76 5.48
Passkey .button:hover 5.48 7.68
Passkey .secondary 3.57 5.20
Passkey .secondary:hover 3.18 4.54
Passkey .skip 2.41 6.36
Passkey .message.success 1.44 5.20
Passkey .message.error 2.62 6.14
Recovery .secondary (Copy, Download) 3.99 5.77
Recovery .button 3.67 5.16
accountSecurity .button / .secondary 3.67 / 3.99 5.16 / 5.77
verifyOTP .resend:hover 3.99 8.14
otpInput code box border (1.4.11) 1.94 5.78

The other bundled stylesheets already pass. Two unused stylesheets (mfaLogin.module.css, the unused verifyOTP .input) still have sub-3:1 input borders and are left alone.

No new custom properties

Every colour already goes through the documented --seamless-* custom properties, so a host can restyle these screens today. Only the fallback values changed; class names and structure are unchanged. The README's theming table lists the new defaults and the previously undocumented --seamless-accent-muted, and notes that the passkey panel is light while the other cards are dark, so shared tokens must read against both.

axe-core is a new root devDependency. lint, typecheck, format, build pass; Jest 872, Vitest 56.

…gainst WCAG 2.2 AA

Render both screens in each of their states and run axe-core with the
wcag2a, wcag2aa, wcag21a, wcag21aa and wcag22aa tags, asserting no
violations.

jsdom has no layout, so axe reports color-contrast as incomplete there
instead of measuring it. A small helper stands in for that rule: it reads
the screen's real stylesheet, resolves each custom property to its
fallback, and computes the WCAG relative luminance contrast of every
rendered text element against its composited background, at rest and on
hover. Disabled controls are left out, as WCAG and axe exempt them.

This fails on the current defaults, which is the bug.

Refs #230
…A contrast

The passkey enrollment and recovery code screens failed axe's
color-contrast rule on their default palette. Every pair now reaches
4.5:1 for text and 3:1 for control boundaries, at rest and on hover:

- passkey enrollment: primary button #047857 (hover #065f46), outlined
  button #047857 with a matching hover tint, skip link #1d4ed8, success
  and error messages #047857 and #b91c1c on the light panel
- recovery codes: primary button #2563eb (hover #1d4ed8), Copy and
  Download #60a5fa
- account security, which shared the recovery palette: primary button
  #2563eb, outlined buttons #60a5fa
- email and phone code entry: resend link hover #93c5fd, and a #9ca3af
  border on the code boxes so they stand out from the card

Only the fallbacks change. The colours already go through the documented
--seamless-* custom properties, so hosts can restyle them without
forking the CSS, and an app that sets those tokens sees no change. The
README token table lists the new defaults and the previously undocumented
--seamless-accent-muted.

Closes #230
@Bccorb
Bccorb merged commit 8062f12 into main Nov 5, 2026
5 checks passed
@Bccorb
Bccorb deleted the fix/react-contrast-wcag-aa branch November 5, 2026 01:03
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.

fix(react): color contrast below WCAG 2.2 AA on the passkey enrollment and recovery code screens

1 participant