--- phase: 5 slug: design-system-token-rework status: draft shadcn_initialized: true preset: new-york / neutral / lucide created: 2026-04-20 --- # Phase 5 — UI Design Contract > Visual and interaction contract for Phase 5: Design System Token Rework. > Generated by gsd-ui-researcher, verified by gsd-ui-checker. > > This phase is a pure token and spacing rework — no new components, no new features. > Every entry in this contract describes what must be TRUE after the change, not before. --- ## Design System | Property | Value | Source | |----------|-------|--------| | Tool | shadcn/ui | components.json | | Style | new-york | components.json | | Preset | neutral base, cssVariables: true | components.json | | Component library | Radix UI (via shadcn) | components.json | | Icon library | lucide-react | components.json | | Font | Inter (--font-sans) | src/index.css | --- ## Spacing Scale Declared values (all multiples of 4): | Token | px Value | Tailwind Class | Usage | |-------|----------|----------------|-------| | xs | 4px | gap-1 / p-1 | Icon gaps, inline tight spacing | | sm | 8px | gap-2 / p-2 | Compact inline elements, badge padding | | md | 16px | gap-4 / p-4 | Default inline element spacing | | lg | 24px | gap-6 / p-6 | Card internal padding (upgraded from p-4) | | xl | 32px | gap-8 | Section gaps between cards and sections (upgraded from gap-6) | | 2xl | 48px | gap-12 | Major section breaks, page-level vertical rhythm | | 3xl | 64px | py-16 | Auth page vertical centering, hero areas | **Phase 5 spacing changes (from CONTEXT.md):** - Card internal padding: `p-4` → `p-6` across all 9 pages - Section gaps: `gap-4` → `gap-6`, `gap-6` → `gap-8` across all pages - Page header bottom margin: standardize to `mb-6` everywhere - Section-to-section gap: standardize to `gap-8` Exceptions: none — 8-point scale applies without exception. --- ## Typography | Role | Size | Tailwind | Weight | Weight Class | Line Height | Usage | |------|------|----------|--------|--------------|-------------|-------| | Caption | 12px | text-xs | 400 | font-normal | 1.5 | Subtitles, secondary metadata | | Body / Label | 14px | text-sm | 500 | font-medium | 1.5 | Table cells, form labels, card labels, stat subtitles | | Base | 16px | text-base | 500 | font-medium | 1.5 | Card titles (e.g. chart section headings) | | Heading | 24px | text-2xl | 600 | font-semibold | 1.2 | Page titles (PageShell h1), auth card titles, template names | Source: Existing usage in PageShell, StatCard, DashboardPage, LoginPage, RegisterPage confirmed these four sizes as the complete in-use set. Weights used: exactly 2 — `font-medium` (500) for body/data, `font-semibold` (600) for headings. The rare `font-bold` (700) occurrences in StatCard value display are reclassified as `font-semibold` for consistency. --- ## Color ### Base UI Tokens (post-rework values) | Role | OKLCH Value | Usage | |------|-------------|-------| | Background (dominant, 60%) | oklch(0.98 0.01 260) | Page background — chroma lifted 0.005→0.01 for subtle warmth | | Card / Popover (secondary, 30%) | oklch(1 0 0) | Cards, modals, popovers, sidebar surface | | Sidebar | oklch(0.97 0.008 260) | Sidebar background — no change | | Primary accent (10%) | oklch(0.55 0.15 260) | Active nav items, primary buttons, focus rings | | Secondary / Muted | oklch(0.93 0.02 260) | Secondary buttons, muted chip backgrounds | | Border / Input | oklch(0.88 0.01 260) | All borders and input outlines | | Destructive | oklch(0.6 0.2 25) | Delete actions, error alerts only | Accent reserved for: primary action buttons, active sidebar navigation item, focus ring outlines, primary CTA buttons. No other elements may use the primary OKLCH(0.55 0.15 260) value. ### Category Text Colors (WCAG 4.5:1 contrast on white — no change to lightness/chroma) | Token | OKLCH Value | Hue | |-------|-------------|-----| | --color-income | oklch(0.55 0.17 155) | Green | | --color-bill | oklch(0.55 0.17 25) | Orange-red | | --color-variable-expense | oklch(0.58 0.16 50) | Amber | | --color-debt | oklch(0.52 0.18 355) | Red | | --color-saving | oklch(0.55 0.16 220) | Blue | | --color-investment | oklch(0.55 0.16 285) | Violet | ### Category Fill Colors (post-rework — chroma raised to 0.22+) These replace the current fill tokens (C=0.18-0.20). Exact values are at Claude's discretion per CONTEXT.md as long as chroma >= 0.22 and the fills remain visually distinct from each other. | Token | Target OKLCH | Constraint | |-------|--------------|------------| | --color-income-fill | oklch(0.72 0.22 155) | C >= 0.22, L ~0.70-0.75 | | --color-bill-fill | oklch(0.70 0.22 25) | C >= 0.22, L ~0.68-0.73 | | --color-variable-expense-fill | oklch(0.74 0.22 50) | C >= 0.22, L ~0.70-0.76 | | --color-debt-fill | oklch(0.66 0.23 355) | C >= 0.22, L ~0.64-0.70 | | --color-saving-fill | oklch(0.72 0.22 220) | C >= 0.22, L ~0.70-0.75 | | --color-investment-fill | oklch(0.68 0.22 285) | C >= 0.22, L ~0.65-0.72 | ### Chart Colors (post-rework — aligned to fill tokens) The `--color-chart-1` through `--color-chart-5` variables are REMOVED. Chart components reference `--color-*-fill` tokens directly. This eliminates the duplicate color system. | Removed | Replaced by | |---------|-------------| | --color-chart-1 | --color-income-fill | | --color-chart-2 | --color-bill-fill | | --color-chart-3 | --color-variable-expense-fill | | --color-chart-4 | --color-debt-fill | | --color-chart-5 | --color-saving-fill | ### Corner Radius | Token | Current Value | Post-Rework Value | Effect | |-------|--------------|-------------------|--------| | --radius | 0.625rem (10px) | 0rem (0px) | All shadcn components become sharp-cornered | Third-party overrides required (applied via CSS selectors in src/index.css): - Recharts bar elements: force `rx="0" ry="0"` or CSS `border-radius: 0` - Sonner toast container: override `.sonner-toast` border-radius to 0 --- ## Copywriting Contract Phase 5 is a pure visual rework — no new user-facing copy is introduced. The following existing copy elements remain unchanged and are documented here as the confirmed contract. | Element | Copy | Notes | |---------|------|-------| | Primary CTA (Budget List) | "New Budget" | Existing — unchanged | | Primary CTA (Template) | "Add item" | Existing — unchanged | | Primary CTA (Categories) | "New Category" | Existing — unchanged | | Empty state (Dashboard) | Translation key: `dashboard.noBudgetForMonth` | Existing — no copy changes this phase | | Error state (auth forms) | Translation key: `auth.error` + specific message | Existing — no copy changes this phase | | Destructive actions | None introduced in this phase | Token rework only | No new destructive actions are introduced. No new confirmation dialogs. No new empty states. --- ## Component Inventory Components affected by token changes (no code changes required — token cascade handles it): | Component | Location | Rounding Source | Change Required | |-----------|----------|-----------------|-----------------| | Button | src/components/ui/button.tsx | --radius token | Automatic via --radius: 0 | | Card | src/components/ui/card.tsx | --radius token | Automatic; padding class updated to p-6 | | Input | src/components/ui/input.tsx | --radius token | Automatic via --radius: 0 | | Badge | src/components/ui/badge.tsx | --radius token | Automatic via --radius: 0 | | Select | src/components/ui/select.tsx | --radius token | Automatic via --radius: 0 | | Sheet | src/components/ui/sheet.tsx | --radius token | Automatic via --radius: 0 | | Dialog | src/components/ui/dialog.tsx | --radius token | Automatic via --radius: 0 | | Popover | src/components/ui/popover.tsx | --radius token | Automatic via --radius: 0 | | Sidebar | src/components/ui/sidebar.tsx | --radius token | Automatic via --radius: 0 | | Dropdown Menu | src/components/ui/dropdown-menu.tsx | --radius token | Automatic via --radius: 0 | | Recharts bars | SpendBarChart, IncomeBarChart | SVG rx/ry attrs | Explicit CSS override needed | | Sonner toasts | src/components/ui/sonner.tsx | Sonner inline styles | CSS selector override needed | | Category swatches | CategoriesPage, CategorySection | Hardcoded rounded-* | Audit and remove rounded-* classes | | Budget progress bars | BudgetDetailPage | Hardcoded rounded-* | Audit and remove rounded-* classes | Pages to audit for inline `rounded-*` classes (must be zero after this phase): 1. DashboardPage — CategorySection swatches, StatCard 2. BudgetListPage — budget item rows 3. BudgetDetailPage — progress bars, budget row items 4. TemplatePage — template item rows, category group headers 5. CategoriesPage — category color swatches 6. QuickAddPage — picker items 7. SettingsPage — form elements 8. LoginPage — auth card 9. RegisterPage — auth card --- ## Registry Safety | Registry | Blocks Used | Safety Gate | |----------|-------------|-------------| | shadcn official (new-york) | button, card, badge, input, label, select, sheet, dialog, popover, sidebar, dropdown-menu, separator, skeleton, table, tooltip, sonner, chart, collapsible | not required | | Third-party | none | not applicable | No third-party registries. No vetting gate required. --- ## Interaction Contract Phase 5 introduces no new interaction patterns. Existing interactions are preserved. The following are confirmed unchanged: - Collapsible sections: 200ms ease-out open/close animation (--animate-collapsible-open/close) — no change - Sidebar navigation: active state uses primary color — no change to behavior - Form validation: error states use `--color-destructive` — no change - Chart tooltips: Recharts default — style override via chart.tsx config only --- ## Visual Regression Checklist The executor must perform a manual visual pass after all token changes confirming: - [ ] No pill buttons visible on any of the 9 pages - [ ] No rounded cards visible on any of the 9 pages - [ ] No rounded inputs visible on any of the 9 pages - [ ] Category fill colors are visibly colorful against white (not grey-tinted) - [ ] Category text colors still appear dark/readable (not washed out by chroma increase) - [ ] Section gaps feel generous — no visual crowding between cards - [ ] Card internal padding feels spacious — content not flush against card edges - [ ] Page headers have consistent bottom margin before first content section - [ ] Recharts bars are square-ended (no rounded caps) - [ ] Sonner toasts have sharp corners --- ## Checker Sign-Off - [ ] Dimension 1 Copywriting: PASS - [ ] Dimension 2 Visuals: PASS - [ ] Dimension 3 Color: PASS - [ ] Dimension 4 Typography: PASS - [ ] Dimension 5 Spacing: PASS - [ ] Dimension 6 Registry Safety: PASS **Approval:** pending