fix(a11y): accent-as-text meets WCAG AA + contrast ratchet#82
Merged
Conversation
…xt tail) The prior a11y sweep bumped muted text to AA but deferred accent-coloured TEXT as a 'brand decision' — it rendered at ~3.3–4.3:1 (under 4.5:1) in several light themes. Resolves it WITHOUT touching any brand fill: a dedicated `color.accent.onSurface` token (accent tuned for text) that = the existing `accent.strong` in the four failing light themes and = `accent.default` elsewhere (nearly free — every theme's safe text variant already existed). The brand accent (fills, borders, focus rings, button faces) is unchanged. - **New token** `accent.onSurface` → `--color-accent-on-surface` in all 12 themes (tokens.ts + themes.ts + the CANONICAL_TOKEN_NAMES mirror). `text.link` fixed in solar/mint/rose (was = the too-light accent.default). - **22 accent-as-text CSS sites** repointed shell/sdk/apps (settings, marketplace, membership, help, bookmarks, theme-editor, …) → `--color-accent-on-surface`; fills / borders / focus / icon glyphs (3:1 bar) left on accent-default/strong. - **Contrast engine fix** (token-set-contrast.ts): a translucent background token (e.g. `surface.default` = rgba(…,0.05)) was evaluated on its raw RGB — treating a 5%-white overlay as pure white and misreporting ~1:1. Now composited over `--color-background-primary` first. + two `accent-on-surface` pairs. - **CI ratchet** (themes-contrast.test.ts): runs lintTokenContrast over every built-in theme so the deferred 'decision' is now an ENFORCED invariant — a regression fails here. The 12 themes were NEVER linted before (the engine only ran on third-party theme-packs); this also surfaced the translucent-surface bug. KNOWN-DEFERRED (tracked in the ratchet): `accent-text-on-accent` — white text ON the light accent FILL (membership badges) fails AA in 3 light themes. This is the INVERSE concern (text on a fill, not accent as text) and needs its own per-theme `accent.onFill` token — a global accent.default→strong swap can't fix it (breaks nord/aurora's dark accent.text on the darker strong). Its follow-up is separate; the ratchet asserts the exact known set so a fix trips it + the entry gets removed. +13 contrast tests; 526 tokens/sdk-types tests green; tsc + lint (incl the phantom-CSS-token checker) clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the deferred accent-text contrast tail. The prior a11y sweep fixed muted text but left accent-coloured TEXT at ~3.3–4.3:1 (under AA 4.5:1) in several light themes as a 'brand decision'. This resolves it without changing any brand fill.
The resolution (conservative — no brand-fill change)
A dedicated
color.accent.onSurfacetoken (accent tuned for text) that = the theme's existingaccent.strongin the 4 failing light themes and =accent.defaulteverywhere else — nearly free, because each theme's AA-safe text variant already existed. The brand accent (fills, borders, focus rings, button faces) is untouched.CANONICAL_TOKEN_NAMESmirror);text.linkfixed in solar/mint/rose.--color-accent-on-surface; fills, borders, focus, and icon glyphs (3:1 bar) deliberately left onaccent-default/strong.token-set-contrast.ts): a translucent background token (e.g.surface.default=rgba(…,0.05)) was scored on its raw RGB — treating a 5%-white overlay as pure white (~1:1). Now composited over--color-background-primaryfirst.themes-contrast.test.ts): runslintTokenContrastover every built-in theme — the 12 themes were never linted before (the engine only ran on third-party theme-packs, which is how the translucent-surface bug hid). The deferred decision is now an enforced invariant.Known-deferred (tracked in the ratchet, not silently skipped)
accent-text-on-accent— white text ON the light accent fill (membership badges) fails AA in 3 light themes. This is the INVERSE concern (text on a fill, not accent as text) and needs its own per-themeaccent.onFilltoken — a globalaccent.default→strongswap can't fix it (breaks nord/aurora's darkaccent.texton the darker strong). The ratchet asserts the exact known-failing set, so an accidental fix trips the test and prompts removing the entry. Follow-up rung.Verified
+13 contrast tests; 526 tokens/sdk-types tests green; full suite 14165 passed (the 10 failed files are the known worktree Electron-install collection artifact — 0 test-case failures);
tsc+bun run lint(incl. the phantom-CSS-token checker) clean.🤖 Generated with Claude Code