PR #119 merged: fix(ux): emit missing token utilities by hand
What broke
Phases 1–7 shipped components using class names like text-strong, bg-panel-raised, border-default, border-hairline, text-faint, bg-hover. Prod looked identical to pre-overhaul because the corresponding CSS rules either didn't exist OR resolved to empty values.
Root cause (refined after local Playwright verification)
Two separate problems compounding:
-
Missing utility classes. Tailwind v4's
@themegenerates utilities from--color-Xusing the FULL suffix. So--color-text-strongproduced.bg-text-strong/.text-text-strong(double prefix), never.text-strong. Same for every role-prefixed token. -
Unresolved var chain. Phase 1 declared tokens like
--color-text-strong: var(--color-zinc-100). Tailwind v4 does not emit its default palette as standalone:rootcustom properties — palette colours only ship as inline fallbacks on the utility rules that consume them directly (e.g..text-zinc-100 { color: var(--color-zinc-100, oklch(96.7% .001 286.375)) }). Sovar(--color-zinc-100)inside a custom:rootdeclaration resolved to empty. Every consumer of--color-text-stronggot nothing.
Both bugs together meant the new aesthetic was a no-op on prod.
The fix
app.css@theme— replace everyvar(--color-zinc-X)/color-mix()chain with the literaloklch()value pulled from Tailwind v4's default palette. Now--color-text-strong,--color-bg-panel-raised,--color-border-default, etc. resolve to real colours on:root.app.css@layer utilities— emit the role-prefixed class names by hand (.text-strong,.bg-panel-raised,.border-default,.border-hairline,.text-faint,.text-default,.bg-page,.bg-panel,.bg-hover, hover/group-hover variants,.decoration-border-default). These reference the now-resolving@themevars.tokens.css— drop the parallel:rootblock that re-declared the same tokens with the brokenvar()chain. It was being cascaded AFTER@themeand undoing the fix. File now only documents the design system + ships motion/radii/font-family vars that don't collide with@theme.
Local verification (Playwright, dev DB)
--color-text-strongresolves tooklch(96.7% .001 286.375)✓--color-bg-panel-raisedresolves tooklch(21% .006 285.885/.6)✓--color-border-defaultresolves tooklch(27.4% .006 286.033)✓- Cards render with zinc-900-at-60% transparent fill + zinc-800 hairline border ✓
- Breadcrumb (Phase 5) renders
Reports / The Prediction Scorecard✓ - TimestampLine (Phase 3) renders "Updated 08:32 UTC · Next in 29m" ✓
- Contextual EmptyState (Phase 3) shows "⏱ First hits land once resolve runs..." ✓
- Phase 6 categories (LIVE PREDICTION / PREDICTION SCORECARD / BACKTEST / MINDSHARE / COMPETITIVE INTEL) all render in emerald ✓
- Mobile (390px) — single column, no horizontal scroll, type hierarchy correct ✓
Screenshots in docs/ux-overhaul/screenshots/.
Test plan
-
npm run buildclean -
php artisan test --compact— 188 pass + 2 pre-existing LinkedIn failures (unrelated) - Playwright verification at 1280×800 + 390×844 (screenshots committed)