Files
SimpleFinanceDash/.planning/phases/01-design-token-foundation/01-01-PLAN.md

12 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
01-design-token-foundation 01 execute 1
frontend/src/index.css
frontend/src/lib/palette.ts
frontend/src/lib/palette.test.ts
frontend/src/test-setup.ts
frontend/vite.config.ts
frontend/package.json
true
DSGN-01
DSGN-02
DSGN-05
truths artifacts key_links
All shadcn CSS variables in :root use pastel oklch values with non-zero chroma (no oklch(L 0 0) neutrals remain except --card which stays pure white)
palette.ts exports typed color objects for all 7 category types with 3 shades each (light, medium, base)
headerGradient() returns a valid CSSProperties object with a linear-gradient background
amountColorClass() returns text-success for positive income, text-warning for over-budget expenses, text-destructive for negative available
Custom --success and --warning CSS tokens exist in :root and are registered in @theme inline
path provides contains
frontend/src/index.css Pastel oklch CSS variables for all shadcn tokens, plus --success and --warning semantic tokens --success
path provides exports
frontend/src/lib/palette.ts Single source of truth for category colors with typed exports
palette
CategoryType
CategoryShades
headerGradient
overviewHeaderGradient
amountColorClass
path provides min_lines
frontend/src/lib/palette.test.ts Unit tests for palette exports, headerGradient, and amountColorClass 40
from to via pattern
frontend/src/index.css frontend/src/lib/palette.ts --chart-* CSS vars synced with palette base colors chart-[1-5]
from to via pattern
frontend/src/lib/palette.ts @/lib/utils amountColorClass returns Tailwind utility classes that reference CSS variables text-success|text-warning|text-destructive
Establish the pastel CSS variable system and palette.ts module that all subsequent visual work depends on.

Purpose: This is the foundation layer. Every component change in Plan 02 references the tokens and palette created here. Without this, component wiring would hardcode new values instead of referencing a single source of truth. Output: Pastel-tinted index.css, typed palette.ts with helpers, passing unit tests.

<execution_context> @/home/jean-luc-makiola/.claude/get-shit-done/workflows/execute-plan.md @/home/jean-luc-makiola/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/01-design-token-foundation/01-CONTEXT.md @.planning/phases/01-design-token-foundation/01-RESEARCH.md @.planning/phases/01-design-token-foundation/01-VALIDATION.md

@frontend/src/index.css @frontend/vite.config.ts @frontend/package.json

Task 1: Install test infrastructure and set up vitest frontend/package.json, frontend/vite.config.ts, frontend/src/test-setup.ts 1. Install test dependencies: `cd frontend && bun add -d vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom`
  1. Update frontend/vite.config.ts — add a test block inside defineConfig:

    test: {
      environment: 'jsdom',
      globals: true,
      setupFiles: ['./src/test-setup.ts'],
    }
    

    Import /// <reference types="vitest" /> at the top of the file.

  2. Create frontend/src/test-setup.ts:

    import '@testing-library/jest-dom'
    
  3. Verify the test runner starts: cd frontend && bun vitest run --reporter=verbose (should exit 0 with "no test files found" or similar). cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run --reporter=verbose 2>&1 | tail -5 vitest runs successfully with zero failures. test-setup.ts imports jest-dom matchers. vite.config.ts has test block with jsdom environment.

Task 2: Replace CSS tokens with pastel oklch values and add success/warning tokens frontend/src/index.css - Manual verification: every oklch value in :root has chroma > 0 (except --card which stays oklch(1 0 0) per locked decision "cards stay pure white") - --success and --warning tokens exist in :root - --color-success and --color-warning are registered in @theme inline - --chart-1 through --chart-5 map to category base colors (bill, variable_expense, debt, saving, investment) Replace ALL zero-chroma oklch values in the `:root` block of `frontend/src/index.css` with pastel-tinted equivalents. Follow these locked decisions:

