Skip to content

refactor(ui): tokenize all design primitives (Phase 0 harmonization) - #338

Merged
krisarmstrong merged 1 commit into
mainfrom
chore/phase0-tokenize-design-primitives
May 27, 2026
Merged

refactor(ui): tokenize all design primitives (Phase 0 harmonization)#338
krisarmstrong merged 1 commit into
mainfrom
chore/phase0-tokenize-design-primitives

Conversation

@krisarmstrong

Copy link
Copy Markdown
Collaborator

Summary

Single-pass migration of stem's UI to use semantic design tokens instead of raw Tailwind utilities. Step 1 of the seed/stem/niac harmonization plan. Each repo will use a self-contained semantic token vocabulary, with shared names (different per-product values) so a future shared shell renders correctly everywhere.

  • 65 files changed, 1265 insertions / 1072 deletions
  • 1186 token replacements applied by a reusable migration script
  • CSS bundle 103.13 kB → 99.34 kB (-3.7%) from semantic deduplication
  • New CI gate blocks regression (scripts/check-token-discipline.sh)
  • Frontend build, type-check, Biome lint all green locally

What changed

Category Before After
Colors bg-red-500, text-blue-400/50, bg-white bg-status-error, text-status-info/50, bg-knob
Spacing space-y-3, gap-3, p-4, mb-6 stack, gap-default, pad, mb-section
Headings text-2xl font-bold text-text-primary leading-tight heading-1
Body text-base text-text-primary leading-relaxed body
Layout flex items-center justify-between flex-between
Indirection text-[var(--color-status-error)] text-status-error
Backdrops bg-black/60 backdrop-blur-sm bg-scrim/60 backdrop-blur-sm
Buttons (solid) bg-gradient-to-r from-red-600 to-red-500 shadow-red-500/25 bg-status-error hover:bg-status-error/85 shadow-status-error/25

