feat(web): unify tailwind theme with design tokens + light palette #216
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!216
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "boss/208"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
apps/web/src/styles/tokens.cssnow carries both dark (Tokyo Night Storm, default at:root) and light (Tokyo Night Day, under:root[data-theme="light"]) palettes, plus a@media (prefers-color-scheme: light)fallback so OS preference wins when the operator hasn't toggled explicitly.design/tokens.jsongainscaption = 10pxandh3 = 14px, both mirrored intotokens.cssas--ch-font-size-caption/--ch-font-size-h3. The@themeblock inindex.cssnow exposes every size as a first-class Tailwind utility (text-caption,text-meta,text-small,text-body,text-body-lg,text-h3,text-h2,text-h1,text-display) so the follow-up ~235text-[Npx]literal migration has a stable target.<ThemeToggle>(tri-stateauto → light → dark) persists tolocalStorage.ch-themeand mirrors ontodocumentElement.dataset.theme. An inline bootstrap script inapps/web/index.htmlreads the same key before React evaluates, so first paint lands on the correct palette — no FOUC.rounded-compact,rounded-default,rounded-card,rounded-pillstill resolve via@theme --radius-*.Closes #208
Test plan
bun x turbo run typecheck— all 3 workspaces clean.bun x biome check ./bun x biome format .— no diagnostics.bun x turbo run test— 795 server + 187 web tests pass, including 7 newlib/themetests and 3 new<ThemeToggle>tests covering cycle + persistence + initial-from-storage.cd apps/web && bun run build— succeeds, no new warnings (pre-existing shiki chunk-size warnings only)./monitor,/planner,/planner/board,/agents,/stats,/usage,/specsrender in both palettes; toggle persists across reloads; OS preference respected inautomode.🤖 Generated with Claude Code
769b2a686f0591bec1dfReview — Round 1
CI: ✅ green (run #1853, 3m54s).
Acceptance criteria
design/tokens.jsongains full light palette (allcolor.*slots)design/tokens.jsonaddsfont-size.caption = 10pxandfont-size.h3 = 14pxtokens.css: dark at:root, light at[data-theme="light"],@mediaOS fallback with:not([data-theme="dark"])guard@themeexposes all 9 font-size slots as first-class Tailwind utilitiesrounded-compact/rounded-default/rounded-card/rounded-pill) unchanged<ThemeToggle>in top bar, tri-state cycleauto → light → dark → autolocalStorage["ch-theme"]<script>inindex.htmlbun run buildcleanWhat's solid
--role-dev,--state-running,--stage-*, etc.) are only defined once at:rootand alias into the palette vars — when the palette vars get overridden in[data-theme="light"]or the@mediablock, the semantic aliases pick up the new values automatically at use time. No duplication needed and no light-mode miss possible.@media (prefers-color-scheme: light)guard correctly uses:root:not([data-theme="dark"]):not([data-theme="light"])so an explicitdarkpin wins over the OS preference.useThemecross-tab sync via thestorageevent is a nice touch and the cleanup is correct.index.html(#1a1b26/#e9ecf2/#c0caf5) match the token values.readStoredTheme+isThemeModedefensive guard handles quota-exceeded / private Safari correctly.Nit (non-blocking)
apps/web/src/components/theme-toggle.tsxline 46: the new<ThemeToggle>usestext-[13px](arbitrary value) in itsclassName. Since this PR introducestext-body(which maps to--ch-font-size-body: 13px) as a first-class Tailwind utility, the component could usetext-bodydirectly instead of the literal — exactly the pattern the follow-up migration ticket will sweep up everywhere else. Since it's new code rather than legacy code, it's an easy win. But given the follow-up ticket already owns the migration, happy to leave it to that pass.LGTM — implementation is correct, tests are thorough, no safety issues. ✅