Background and surface tokens (lavender tint, hue ~290):

  • --background: oklch(0.98 0.005 290) — very subtle lavender tint
  • --card: oklch(1 0 0) — KEEP pure white (cards float on tinted bg)
  • --card-foreground: oklch(0.145 0.005 290) — near-black with slight tint
  • --popover: oklch(1 0 0) — pure white like cards
  • --popover-foreground: oklch(0.145 0.005 290)
  • --foreground: oklch(0.145 0.005 290)

Primary/accent tokens (soft lavender-blue, hue ~260-280):

  • --primary: oklch(0.50 0.12 260) — soft lavender-blue
  • --primary-foreground: oklch(0.99 0.005 290)
  • --secondary: oklch(0.95 0.015 280)
  • --secondary-foreground: oklch(0.25 0.01 280)
  • --muted: oklch(0.95 0.010 280)
  • --muted-foreground: oklch(0.50 0.01 280)
  • --accent: oklch(0.94 0.020 280)
  • --accent-foreground: oklch(0.25 0.01 280)
  • --ring: oklch(0.65 0.08 260) — tinted focus ring
  • --border: oklch(0.91 0.008 280) — subtle lavender border
  • --input: oklch(0.91 0.008 280)

Sidebar tokens (slightly more distinct lavender):

  • --sidebar: oklch(0.97 0.012 280)
  • --sidebar-foreground: oklch(0.20 0.01 280)
  • --sidebar-primary: oklch(0.50 0.12 260)
  • --sidebar-primary-foreground: oklch(0.99 0.005 290)
  • --sidebar-accent: oklch(0.93 0.020 280)
  • --sidebar-accent-foreground: oklch(0.25 0.01 280)
  • --sidebar-border: oklch(0.90 0.010 280)
  • --sidebar-ring: oklch(0.65 0.08 260)

Chart tokens (mapped to category base colors — synced with palette.ts):

  • --chart-1: oklch(0.76 0.12 250) — bill (blue)
  • --chart-2: oklch(0.80 0.14 85) — variable_expense (amber)
  • --chart-3: oklch(0.76 0.13 15) — debt (rose)
  • --chart-4: oklch(0.75 0.13 280) — saving (violet)
  • --chart-5: oklch(0.76 0.12 320) — investment (pink)

NEW semantic tokens — add to :root block:

  • --success: oklch(0.55 0.15 145) — green for positive amounts
  • --success-foreground: oklch(0.99 0 0)
  • --warning: oklch(0.70 0.14 75) — amber for over-budget
  • --warning-foreground: oklch(0.99 0 0)

NEW @theme inline additions — add these lines:

--color-success: var(--success);
--color-success-foreground: var(--success-foreground);
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);

Do NOT modify the .dark block (dark mode is out of scope). Do NOT touch the @layer base block. cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash && grep -c "oklch.*0 0)" frontend/src/index.css | head -1 The :root block has zero remaining zero-chroma neutral tokens (except --card and --popover which are intentionally pure white). --success and --warning tokens exist in :root and are registered in @theme inline. All chart tokens map to category base colors. The .dark block is unchanged.

Task 3: Create palette.ts and palette.test.ts frontend/src/lib/palette.ts, frontend/src/lib/palette.test.ts - palette exports all 7 CategoryType values: income, bill, variable_expense, debt, saving, investment, carryover - Each category has 3 shades: light, medium, base — all non-empty oklch strings - headerGradient('bill') returns { background: 'linear-gradient(to right, ...)' } - overviewHeaderGradient() returns a multi-stop gradient for FinancialOverview - amountColorClass({ type: 'income', actual: 100, budgeted: 0, isIncome: true }) returns 'text-success' - amountColorClass({ type: 'income', actual: 0, budgeted: 0, isIncome: true }) returns '' - amountColorClass({ type: 'bill', actual: 200, budgeted: 100 }) returns 'text-warning' - amountColorClass({ type: 'bill', actual: 100, budgeted: 100 }) returns '' (exactly on budget = normal) - amountColorClass({ type: 'bill', actual: 50, budgeted: 100 }) returns '' (under budget = normal) - amountColorClass({ type: 'bill', actual: 0, budgeted: 0, isAvailable: true }) returns '' - amountColorClass({ type: 'bill', actual: 500, budgeted: 0, isAvailable: true }) returns 'text-success' - amountColorClass({ type: 'bill', actual: -100, budgeted: 0, isAvailable: true }) returns 'text-destructive' **Write tests first (RED):**

