Frontend design system: feature screens bypass the OKLCH tokens + ui/* kit (inconsistent visual language) #49

Closed
opened 2026-06-06 18:51:31 +00:00 by logaritmisk · 1 comment
Owner

Severity: High. From a frontend UX audit. A coherent design foundation exists; almost no feature screen uses it — "a coherent kit wrapped around an incoherent app."

Problems

  • Tokens defined, never used outside ui/. web/src/index.css:31-72 defines a full semantic OKLCH token set (--foreground, --muted-foreground, --primary, --destructive, --border, --radius, light + .dark). A grep for token utilities (text-foreground, text-muted-foreground, bg-primary, text-destructive…) outside components/ui/ returns zero hits, while ~70 hardcoded palette utilities appear across the feature screens.
  • Inconsistent neutrals & danger: muted text is text-neutral-400/-500/-600 (three shades, one role); danger is text-red-600 everywhere — not the --destructive token (a different red).
  • Two competing accents: "active/selected" and links use indigo (bg-indigo-50, text-indigo-600, bg-indigo-600, combobox data-[highlighted]:bg-indigo-50), but --primary and the primary Button are near-black, and the publish stepper / active filter chips use bg-neutral-800. No single recognizable accent.
  • Status colors aren't tokens: visibility-badge.tsx:9-11 hardcodes amber/green-100/800 (no dark variant); highlight.tsx:34 hardcodes bg-yellow-200.
  • Radius token ignored: bare rounded (0.25rem) used ~20× vs the kit's token-derived rounded-md/lg (--radius: 0.625rem). ui/card.tsx exists but has zero usages — panels are hand-rolled with ad-hoc borders/radii.
  • Uppercase caption-label appears ~6× with 4 different recipes (color/weight/tracking/size all vary).

Suggested fixes

  • Migrate feature screens to token utilities (text-muted-foreground, text-destructive, bg-accent/bg-muted, text-primary); pick one accent and route nav/active/combobox-highlight/links/filter-chips through it.
  • Add --success/--warning/--highlight tokens (+ dark) and give Badge success/warning variants so VisibilityBadge selects a variant instead of patching classes.
  • Standardize on rounded-md/rounded-lg; use Card for panel containers; extract one .label-caption utility.
  • Add a CI guard / lint rule banning raw color utilities outside components/ui/.

Source: frontend UX/design audit, 2026-06-06.

**Severity: High.** _From a frontend UX audit. A coherent design foundation exists; almost no feature screen uses it — "a coherent kit wrapped around an incoherent app."_ ## Problems - **Tokens defined, never used outside `ui/`.** `web/src/index.css:31-72` defines a full semantic OKLCH token set (`--foreground`, `--muted-foreground`, `--primary`, `--destructive`, `--border`, `--radius`, light + `.dark`). A grep for token utilities (`text-foreground`, `text-muted-foreground`, `bg-primary`, `text-destructive`…) **outside `components/ui/` returns zero hits**, while ~70 hardcoded palette utilities appear across the feature screens. - **Inconsistent neutrals & danger:** muted text is `text-neutral-400`/`-500`/`-600` (three shades, one role); danger is `text-red-600` everywhere — not the `--destructive` token (a *different* red). - **Two competing accents:** "active/selected" and links use **indigo** (`bg-indigo-50`, `text-indigo-600`, `bg-indigo-600`, combobox `data-[highlighted]:bg-indigo-50`), but `--primary` and the primary `Button` are **near-black**, and the publish stepper / active filter chips use `bg-neutral-800`. No single recognizable accent. - **Status colors aren't tokens:** `visibility-badge.tsx:9-11` hardcodes `amber/green-100/800` (no dark variant); `highlight.tsx:34` hardcodes `bg-yellow-200`. - **Radius token ignored:** bare `rounded` (0.25rem) used ~20× vs the kit's token-derived `rounded-md/lg` (`--radius: 0.625rem`). `ui/card.tsx` exists but has **zero usages** — panels are hand-rolled with ad-hoc borders/radii. - **Uppercase caption-label** appears ~6× with 4 different recipes (color/weight/tracking/size all vary). ## Suggested fixes - Migrate feature screens to token utilities (`text-muted-foreground`, `text-destructive`, `bg-accent`/`bg-muted`, `text-primary`); pick **one** accent and route nav/active/combobox-highlight/links/filter-chips through it. - Add `--success`/`--warning`/`--highlight` tokens (+ dark) and give `Badge` `success`/`warning` variants so `VisibilityBadge` selects a variant instead of patching classes. - Standardize on `rounded-md`/`rounded-lg`; use `Card` for panel containers; extract one `.label-caption` utility. - Add a CI guard / lint rule banning raw color utilities outside `components/ui/`. _Source: frontend UX/design audit, 2026-06-06._
Author
Owner

Done — merged to main (48edb03).

Every feature screen now routes through the OKLCH design tokens:

  • One brand accent: --primary/--ring set to indigo (light + .dark); primary buttons, selected rows, links, and chips share it.
  • Status tokens: --success/--warning/--highlight (+ foregrounds) added in :root and .dark, exposed via @theme inline. VisibilityBadge selects new Badge success/warning variants; search highlight uses bg-highlight.
  • Migration: ~120 raw Tailwind color utilities across 24 feature files swapped to tokens; bare roundedrounded-md (radius token); the 5 ad-hoc uppercase-caption recipes collapsed to a single .label-caption utility.
  • Enforcement: new web/scripts/check-no-raw-colors.mjs guard (pnpm check:colors) bans raw palette utilities outside components/ui/, wired into the CI web job after check:size. Passes (107 files, 0 offenses).

Pure styling refactor — no behavior/layout/API/data change. Gate green: typecheck, lint, 169 tests, build, check:size (183.4 KB gz), check:colors. CssCheck story re-pinned.

The .dark token set is now coherent, which unblocks #59 (dark-mode toggle). Two minor items deferred to #59: dark --primary label contrast (3.21:1 — AA for UI/large text, tunable for normal text) and optionally widening the guard to accent-/caret-/shadow- color prefixes.

Done — merged to `main` (`48edb03`). Every feature screen now routes through the OKLCH design tokens: - **One brand accent:** `--primary`/`--ring` set to indigo (light + `.dark`); primary buttons, selected rows, links, and chips share it. - **Status tokens:** `--success`/`--warning`/`--highlight` (+ foregrounds) added in `:root` and `.dark`, exposed via `@theme inline`. `VisibilityBadge` selects new Badge `success`/`warning` variants; search highlight uses `bg-highlight`. - **Migration:** ~120 raw Tailwind color utilities across 24 feature files swapped to tokens; bare `rounded` → `rounded-md` (radius token); the 5 ad-hoc uppercase-caption recipes collapsed to a single `.label-caption` utility. - **Enforcement:** new `web/scripts/check-no-raw-colors.mjs` guard (`pnpm check:colors`) bans raw palette utilities outside `components/ui/`, wired into the CI web job after `check:size`. Passes (107 files, 0 offenses). Pure styling refactor — no behavior/layout/API/data change. Gate green: typecheck, lint, 169 tests, build, check:size (183.4 KB gz), check:colors. CssCheck story re-pinned. The `.dark` token set is now coherent, which **unblocks #59** (dark-mode toggle). Two minor items deferred to #59: dark `--primary` label contrast (3.21:1 — AA for UI/large text, tunable for normal text) and optionally widening the guard to `accent-`/`caret-`/`shadow-` color prefixes.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logaritmisk/biggus-dickus#49