diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 53dfeb0..e45ba16 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -7,8 +7,8 @@ ### Theme -- [ ] **THEME-01**: App uses a consistent MD3 color token system (CSS custom properties) that all components reference instead of hardcoded Tailwind color classes -- [ ] **THEME-02**: User can toggle between System, Light, and Dark themes, with choice persisted across sessions and no flash of unstyled content on load +- [x] **THEME-01**: App uses a consistent MD3 color token system (CSS custom properties) that all components reference instead of hardcoded Tailwind color classes +- [x] **THEME-02**: User can toggle between System, Light, and Dark themes, with choice persisted across sessions and no flash of unstyled content on load ### Components @@ -56,8 +56,8 @@ | Requirement | Phase | Status | |-------------|-------|--------| -| THEME-01 | Phase 8 | Pending | -| THEME-02 | Phase 8 | Pending | +| THEME-01 | Phase 8 | Complete | +| THEME-02 | Phase 8 | Complete | | COMP-01 | Phase 9 | Pending | | COMP-02 | Phase 9 | Pending | | COMP-03 | Phase 9 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 096437a..20938d9 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -51,7 +51,7 @@ Full phase details: [.planning/milestones/v1.1-ROADMAP.md](milestones/v1.1-ROADM 2. User can toggle between System, Light, and Dark themes via a visible control, and the entire UI responds immediately 3. Theme preference persists in localStorage and applies on reload without any flash of wrong theme 4. Tailwind v4 `@theme` directive maps MD3 token names to utility classes (e.g., `bg-surface`, `text-on-primary`) -**Plans:** 2 plans +**Plans:** 1/2 plans executed Plans: - [ ] 08-01-PLAN.md — Token system, flash prevention, ThemeToggle component - [ ] 08-02-PLAN.md — Color class migration across all components @@ -101,7 +101,7 @@ Plans: | 5. Tech Debt | v1.1 | 4/4 | Complete | 2026-03-30 | | 6. New Backends | v1.1 | 4/4 | Complete | 2026-03-31 | | 7. Validation & UX Polish | v1.1 | 3/3 | Complete | 2026-03-31 | -| 8. Theme Foundation | v1.2 | 0/2 | Planning | - | +| 8. Theme Foundation | 1/2 | In Progress| | - | | 9. MD3 Components | v1.2 | 0/? | Not started | - | | 10. Content & Clarity | v1.2 | 0/? | Not started | - | | 11. Polish & Responsiveness | v1.2 | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 801805f..f53416e 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v1.2 milestone_name: UI Polish & Clarity status: planning -stopped_at: Phase 8 context gathered -last_updated: "2026-03-31T15:53:28.623Z" +stopped_at: Completed 08-01-PLAN.md — MD3 token system, ThemeToggle, dark mode +last_updated: "2026-04-01T02:51:28.872Z" last_activity: 2026-03-31 — Roadmap created for v1.2 progress: total_phases: 4 completed_phases: 0 - total_plans: 0 - completed_plans: 0 + total_plans: 2 + completed_plans: 1 percent: 0 --- @@ -42,6 +42,8 @@ Recent decisions affecting current work: - v1.2 roadmap: Zero new runtime dependencies — Tailwind v4 @theme + CSS custom properties only - v1.2 roadmap: Bottom-up migration order (tokens -> primitives -> migration -> content -> polish) - v1.2 roadmap: ThemeToggle via DOM class toggle, not React Context (avoids re-render cascade) +- [Phase 08-01]: Two-layer CSS token pattern: @theme var() references + @layer base :root/.dark raw values — required for .dark cascade in Tailwind v4 +- [Phase 08-01]: vi.stubGlobal for localStorage/matchMedia in tests — Node v25 experimental WebStorage breaks standard Storage API in jsdom environment ### Pending Todos @@ -54,6 +56,6 @@ None yet. ## Session Continuity -Last session: 2026-03-31T15:53:28.621Z -Stopped at: Phase 8 context gathered -Resume file: .planning/phases/08-theme-foundation/08-CONTEXT.md +Last session: 2026-04-01T02:51:28.870Z +Stopped at: Completed 08-01-PLAN.md — MD3 token system, ThemeToggle, dark mode +Resume file: None diff --git a/.planning/phases/08-theme-foundation/08-01-SUMMARY.md b/.planning/phases/08-theme-foundation/08-01-SUMMARY.md new file mode 100644 index 0000000..ca93aa2 --- /dev/null +++ b/.planning/phases/08-theme-foundation/08-01-SUMMARY.md @@ -0,0 +1,143 @@ +--- +phase: 08-theme-foundation +plan: 01 +subsystem: ui +tags: [tailwindcss, css-custom-properties, dark-mode, theme, react, vitest] + +# Dependency graph +requires: [] +provides: + - MD3 color token system (15 tokens: primary, surface, error, success, warning families) + - Dark mode infrastructure via @custom-variant dark + .dark class on + - Flash prevention inline script in index.html + - ThemeToggle segmented control component (Light/Dark/System) + - bg-surface, text-on-surface, bg-primary, etc. Tailwind utility classes +affects: [09-component-migration, 10-content-refinement, 11-polish] + +# Tech tracking +tech-stack: + added: [] + patterns: + - Two-layer token architecture (@layer base :root/.dark raw values + @theme var() references) + - DOM class toggle for theme (no React Context, no re-render cascade) + - Inline blocking script in
for FOUC prevention + - vi.stubGlobal for Node v25 localStorage/matchMedia in jsdom tests + +key-files: + created: + - src/components/ui/ThemeToggle.tsx + - src/components/ui/ThemeToggle.test.tsx + modified: + - src/index.css + - index.html + - src/App.tsx + +key-decisions: + - "Two-layer CSS token pattern: raw values in @layer base :root/.dark, @theme references via var() — required for .dark cascade to work" + - "vi.stubGlobal for localStorage and matchMedia in tests — Node v25 experimental WebStorage breaks standard Storage API in jsdom" + - "15 tokens (11 core + success/on-success/warning/on-warning) to cover ReviewStep semantic colors without hardcoded dark: prefixes" + - "Inline FOUC script placed before stylesheets in for maximum paint-blocking guarantee" + +patterns-established: + - "Token pattern: Always use @theme { --color-X: var(--r2b-X); } + @layer base :root/.dark for dynamic theming" + - "Test pattern: vi.stubGlobal('localStorage', mock) + vi.stubGlobal('matchMedia', mock) for Node v25 jsdom compatibility" + - "DOM theme toggle: applyTheme() directly mutates document.documentElement.classList — no useEffect, no Context" + +requirements-completed: [THEME-01, THEME-02] + +# Metrics +duration: 17min +completed: 2026-04-01 +--- + +# Phase 8, Plan 01: Theme Foundation Summary + +**MD3 color token system with 15 CSS custom properties, @custom-variant dark toggle, FOUC-free flash prevention script, and ThemeToggle segmented control component with 7 passing unit tests** + +## Performance + +- **Duration:** 17 min +- **Started:** 2026-04-01T02:32:49Z +- **Completed:** 2026-04-01T02:50:04Z +- **Tasks:** 2 (+ TDD RED commit) +- **Files modified:** 5 + +## Accomplishments + +- Full MD3 color token system in `src/index.css`: 15 token pairs (light/dark), wired to Tailwind utility classes via `@theme { --color-*: var(--r2b-*) }` two-layer pattern +- Flash prevention inline blocking script added to `index.html` `` before stylesheets; reads `r2b-theme` from localStorage before first CSS paint +- ThemeToggle segmented control: Light/Dark/System, writes localStorage + toggles `.dark` on ``, no React Context overhead +- ThemeToggle wired into App.tsx header flex row; `bg-gray-50` → `bg-surface`, `text-gray-900` → `text-on-surface` +- All 166 tests pass (159 pre-existing + 7 new ThemeToggle unit tests) + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Define MD3 color tokens and flash prevention script** - `6027552` (feat) +2. **Task 2 RED: Add failing tests for ThemeToggle** - `fa2c0d9` (test) +3. **Task 2 GREEN: Implement ThemeToggle and wire into App.tsx** - `074dc33` (feat) + +**Plan metadata:** _(docs commit follows)_ + +_Note: TDD task split into RED (fa2c0d9) and GREEN (074dc33) commits per TDD protocol_ + +## Files Created/Modified + +- `src/index.css` — @import + @custom-variant dark + @layer base with 15 token pairs + @theme mapping + body transition +- `index.html` — Inline blocking IIFE script reading r2b-theme from localStorage before paint +- `src/components/ui/ThemeToggle.tsx` — Segmented control: 3 buttons, DOM class toggle, localStorage write +- `src/components/ui/ThemeToggle.test.tsx` — 7 unit tests with localStorage/matchMedia stubs for Node v25 +- `src/App.tsx` — Import ThemeToggle, flex header row, bg-surface + text-on-surface token classes + +## Decisions Made + +- **Two-layer token pattern confirmed:** `@theme { --color-X: var(--r2b-X); }` + `@layer base :root/.dark { --r2b-X: