a11y(1.4.11): focus rings — lighten dark-mode --color-border-focus-info to indigo.400 so ring-primary/70 meets 3:1 against surface-primary#3478
Open
canvanooo wants to merge 1 commit into
Conversation
…fo to indigo.400 so ring-primary/70 meets 3:1 against surface-primary The ring-primary/70 pattern (focus-visible:ring-2 focus-visible:ring-primary/70) is used on 34 sites across 25 files in src/lib/holocene/ and src/lib/components/ — Button, Link, Tab, Input, Textarea, Combobox, Checkbox, RadioInput, FileInput, ToggleSwitch, Pill, navigation primitives, and more. ring-primary maps to --color-border-focus-info, which was indigo.600 (#444CE7) in both light and dark modes. At 70% alpha composited over the dark-mode surface (--color-surface-primary = #141414), the ring renders as approximately #273074. Contrast ratio against the surrounding canvas: ~1.92:1 — below the SC 1.4.11 Non-text Contrast floor of 3:1. Light mode passes at 3.58:1; the failure is dark-mode only. This shifts the dark-mode value of --color-border-focus-info to indigo.400 (#8098F9), which at 70% over the dark surface composites to ~#5A6BAF, giving ~3.78:1 against the surrounding canvas. Light mode is byte-identical (still indigo.600). The change is scoped to one CSS variable assignment because the token flows exclusively through Tailwind's ring-primary utility (verified: only two references in src/ — the variable definition itself and the ringColor.primary mapping in plugin.ts). The fix is intentionally token-level rather than class-level (Option A in the fix doc) for blast radius and design-judgment reasons: Option A would touch 34 consumer sites and change light-mode rendering too. The asymmetric light/dark token pattern is consistent with neighboring tokens like --color-border-danger (red.500 light / red.400 dark). Cross-walks 2.4.7 Focus Visible (Level AA). Cascades to cloud-ui-main via the @temporalio/ui tarball on next repack. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
Contributor
|
12 tasks
ardiewen
approved these changes
May 29, 2026
13 tasks
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.
Description & motivation 💭
The Tailwind class pattern
focus-visible:ring-2 focus-visible:ring-primary/70is used on 34 sites across 25 files insrc/lib/holocene/andsrc/lib/components/— Button (secondary, ghost), Link, Tab, Input, Textarea, Combobox, Checkbox, RadioInput, FileInput, ToggleSwitch, Pill, navigation primitives, and more.ring-primaryresolves to--color-border-focus-info, which was set toindigo.600(#444CE7) in both light and dark modes.At 70% alpha composited over the dark-mode surface (
--color-surface-primary=#141414), the ring renders as approximately#273074. Contrast ratio against the surrounding canvas: ~1.92:1 — below the WCAG 1.4.11 Non-text Contrast floor of 3:1. Light mode passes at 3.58:1; the failure is dark-mode only, but it affects every focus-visible ring rendered there.This PR shifts the dark-mode value of
--color-border-focus-infofromindigo.600toindigo.400(#8098F9). At 70% alpha composited over the dark surface that resolves to~#5A6BAF, giving ~3.78:1 against the surrounding canvas. Light mode is byte-identical (stillindigo.600).The diff:
'--color-border-focus-info': { light: 'indigo.600', - dark: 'indigo.600', + dark: 'indigo.400', },One line in
src/lib/theme/variables.ts:215.Why a token-level change rather than dropping
/70from every consumer. The audit fix doc offered two viable options. Option A: find-replacering-primary/70→ring-primaryacross 34 sites. Simpler conceptually, but touches 25 files in the design-system layer, changes light-mode ring intensity on every consumer (3.58 → 5.27), and the resulting dark-mode contrast is 3.12:1 — sub-pixel rendering variance away from 3:1. Option B (this PR): change one CSS variable's dark-mode value. Light-mode rendering pixel-identical. Dark-mode contrast 3.78:1 with margin. Token blast radius is minimal —--color-border-focus-infohas exactly two references insrc/: its own definition invariables.ts:213and theringColor.primarymapping intheme/plugin.ts:221. Nothing else (no outline, no border, no text) consumes it. The asymmetric light/dark token pattern is also consistent with neighboring tokens —--color-border-dangeris alreadyred.500light /red.400dark in this same file.Composes with PR #3477. That PR adds
ring-offsetto the Checkbox:checkedstate to fix the indigo-on-indigo problem (addresses where the ring sits). This PR changes the dark-mode token color (addresses what color it is). Together they bring the dark-mode focused-and-checked checkbox to ~8.43:1 ring-vs-canvas contrast. They're independent and can land in either order.Screenshots (if applicable) 📸
Screenshots to be captured by the PR author from the Vercel preview build (link appears once the
Vercelcheck passes). Include light-mode and dark-mode captures for each affected primitive.Design Considerations 🎨
Token-color shift in one mode only. The dark-mode focus ring becomes a lighter indigo (slightly cooler / less saturated). Design team may want to confirm the perceptual change is acceptable — it's necessary for WCAG AA contrast at the current
/70opacity. If the team prefers to keepindigo.600everywhere, the alternative is Option A (drop/70opacity), with the tradeoffs documented in the rationale above.Testing 🧪
How was this tested 👻
Automated checks performed locally on
a11y/1.4.11-focus-ring-dark-mode-opacitybefore pushing:pnpm lint— 0 errorspnpm check(svelte-check) — 0 errors (84 pre-existing warnings repo-wide, none introduced by this change)pnpm test -- --run— 142 test files / 2023 tests passedlint-staged: eslint --fix, prettier --write, stylelint --fix) clean--color-border-focus-infois consumed by exactly one Tailwind utility (ring-primary) — no other Tailwind utility, no raw CSS referenceNo new automated tests added because the change is a single CSS-variable value; existing Chromatic visual-regression coverage exercises every primitive's focused state in both modes and will produce expected dark-mode-only diffs to accept as new baseline. Manual visual testing in Storybook is the responsibility of the PR author after the preview deploy is ready (see "Steps for others to test" below).
Steps for others to test: 🚶🏽♂️🚶🏽♀️
pnpm installif needed.pnpm stories:dev— open Storybook at http://localhost:6006.main(no visible change in light mode).#141414should report ≥ 3:1.Checklists
Draft Checklist
src/lib/theme/variables.ts--color-border-focus-infoconsumer audit: 2 references confirmed, both internal to the theme layerpnpm lint,pnpm check,pnpm test -- --runall passMerge Checklist
indigo.600→indigo.400in dark mode only)Issue(s) closed
A11y-Audit-Ref: 1.4.11-focus-ring-dark-mode-opacity
Closes the focus-ring dark-mode opacity defect documented in the May 2026 audit (manifest bucket 1, severity serious, scope ui-main). See
scripts/a11y/manifest.ymlfor the canonical entry.Docs
Any docs updates needed?
No external docs (
docs.temporal.io) need updating — this is a design-system internal token-value change with no surface change in light mode and a subtle color shift in dark mode that doesn't introduce new APIs or behaviors. If the team maintains an internal design-system change-log, an entry noting "dark-mode focus ring color shifted from indigo.600 to indigo.400 for WCAG AA contrast" would be appropriate.🤖 Generated with Claude Code