fix(design): the tint that never painted - #543
Merged
Merged
Conversation
`bg-risk-critical/10` was written at 27 call sites across 13 files, and the design-system doc described that spelling as the way to tint a surface. It painted nothing. Tailwind implements an opacity modifier by splitting the colour into channels and re-emitting it with an alpha; the `--risk-*` tokens held a bare `var()` it cannot split, so it emitted no rule at all for the slashed class and every one of those elements fell back to transparent. The fix is in the mechanism, not the call sites: each risk token is now wrapped in a `color-mix()` carrying Tailwind's `<alpha-value>`, so the spelling everybody was already writing works — including variants a hand-written class cannot reach (`hover:bg-risk-low/20`). The severity hex in `index.css` stays the single source of the hue, charts still read it raw, and the four `.risk-tint-*` classes W16 added as a workaround for one caller are gone. Consequences of the tints existing for the first time: - The alpha scale is three steps (/10 surface, /20 emphasis, /40 border). The old call sites mixed six; preserving those distinctions would have been preserving differences nobody had ever seen. - Text on a tint needs a darker shade of the same hue — the severity hex measures 3.05:1 on its own tint. All five `--risk-*-foreground` tokens are declared now, taking the hexes `design-system.md` had already been listing as though they were in the stylesheet. - `statusTokenContrast.test.ts` re-measures against /20 over `--background`, the worst case, rather than the /15-over-white surface that never rendered. `riskTintOpacity.test.ts` compiles the stylesheet and greps it, because asserting the config's shape would only assert our theory of the fix, and source text is exactly what looked fine here for as long as the defect lived. Two gate repairs fell out of the work: - The a11y scan raced the routed subtree's 250ms fade-in. axe folds an ancestor's opacity into the foreground colour it measures, so a scan landing mid-fade read the muted grey as #7c7c84 and reported ~120 contrast violations that evaporated once the frame settled — a different set of screens each run. It now waits for finite animations to finish. - With that settled, the tints' arrival drops `projects-list` from 4 colour-contrast violations to 0. Frozen budget: 12 -> 8.
Seven of the eight representative screens move pixels now that the risk tints paint: the governance band's risk-score and build-gate tiles, the severity chips, the scan-log lines. Login is unchanged — it has no risk surface. Captured by `ui-gates.yml` with `update_baselines=true`, never locally: the runner's fonts and seed are what the gate compares against. The a11y baseline comes from the same run, and it disagrees with the local one. Locally `projects-list` reported its four colour-contrast violations gone; on CI they are still there. The local seed simply renders fewer rows than the CI seed, so the offending cells were not on screen — the frozen budget stays at 12. This is the third time a locally-derived baseline has claimed an improvement that did not exist, which is why the handoff says to take these from CI and why the number here is CI's.
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.
What was wrong
bg-risk-critical/10was written at 27 call sites across 13 files, anddesign-system.mddescribed that spelling as the way to tint a surface. It painted nothing.Tailwind implements an opacity modifier by splitting the colour into channels and re-emitting it with an alpha. The
--risk-*tokens held a barevar()it cannot split, so it did not emit a degraded rule — it emitted no rule at all for the slashed class, and every one of those elements fell back to transparent. No error, no warning, no visible diff in review. The only ways to see it were to compile the stylesheet and grep, or to read a computed style in a browser.The fix
In the mechanism, not the call sites. Each risk token is now wrapped in a
color-mix()carrying Tailwind's<alpha-value>, so the spelling everybody was already writing works — including variants a hand-written class cannot reach (hover:bg-risk-low/20). The severity hex inindex.cssstays the single source of the hue and charts still read it raw. W16's four.risk-tint-*workaround classes, which covered one caller while twenty-six others kept rendering transparent, are gone.Consequences of the tints existing for the first time:
/10surface,/20emphasis,/40border. The old call sites mixed six; preserving those distinctions would have meant preserving differences nobody had ever seen.--risk-*-foregroundtokens are declared. The severity hex measures 3.05:1 on its own tint, so text on a tint needs the darker shade. The three new ones are the hexesdesign-system.mdhad already been listing as though they were in the stylesheet — drift resolved toward the code.statusTokenContrast.test.tsre-measures against/20over--background(the worst case) instead of the/15-over-white surface that never rendered.riskTintOpacity.test.tscompiles the stylesheet and greps it. Asserting the config's shape would assert our theory of the fix; source text is exactly what looked fine here for as long as the defect lived.Two gate repairs fell out of the work
#7c7c84instead of#6c6c75and reported ~120 contrast violations that evaporated once the frame settled — a different set of screens each run.scan()now waits for finite animations to finish (infinite ones — skeleton pulse, spinners — are excluded; waiting on those never returns).projects-list's four colour-contrast violations; on CI they are still there, because the local seed renders fewer rows. The frozen budget stays at 12. Third occurrence of a locally-derived baseline claiming an improvement that did not exist.Verification
vitest run— 1584 passed, design suite 75 passedtsc -b --noEmitclean (tailwind.config.tsadded totsconfig.spec.json's include, since the new test imports it)npm run lint0 errors ·token:lint130 → 123 ·ko-style --fail-on S20 findingsui-gates.yml -f update_baselines=true(run 30455666100). Seven screens move; login is unchanged, having no risk surface.