Create frontend/src/lib/palette.test.ts with tests covering all behaviors listed above. Group into describe blocks: "palette exports", "headerGradient", "overviewHeaderGradient", "amountColorClass".

Run tests — they must FAIL (module not found).

Write implementation (GREEN):

Create frontend/src/lib/palette.ts with these exports:

  1. CategoryType — union type of all 7 category strings

  2. CategoryShades — interface with light, medium, base string fields

  3. paletteRecord<CategoryType, CategoryShades> with oklch values per locked decisions:

    • income: hue 145 (green), light L=0.96 C=0.04, medium L=0.88 C=0.08, base L=0.76 C=0.14
    • bill: hue 250 (blue), light L=0.96 C=0.03, medium L=0.88 C=0.07, base L=0.76 C=0.12
    • variable_expense: hue 85 (amber), light L=0.97 C=0.04, medium L=0.90 C=0.08, base L=0.80 C=0.14
    • debt: hue 15 (rose), light L=0.96 C=0.04, medium L=0.88 C=0.08, base L=0.76 C=0.13
    • saving: hue 280 (violet), light L=0.95 C=0.04, medium L=0.87 C=0.08, base L=0.75 C=0.13
    • investment: hue 320 (pink), light L=0.96 C=0.04, medium L=0.88 C=0.07, base L=0.76 C=0.12
    • carryover: hue 210 (sky), light L=0.96 C=0.03, medium L=0.88 C=0.06, base L=0.76 C=0.11
  4. headerGradient(type: CategoryType): React.CSSProperties — returns { background: 'linear-gradient(to right, ${light}, ${medium})' }

  5. overviewHeaderGradient(): React.CSSProperties — returns a multi-stop gradient using carryover.light, saving.light, and income.light (sky via lavender to green) for the FinancialOverview header

  6. amountColorClass(opts) — implements the locked amount coloring rules:

    • isAvailable or isIncome path: positive → 'text-success', negative → 'text-destructive', zero → ''
    • Expense path: actual > budgeted → 'text-warning', else → ''

IMPORTANT: The base colors for bill, variable_expense, debt, saving, investment MUST match the --chart-1 through --chart-5 values set in Task 2's index.css. These are the same oklch strings.

Run tests — they must PASS. cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run src/lib/palette.test.ts --reporter=verbose All palette.test.ts tests pass. palette.ts exports all 7 categories with 3 shades each. headerGradient returns valid gradient CSSProperties. amountColorClass correctly returns text-success/text-warning/text-destructive/empty string per the locked rules.

1. `cd frontend && bun vitest run --reporter=verbose` — all tests pass 2. `grep -c "oklch.*0 0)" frontend/src/index.css` — returns a small number (only --card and --popover intentionally white) 3. `grep "success\|warning" frontend/src/index.css` — shows the new semantic tokens 4. palette.ts exports are importable: `cd frontend && bun -e "import { palette } from './src/lib/palette'; console.log(Object.keys(palette).length)"` — prints 7

<success_criteria>

  • Zero zero-chroma neutrals remain in :root (except intentional pure white on --card/--popover)
  • palette.ts is the single source of truth for 7 category types x 3 shades
  • headerGradient and amountColorClass helpers work correctly per unit tests
  • --success and --warning CSS variables are usable as Tailwind utilities (text-success, text-warning)
  • --chart-1 through --chart-5 values match palette.ts base colors
  • vitest runs with all tests green </success_criteria>
After completion, create `.planning/phases/01-design-token-foundation/01-01-SUMMARY.md`