fix(cli): warn when a theme overrides one side of a colour pair - #5281
fix(cli): warn when a theme overrides one side of a colour pair#5281cixzhang wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR Analysis Report📚 Storybook PreviewView Storybook for this PR 🧪 Sandbox PreviewView Sandbox for this PR No new or modified components detected. Bundle Size SummaryNo component packages changed. Accessibility AuditStatus: No accessibility violations detected. Generated by PR Enrichment workflow | Storybook | Sandbox | View full report |
8be0c03 to
897328a
Compare
897328a to
2689110
Compare
2689110 to
bf1cef3
Compare
expandColorScale holds generated text at 4.5:1 and control boundaries at 3:1 for any accent — but only for what it generated. Replace --color-accent in `tokens` and the --color-on-accent beside it still holds the value derived for the colour that is gone, so a theme ships below-AA text without anyone writing a bad colour, and nothing says so until someone measures pixels. theme build now measures ten pairs in both modes and names the token you forgot. Scoped to pairs with exactly one side hand-written: both sides set was a choice, not a silent void, and neither side set belongs to the defaults (#5019). Silent on all seven bundled themes; it caught a real 2.94:1 boundary in the shipped theme.template.ts, fixed here. Fixes #5014
bf1cef3 to
3c640b8
Compare
cixzhang
left a comment
There was a problem hiding this comment.
Approve (GitHub won't take a self-approval, so this is a comment).
The false-clean paths are fixed: raw thresholding, shared parsing, unmeasurable colours, and normal/check parity. The light/dark evidence is clean.
[Reviewed by Robohands]
|
Closing by product-direction decision. The checker is internally consistent and reviewed, but we do not want to add this new warning contract without a concrete gap asking for it. |
Closes #5014, the last unclaimed finding from the theme-authoring study (#5047).
expandColorScaleholds generated text at 4.5:1 and control boundaries at 3:1 for any accent — but only for what it generated. Replace--color-accentintokensand the--color-on-accentbeside it still holds the value derived for the colour that is no longer there. The theme ships below-AA text without anyone having written a bad colour, and nothing says so until someone measures rendered pixels.That is not hypothetical. In the study, a bedside medication-administration theme hand-tuned its dark accent, kept the generated on-accent, and rendered its primary button at 2.61:1 — measured off the DOM. Its author had run contrast math on the accent, against white, which is not what the theme renders. Every theme in that study that hand-wrote
--color-border-emphasizedalso fell under 3:1; the ones that left neutrals tocolor: {}did not.astryx theme buildnow resolves ten pairs — fill/label for accent and each status, text on body/surface/card, control boundary and focus ring on surface — in both colour modes, and warns with the token you forgot:A
warning, not anotice(where #5045 put the font advisory): a font a theme cannot load is the app's job by design, but this is the theme's own defect and fixable in the theme file.The comparison keeps its full precision until after the threshold decision and uses core's shared color parser. A valid value the parser cannot measure now warns explicitly instead of reading as clean.
theme build --checkreturns the same validation warnings as a normal build.The scope decision, which is the whole design
Only pairs with exactly one side hand-written. Two neighbours are deliberately silent:
I built the wider version first and ran it over the bundled themes: all seven warn, 2–9 findings each. Some are real (chocolate's white-on-
#FD0000error label is 4.06:1). But stone's is the one that settled it — its--color-on-erroris#58413eand its--color-erroris also#58413ein light mode, a literal 1:1. Not a bug: stone tunes--color-on-errorfor--color-error-muted(the badge fill) while--color-errorcarries its icon/border tone. A pair the defaults treat as fill+label, stone treats as two separate roles. The token contract itself is not settled, so a check that calls stone broken is asserting a semantics nobody agreed to. Narrowed to the one-sided case, all seven go silent and the real bug still fires.Worth a separate decision if you want it: whether
--color-on-*is defined against--color-*or--color-*-muted. Settle that and the both-sides case becomes checkable.It caught one of mine
The shipped
theme.template.tsoverrides--color-background-surfaceto#1F1F22and inherited a--color-border-emphasizedtoned for the surface it replaced — 2.94:1, just under the floor, in the file that teaches people. Fixed by authoring the pair, with the reason in a comment, so the template now demonstrates the discipline twice (it already did for on-accent).Visual evidence
Light mode is pixel-identical. In dark mode the boundary token moves from
rgb(102, 103, 116)torgb(111, 116, 124), raising contrast against the surface from 2.94:1 to 3.50:1. The enlarged swatch makes the color change judgeable; the real 2px control boundary is shown beneath it.Test plan
themeBuild(), including named colors and--checkparity.__inputTokensto know which side was hand-written, and the build has two loaders. jiti returns the DefinedTheme (has them); the legacy eval fallback, used whenever the theme file cannot be imported — the common case outside this repo, where@astryxdesign/core/themedoes not resolve — returns the raw input, which does not. On that path the check was silently dead.build.mjsnow re-mints from the input (whosetokensare exactly the hand-written set), and a test builds a plain-object theme to hold that path open.check:repo, eslint, and formatting pass.