Tokens added to ui/src/index.css

  • --color-text-disabled (light + dark)
  • --color-surface-deep (gradient terminus for the upcoming shared shell)
  • --color-log-{trace,debug,info,warn,error,fatal} (Syslog/RFC 5424, constant across modes — port from NIAC)
  • --color-scrim (#000000, constant) — bg-scrim/N for modal/drawer backdrops
  • --color-knob (#ffffff, constant) — toggle thumbs, text on saturated brand bg

New tooling

  • scripts/migrate-tokens.py — reusable Python migration script. Idempotent; will be re-run in seed and niac with the same source-of-truth mapping.
  • scripts/check-token-discipline.sh — CI gate wired into .github/workflows/ci.yml frontend job. Fails on raw palette, bare white/black, raw spacing utilities with semantic replacements, heading pairs that should be heading-N, var() color indirection, and flex shortcuts.
  • ui/src/utils/prefetch.ts — minimal stub so the shared Sidebar's hover-prefetch call site compiles. Populate ROUTE_PREFETCH_MAP with stem routes as performance wins arise.

Why

Three-product harmonization (seed / stem / niac) needs each repo to expose the same token vocabulary by name while owning its own values. This PR is the proof-of-concept and reference implementation. Identical Phase 0 PRs will follow for seed and niac before the shared shell (Sidebar / HeaderBar / PageHeader) is rolled out.

Test plan

  • ./scripts/check-token-discipline.sh — PASS
  • npm run lint — PASS (2 pre-existing warnings unrelated to this PR)
  • npx tsc --noEmit — PASS
  • npm run build — PASS, CSS bundle smaller
  • Visual smoke test of dev server in both light + dark mode (CI Lighthouse + Playwright will catch regressions)
  • Reviewer to spot-check Button.tsx solid variants in both themes (color now constant per status token instead of fixed red/green/blue/etc.)

🤖 Generated with Claude Code

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

Single-pass migration of stem's UI to use semantic design tokens instead
of raw Tailwind utilities. Step 1 of the seed/stem/niac harmonization
plan: each repo must use a self-contained semantic token vocabulary,
with shared names (different per-product values) so a shared shell
renders correctly everywhere.

Tokens added to ui/src/index.css
- --color-text-disabled (light + dark)
- --color-surface-deep (gradient terminus for the upcoming shared shell)
- --color-log-{trace,debug,info,warn,error,fatal} (Syslog/RFC 5424,
  constant across modes)
- --color-scrim (#000000, constant) — bg-scrim/N for modal/drawer backdrops
- --color-knob (#ffffff, constant) — toggle thumbs, text on saturated brand bg

Migration applied
- Colors: removed every raw palette class and bare bg-white/bg-black.
  Routes through brand-*/surface-*/text-*/status-*/log-* tokens.
- Spacing: numeric utilities → semantic (space-y-3 → stack, gap-3 →
  gap-default, p-4 → pad, mb-6 → mb-section, etc.).
- Typography: text-2xl font-bold compounds → heading-1/2/3/4; body /
  body-small / caption / label / section-title where matched.
- Layout: flex items-center justify-between → flex-between, flex-center.
- Arbitrary-value indirection: text-[var(--color-status-error)] →
  text-status-error.
- Button.tsx: solid-tone variants now use bg-status-* / bg-brand-primary
  with hover opacity instead of gradient-from-palette pairs.

New tooling
- scripts/migrate-tokens.py — reusable Python migration script (also
  used in seed and niac with the same source-of-truth mapping).
- scripts/check-token-discipline.sh — CI gate that fails on raw palette,
  bare white/black, raw spacing utilities, heading pairs, var() color
  indirection, and flex shortcuts. Wired into ci.yml frontend job.
- ui/src/utils/prefetch.ts — minimal stub for the shared Sidebar's
  hover-prefetch call site.

Validated
- Discipline gate, Biome lint, npm run build all green
- Rebased on latest main after merge conflicts
@krisarmstrong
krisarmstrong force-pushed the chore/phase0-tokenize-design-primitives branch from 6c3f7ae to 32464c8 Compare May 27, 2026 19:20
@krisarmstrong
krisarmstrong enabled auto-merge (squash) May 27, 2026 19:21
@github-actions

Copy link
Copy Markdown
Contributor

License Compliance Report

All dependencies pass license compliance checks

Go Dependencies

  • Unknown: 31 package(s)
  • MIT: 26 package(s)
  • BSD-3-Clause: 16 package(s)
  • Apache-2.0: 11 package(s)
  • BSD-2-Clause: 1 package(s)

npm Dependencies

See full report in workflow artifacts

Allowed Licenses: MIT, Apache-2.0, BSD-*, ISC, CC0-1.0, MPL-2.0
Forbidden: GPL, AGPL, SSPL (strong copyleft)

@krisarmstrong
krisarmstrong merged commit a7dab80 into main May 27, 2026
26 of 28 checks passed
@krisarmstrong
krisarmstrong deleted the chore/phase0-tokenize-design-primitives branch May 27, 2026 19:24
krisarmstrong added a commit that referenced this pull request May 27, 2026
…345)

* fix(ci): unblock main — race in sse, lighthouse cert, e2e selectors

main ci has been failing since phase 0 tokenization (#338) merged.
three independent root causes — none were phase-0 regressions but
phase-0 was the first change broad enough to trip the path filters
and surface them.

backend (race detector):
sse broadcaster's publish() released the read lock before sending,
so a concurrent unsubscribe could close the channel between the
"copy subs" snapshot and the send → write-on-closed-channel race.
fix: hold the read lock across the (non-blocking, select-default)
sends; the stalled-subscriber cleanup happens after the rlock is
released so unsubscribe's write lock can proceed.

e2e:
- history-page "result snapshot or empty hint": locator
  `text=/...|history|.../` matched the sidebar "history" label
  first (hidden on mobile viewport) before reaching the main
  content. scope to the page section.
- language-switch tests asserted on "test modules" /
  "módulos de prueba" but the i18n key common.sections.modules
  was never wired into any rendered ui — sidebar tests group was
  hardcoded `label: 'tests'`. wire sidebar via t() with
  defaultvalue fallback so existing display strings still work,
  set tests group label to the i18n key. now the marker actually
  renders, in both languages.

lighthouse audit:
treosh/lighthouse-ci-action was called with an inline urls:
override but no configpath, so .lighthouserc.json's settings were
ignored. workflow targets https://localhost:8444 (stem is https-
only post-wave-1) with a mkcert dev cert that the ci runner does
not trust → INSECURE_DOCUMENT_REQUEST / ERR_CERT_AUTHORITY_INVALID
on every audit, blocking every release-please run for the past
several merges. fix: add chromeflags (--ignore-certificate-errors
--allow-insecure-localhost) to .lighthouserc.json settings and
pass configpath: ./.lighthouserc.json so the action loads them.

unblocks release-please for stem.

* fix(lint): biome formatting + replace dead noExplicitAny suppressions

- Sidebar.tsx: biome format pass after the t() wrapping refactor
  (re-indentation inside the new `=> { ... }` body)
- useLocale.test.ts: the two `biome-ignore lint/suspicious/noExplicitAny`
  comments stopped suppressing anything after a biome upgrade — the
  underlying `any` was already type-safe enough that biome no longer
  flagged it. replace `as any` with explicit cast through
  `UseTranslationResponse<...>['i18n'|'t']` and drop the dead
  suppressions. tests still pass (3/3 in useLocale.test.ts).

---------

Co-authored-by: Kris Armstrong <kris.armstrong@icloud.com>
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