Skip to content

fix(ui): seventeen error messages that were never red, and a test so it cannot happen again - #20

Merged
meocong merged 1 commit into
masterfrom
ui/dead-styles
Aug 14, 2026
Merged

fix(ui): seventeen error messages that were never red, and a test so it cannot happen again#20
meocong merged 1 commit into
masterfrom
ui/dead-styles

Conversation

@meocong

@meocong meocong commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

--color-danger was never defined

Seventeen places say text-danger or bg-danger-soft — the failed import, the calendar that would not fetch, the model that would not download, the agent that refused, every role="alert" in onboarding. Tailwind v4 emits a utility only when it can resolve the token behind it, and emitting nothing is not an error: no rule, no warning, and no visible sign except that the alert is the same colour as the sentence above it. .text-danger appears zero times in the built stylesheet.

Same fault in a second place: border-ok/30 bg-ok-soft on the onboarding success panel, against a --color-ok that does not exist either. Pointed at the accent, which is what that panel meant — a fifth green would have been a fifth thing to keep in step.

The test

theme.test.ts checked that what the theme declares survives into the bundle. It now also checks the other direction: every text-, bg-, border- and ring- colour the interface names was declared somewhere. Nothing else could have caught this — the type checker sees a string, the linter sees a string, and the screenshot audit was happy, because grey text on the page background is a perfectly legible contrast pair.

Tailwind overloads these prefixes, so a name is resolved against --color-* first, then the size, radius, font and shadow scales (text-meta is a size, rounded-card a radius), then by peeling off an edge or an offset (border-l-accent, ring-offset-bg). Comments and arbitrary values are stripped before scanning: a doc comment explaining why bg-soft was rejected must not read as a use of it, and bg-[linear-gradient(…var(--color-bg-elevated)…)] is CSS passing through rather than a utility.

Two empty screens that read as broken

The voice book put "GIỌNG ĐÃ BIẾT" at the top of a blank pane with "no voices yet" four hundred pixels below — two answers to the same question with a void between them. The heading now only appears over a list.

The calendar centred its empty state in what was left under the subscribe form, putting the explanation of why there is nothing three hundred pixels beneath the box that fixes it. That screen no longer asks for full, which means "this is the whole screen" and was never true here.

Capping full centrally was tried first and reverted: it made every screen where the empty state genuinely is the content fail e2e/density.mjs. The comment in Empty now says so, because the next person to notice the calendar gap will reach for the same fix.

Also: the notes pane's empty state offers the button that fixes it. It was a dead end whose only way out was a 288px column away in the corner of another pane.

Verification

All 18 browser suites green, 298 unit tests, 44 screenshots re-shot in both schemes — no overflow, contrast AA everywhere.

🤖 Generated with Claude Code

…it cannot happen again

`--color-danger` was never defined. Seventeen places say `text-danger` or `bg-danger-soft`
— the failed import, the calendar that would not fetch, the model that would not download,
the agent that refused, every `role="alert"` in onboarding — and Tailwind v4 emits a utility
only when it can resolve the token behind it. Emitting nothing is not an error: no rule, no
warning, and no visible sign except that the alert is the same colour as the sentence above
it. `.text-danger` appears zero times in the built stylesheet.

The same fault in a second place: `border-ok/30 bg-ok-soft` on the onboarding success panel,
against a `--color-ok` that does not exist either. Pointed at the accent, which is what that
panel meant; a fifth green would have been a fifth thing to keep in step.

## The test

`theme.test.ts` checked that what the theme *declares* survives into the bundle. It now also
checks the other direction — that every `text-`, `bg-`, `border-` and `ring-` colour the
interface *names* was ever declared. Nothing else could have caught this. The type checker
sees a string, the linter sees a string, and the screenshot audit is happy: grey text on the
page background is a perfectly legible contrast pair, so it scored those alerts as passing.

Tailwind overloads these prefixes, so the check resolves a name against `--color-*` first,
then the size, radius, font and shadow scales (`text-meta` is a size, `rounded-card` a
radius), then peels off an edge or an offset (`border-l-accent`, `ring-offset-bg`). Comments
and arbitrary values are stripped before scanning: a doc comment saying why `bg-soft` was
rejected must not read as a use of it, and `bg-[linear-gradient(…var(--color-bg-elevated)…)]`
is CSS passing through rather than a utility.

## Two empty screens that read as broken

The voice book put "GIỌNG ĐÃ BIẾT" at the top of a blank pane with "no voices yet" four
hundred pixels below — two answers to the same question with a void between them. The
heading now only appears over a list.

The calendar centred its empty state in what was left under the subscribe form, which put
the explanation of why there is nothing three hundred pixels beneath the box that fixes it.
That screen no longer asks for `full`, which means "this is the whole screen" and was never
true here.

Capping `full` centrally was tried first and reverted: it made every screen where the empty
state genuinely *is* the content fail `e2e/density.mjs` with four hundred pixels of
background under a centred message. The comment in `Empty` now says so, because the next
person to notice the calendar gap will reach for the same fix.

Also: the notes pane's empty state offers the button that fixes it. It was a dead end whose
only way out was a 288px column away in the corner of another pane.

All 18 browser suites green, 298 unit tests, 44 screenshots re-shot in both schemes with no
overflow and contrast AA everywhere.
@meocong
meocong merged commit 0fac652 into master Aug 14, 2026
10 checks passed
@meocong
meocong deleted the ui/dead-styles branch August 14, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant