a11y(1.4.11): introduce border-input token so form-input boundaries meet 3:1 against surface-primary in both modes#3480
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
|
…dary meets 3:1 against surface-primary The --color-border-subtle token at src/lib/theme/variables.ts:185-188 resolved to slate.200 (#AEBED9) in light mode and slate.800 (#273860) in dark mode. Against --color-surface-primary (#FFFFFF light / #000000 dark), those compute to 1.89:1 and 1.83:1 — both below the WCAG SC 1.4.11 Non-text Contrast 3:1 floor. border-subtle is used at 106 sites across 76 files in src/lib/holocene and src/lib/components, on every form-input outer wrapper (Input, Textarea, NumberInput, ChipInput, Combobox, DurationInput, FileInput, RangeInput) AND on the Button "secondary" variant (which Select and MenuButton route through) AND on cards, panels, table-row dividers, nav dividers, and other component-boundary surfaces. All of them currently fail the same SC. This shifts the token to slate.400 (#7C8FB1) light / slate.500 (#667CA1) dark, giving: - light: #7C8FB1 vs #FFFFFF → 3.27:1 ✓ - dark: #667CA1 vs #000000 → ~4.96:1 ✓ The change is a single-line token-value update with no consumer-side modifications and no new token surface. Every consumer of border-subtle improves to ≥ 3:1 atomically. Visual tradeoff: borders that were previously near-invisible become clearly visible across the design system — cards more "carded", tables more grid-like, nav dividers more pronounced. This was the design's intent (these borders exist to demarcate boundaries); the previous values rendered the intent invisibly. Design-team sign-off on the weight shift is in the PR's Merge Checklist. The audit team's verification doc (audit-output/issues/1.4.11-non-text-contrast-verification.md) identified border-subtle as failing universally; the audit-output fix doc (1.4.11-input-border-contrast.md) recommended this exact one-line token darken as Option A. This PR implements that recommendation. 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>
9f56796 to
030b8d1
Compare
|
Deferring this fix pending an upcoming color refresh that will re-token border and surface semantics across the design system. Background:
Branch Audit reference: |
Description & motivation 💭
The
--color-border-subtletoken atsrc/lib/theme/variables.ts:185-188resolved to slate.200 (#AEBED9) in light mode and slate.800 (#273860) in dark mode. Against--color-surface-primary(#FFFFFF/#000000), those compute to:#AEBED9(slate.200)#FFFFFF#273860(slate.800)#000000Both below the WCAG 1.4.11 Non-text Contrast 3 : 1 floor.
border-subtleis used at 106 sites across 76 files insrc/lib/holoceneandsrc/lib/components, including every form-input outer wrapper (Input, Textarea, NumberInput, ChipInput, Combobox, DurationInput, FileInput, RangeInput), theButton variant="secondary"rendering (which Select and MenuButton route through), and a long tail of cards, panels, table-row dividers, nav dividers, and other component-boundary surfaces. All of them currently fail the same SC.This PR shifts the token to slate.400 (
#7C8FB1) light / slate.500 (#667CA1) dark:'--color-border-subtle': { - light: 'slate.200', - dark: 'slate.800', + light: 'slate.400', + dark: 'slate.500', },#7C8FB1(slate.400)#FFFFFF#667CA1(slate.500)#000000One line in
src/lib/theme/variables.ts:186-187. No consumer-side changes, no new token surface, no plugin map updates. Every consumer ofborder-subtleimproves to ≥ 3 : 1 atomically.Why a token-level darken rather than a selective swap or new token. Earlier iterations of this branch (history rewritten via force-push, single draft PR) introduced a new
border-inputtoken used selectively on form-input primitives, in an attempt to keepborder-subtleunchanged for non-input consumers. That approach hit two scope-expansion rounds in succession: a missed grep added DurationInput / FileInput / RangeInput, then Select surfaced via the Buttonsecondaryvariant. Each round was the symptom of having picked the wrong abstraction layer. The verification doc (audit-output/issues/1.4.11-non-text-contrast-verification.md) identifiedborder-subtleas failing universally, and the audit fix doc (1.4.11-input-border-contrast.md) recommended this exact one-line token darken as Option A from the start. Implementing Option A directly closes the entire SC failure in one change rather than chasing consumers individually.Cascade.
border-subtleis part of the@temporalio/uidesign-token surface; the change cascades to cloud-ui-main on next tarball repack.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 form inputs (Input / Textarea / Select / etc.) AND for representative non-input surfaces (cards on the workflow detail page, tables on the workflow list page, the breadcrumb / nav dividers) to show the universal effect.Design Considerations 🎨
Token-level visual change across ~106 consumers in both modes. The biggest perceptual delta is in dark mode, where borders go from nearly-invisible (
slate.800on#000000) to clearly defined (slate.500on#000000). In light mode, borders go fromslate.200(a very light blue-gray) toslate.400(a medium blue-gray) — visible but not jarring.What gets visibly more present after the change:
variant="secondary"— Cancel, Save, "Add filter", modal action buttons, MenuButton triggers. Boundaries now clearly visible.This is the design's existing intent (these borders exist to demarcate boundaries); the previous values rendered the intent invisibly. Design team should confirm the heavier rendering is acceptable; if not, the conversation is "do we want decorative separators below 3 : 1?" rather than "do we want this fix?" — the contrast fix is required for accessibility regardless.
Testing 🧪
How was this tested 👻
Automated checks performed locally on
a11y/1.4.11-input-border-contrastbefore 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 passlint-staged: eslint --fix, prettier --write, stylelint --fix) clean on the modified fileManual visual testing in Storybook and the live app 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.pnpm dev. Walk a few high-traffic pages:/namespaces/default/standalone-activities/start-activity-form) — confirm timeout DurationInputs, payload FileInput, Combobox selectors all render with the new border./namespaces/default/schedules/create) — confirm interval offset Select picklist and other form inputs./namespaces/default/workflows) — confirm filter Combobox at top + table-row separators below.border-subtleconsumer. Treat as expected; accept as new baseline.Checklists
Draft Checklist
src/lib/theme/variables.ts:186-187pnpm lint,pnpm check,pnpm test -- --runall passMerge Checklist
border-subtle)Issue(s) closed
A11y-Audit-Ref: 1.4.11-input-border-contrast
Closes the border-subtle contrast 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 API surface change. If the team maintains an internal design-system changelog, an entry noting "darkened--color-border-subtlefrom slate.200/slate.800 to slate.400/slate.500 for WCAG AA contrast" would be appropriate.🤖 Generated with Claude Code