Problem
The design-token system covers color well (24 CSS vars, 12 presets, validated custom themes) but stops there — spacing, typography, radii, and shadows are raw literals spread across ~1000 inline style= objects, and button base styles are copy-pasted per dialog.
Measured state (adversarially re-counted):
- ~999 inline
style= occurrences in components (SettingsDialog.tsx 162, NewTaskDialog.tsx 65) encoding raw paddings/gaps/radii/font-sizes.
- 25 distinct font sizes (17 px values + 8 em values); 13px ×101, 12px ×82, 14px ×78, with one-offs like 9/18/21/24/25/30/37px. No scale.
src/lib/fontScale.ts sf(px) is an identity stub used in 24 files, while 58 other files write raw 'font-size': '13px' — if sf() ever becomes a real scaler, most of the app won't scale.
- 11 distinct border-radius values; 12 distinct gap values; 16 box-shadow literals in styles.css + 9 inline — including four different "floating popover" shadows for one concept (
Dialog.tsx:121, CommitTreeOverlay.tsx:127, BranchCombobox.tsx:234, TaskAITerminal.tsx:433), despite --shadow-soft/--shadow-pop existing.
Button split-brain (the single most fragile pattern): .btn-primary/.btn-secondary/.btn-danger in styles.css define hover/active/focus states only (:1088-1139); the resting appearance (padding: '9px 20px', accent bg, radius 8px, 14px/500) is duplicated inline in ConfirmDialog.tsx:80-110, PushDialog.tsx:151-177, NewTaskDialog.tsx:~1393, EditProjectDialog.tsx:537,553 — with drift already visible (9px 18px vs 9px 20px). A padding change is an 8-file edit; any missed file silently forks the design. ConfirmDialog.tsx:102 also hardcodes '#fff' for danger text (should be a token).
Related: #160 (duplication audit) — this is the design-system slice of the same theme.
Proposed fix
From a full design/UX audit (2026-07-07); adversarially verified, counts re-derived. Line numbers as of b342bbd (v1.12.0).
Problem
The design-token system covers color well (24 CSS vars, 12 presets, validated custom themes) but stops there — spacing, typography, radii, and shadows are raw literals spread across ~1000 inline
style=objects, and button base styles are copy-pasted per dialog.Measured state (adversarially re-counted):
style=occurrences in components (SettingsDialog.tsx162,NewTaskDialog.tsx65) encoding raw paddings/gaps/radii/font-sizes.src/lib/fontScale.tssf(px)is an identity stub used in 24 files, while 58 other files write raw'font-size': '13px'— ifsf()ever becomes a real scaler, most of the app won't scale.Dialog.tsx:121,CommitTreeOverlay.tsx:127,BranchCombobox.tsx:234,TaskAITerminal.tsx:433), despite--shadow-soft/--shadow-popexisting.Button split-brain (the single most fragile pattern):
.btn-primary/.btn-secondary/.btn-dangerinstyles.cssdefine hover/active/focus states only (:1088-1139); the resting appearance (padding: '9px 20px', accent bg, radius 8px, 14px/500) is duplicated inline inConfirmDialog.tsx:80-110,PushDialog.tsx:151-177,NewTaskDialog.tsx:~1393,EditProjectDialog.tsx:537,553— with drift already visible (9px 18pxvs9px 20px). A padding change is an 8-file edit; any missed file silently forks the design.ConfirmDialog.tsx:102also hardcodes'#fff'for danger text (should be a token).Related: #160 (duplication audit) — this is the design-system slice of the same theme.
Proposed fix
.btn-*classes (they're already applied everywhere) and delete the inline copies — zero-risk, high-value first step.--space-1..6(2/4/8/12/16/24),--radius-sm/md/lg/full,--text-2xs..xl(10/11/12/13/14/17),--shadow-overlay. Consume incrementally from inline styles viavar()or shared constants — no need to abandon inline styles wholesale.sf()(or the tokens) consistently so a future scale/OS-font-size feature is possible.From a full design/UX audit (2026-07-07); adversarially verified, counts re-derived. Line numbers as of b342bbd (v1.12.0).