` with three buttons
+- Each button: `type="button"`, `aria-pressed={theme === v}`, `onClick={() => select(v)}`
+- Active state: `bg-primary text-on-primary font-medium`
+- Inactive state: `bg-surface-container text-on-surface-container hover:bg-surface`
+- Icons: Sun for Light, Moon for Dark, Monitor for System (use Unicode or simple SVG inline icons)
+- Outer div: `flex rounded-md border border-outline overflow-hidden text-sm`
+
+Run tests — they must PASS (GREEN).
+
+**App.tsx** updates:
+1. Add `import { ThemeToggle } from './components/ui/ThemeToggle';`
+2. Change the header from centered h1 to a flex row: `
`
+3. h1 keeps `text-3xl font-bold` but change `text-gray-900` to `text-on-surface`, remove `text-center`
+4. Add `
` as second child in the flex row
+5. Change outer div `bg-gray-50` to `bg-surface`
+6. Change h1 `text-gray-900` to `text-on-surface`
+
+Run full test suite to confirm no regressions.
+
+
+ cd C:/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npm test 2>&1 | tail -20
+
+
ThemeToggle.test.tsx has 6+ passing tests. ThemeToggle renders segmented control with Light/Dark/System. App.tsx header shows title left and ThemeToggle right. All existing tests still pass. App outer div uses bg-surface, h1 uses text-on-surface.
+
+
+
+
+
+1. `npx vite build` completes without errors
+2. `npm test` — all tests pass (existing + new ThemeToggle tests)
+3. `grep -c "@theme" src/index.css` returns 1 (theme block exists)
+4. `grep -c "r2b-theme" index.html` returns at least 1 (inline script present)
+5. `grep "ThemeToggle" src/App.tsx` confirms component is wired in
+
+
+
+- Token system produces working Tailwind utility classes: bg-surface, text-on-surface, bg-primary, etc.
+- Dark mode toggle works: clicking Dark applies .dark class, Light removes it, System checks OS preference
+- Flash prevention: inline script in index.html reads localStorage before CSS paints
+- ThemeToggle has passing unit tests covering all three modes and DOM class toggling
+- No regression in existing test suite
+
+
+
diff --git a/.planning/phases/08-theme-foundation/08-02-PLAN.md b/.planning/phases/08-theme-foundation/08-02-PLAN.md
new file mode 100644
index 0000000..b06c44c
--- /dev/null
+++ b/.planning/phases/08-theme-foundation/08-02-PLAN.md
@@ -0,0 +1,262 @@
+---
+phase: 08-theme-foundation
+plan: 02
+type: execute
+wave: 2
+depends_on:
+ - "08-01"
+files_modified:
+ - src/components/ui/BackendCard.tsx
+ - src/components/ui/FieldRenderer.tsx
+ - src/components/ui/PasswordField.tsx
+ - src/components/wizard/AzureAuthToggle.tsx
+ - src/components/wizard/SftpAuthToggle.tsx
+ - src/components/wizard/DeploymentStep.tsx
+ - src/components/wizard/OutputBlock.tsx
+ - src/components/wizard/RemoteConfigStep.tsx
+ - src/components/wizard/ReviewStep.tsx
+autonomous: false
+requirements:
+ - THEME-01
+ - THEME-02
+
+must_haves:
+ truths:
+ - "No hardcoded Tailwind color classes (gray-*, blue-*, red-*, green-*, yellow-*) remain in any of the 10 migrated files (App.tsx already done in Plan 01)"
+ - "All components render correctly using semantic token classes (bg-surface, text-on-surface, bg-primary, etc.)"
+ - "Components look correct in both light and dark mode without any dark: prefixes"
+ - "All existing tests pass without modification (tests use semantic queries, not class selectors)"
+ artifacts:
+ - path: "src/components/ui/BackendCard.tsx"
+ provides: "Migrated backend selection card"
+ min_lines: 15
+ - path: "src/components/ui/FieldRenderer.tsx"
+ provides: "Migrated form field renderer"
+ min_lines: 50
+ - path: "src/components/ui/PasswordField.tsx"
+ provides: "Migrated password input"
+ min_lines: 30
+ - path: "src/components/wizard/AzureAuthToggle.tsx"
+ provides: "Migrated Azure auth method toggle"
+ min_lines: 20
+ - path: "src/components/wizard/SftpAuthToggle.tsx"
+ provides: "Migrated SFTP auth method toggle"
+ min_lines: 25
+ - path: "src/components/wizard/DeploymentStep.tsx"
+ provides: "Migrated deployment step"
+ min_lines: 50
+ - path: "src/components/wizard/OutputBlock.tsx"
+ provides: "Migrated code output block"
+ min_lines: 20
+ - path: "src/components/wizard/RemoteConfigStep.tsx"
+ provides: "Migrated remote config step"
+ min_lines: 50
+ - path: "src/components/wizard/ReviewStep.tsx"
+ provides: "Migrated review step with success/warning tokens"
+ min_lines: 60
+ key_links:
+ - from: "src/index.css"
+ to: "all 9 migrated component files"
+ via: "CSS variable cascade through Tailwind utility classes"
+ pattern: "bg-surface|text-on-surface|border-outline|bg-primary|text-on-primary"
+---
+
+
+Migrate all 63 hardcoded Tailwind color classes across 9 component files to semantic MD3 token classes.
+
+Purpose: Complete THEME-01 by eliminating all hardcoded color references. After this plan, the entire UI responds to theme changes via the CSS variable cascade established in Plan 01.
+Output: 9 migrated component files with zero hardcoded color classes, all tests passing.
+
+
+
+@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
+@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/08-theme-foundation/08-CONTEXT.md
+@.planning/phases/08-theme-foundation/08-RESEARCH.md
+@.planning/phases/08-theme-foundation/08-01-SUMMARY.md
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Task 1: Migrate UI primitives (BackendCard, FieldRenderer, PasswordField)
+ src/components/ui/BackendCard.tsx, src/components/ui/FieldRenderer.tsx, src/components/ui/PasswordField.tsx
+
+Migrate all hardcoded color classes to semantic tokens. This is mechanical find-and-replace following the mapping. Do NOT change any DOM structure, only className strings.
+
+**BackendCard.tsx** (5 occurrences):
+- `border-blue-600 bg-blue-50` (selected) -> `border-primary bg-primary/10`
+- `border-gray-200 bg-white hover:border-blue-400 hover:bg-gray-50` (unselected) -> `border-outline bg-surface-container hover:border-primary hover:bg-surface`
+- `text-gray-900` -> `text-on-surface`
+- `text-gray-500` -> `text-on-surface-container/70`
+
+**FieldRenderer.tsx** (17 occurrences):
+- `text-gray-700` (labels) -> `text-on-surface-container`
+- `text-red-500` (required asterisk) -> `text-error`
+- `text-blue-500 hover:text-blue-700` (toggle link) -> `text-primary hover:text-primary`
+- `text-blue-700 bg-blue-50 border border-blue-200` (tooltip/info) -> `text-primary bg-primary/10 border border-primary/30`
+- `border-red-500 focus:ring-red-300` (error state) -> `border-error focus:ring-error/50`
+- `border-gray-300 focus:ring-blue-300` (normal state) -> `border-outline focus:ring-primary/50`
+- `text-gray-500` (helpText) -> `text-on-surface-container/70`
+- `text-red-600` (error message) -> `text-error`
+- Apply the same mapping to both the text-input branch and the select branch (they have identical color patterns)
+
+**PasswordField.tsx** (8 occurrences):
+- `text-gray-700` -> `text-on-surface-container`
+- `text-blue-500 hover:text-blue-700` -> `text-primary hover:text-primary`
+- `text-blue-700 bg-blue-50 border border-blue-200` -> `text-primary bg-primary/10 border border-primary/30`
+- `border-red-500 focus:ring-red-300` -> `border-error focus:ring-error/50`
+- `border-gray-300 focus:ring-blue-300` -> `border-outline focus:ring-primary/50`
+- `text-gray-400 hover:text-gray-700` (eye icon) -> `text-on-surface-container/50 hover:text-on-surface-container`
+- `text-gray-500` (helpText) -> `text-on-surface-container/70`
+- `text-red-600` (error message) -> `text-error`
+
+Run `npm test` after each file to catch any regressions immediately.
+
+
+ cd C:/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npm test 2>&1 | tail -20
+
+ BackendCard.tsx has 0 hardcoded color classes. FieldRenderer.tsx has 0 hardcoded color classes. PasswordField.tsx has 0 hardcoded color classes. All tests pass.
+
+
+
+ Task 2: Migrate wizard components (toggles, steps, output block)
+ src/components/wizard/AzureAuthToggle.tsx, src/components/wizard/SftpAuthToggle.tsx, src/components/wizard/DeploymentStep.tsx, src/components/wizard/OutputBlock.tsx, src/components/wizard/RemoteConfigStep.tsx, src/components/wizard/ReviewStep.tsx
+
+Continue the mechanical migration for all wizard components. Do NOT change DOM structure, only className strings.
+
+**AzureAuthToggle.tsx** (7 occurrences):
+- `border-gray-300` -> `border-outline`
+- `bg-blue-600 text-white` (active tab) -> `bg-primary text-on-primary`
+- `bg-white text-gray-700 hover:bg-gray-50` (inactive tab) -> `bg-surface-container text-on-surface-container hover:bg-surface`
+
+**SftpAuthToggle.tsx** (10 occurrences):
+- `text-gray-700` (label) -> `text-on-surface-container`
+- `text-blue-500 hover:text-blue-700` (toggle link) -> `text-primary hover:text-primary`
+- `text-blue-700 bg-blue-50 border border-blue-200` (info) -> `text-primary bg-primary/10 border border-primary/30`
+- `border-gray-300` -> `border-outline`
+- `bg-blue-600 text-white` (active) -> `bg-primary text-on-primary`
+- `bg-white text-gray-700 hover:bg-gray-50` (inactive) -> `bg-surface-container text-on-surface-container hover:bg-surface`
+
+**DeploymentStep.tsx** (2 occurrences):
+- `border border-gray-300 ... hover:bg-gray-50` (Back button) -> `border border-outline ... hover:bg-surface`
+- `bg-blue-600 text-white ... hover:bg-blue-700` (Next button) -> `bg-primary text-on-primary ... hover:bg-primary`
+
+**OutputBlock.tsx** (4 occurrences):
+- `text-gray-700` (label) -> `text-on-surface-container`
+- `border-gray-300` (action buttons, 2 occurrences) -> `border-outline`
+- `bg-gray-900 text-gray-100` (code pre) -> `bg-surface-variant text-on-surface-variant`
+
+**RemoteConfigStep.tsx** (2 occurrences):
+- `border border-gray-300 ... hover:bg-gray-50` (Back button) -> `border border-outline ... hover:bg-surface`
+- `bg-blue-600 text-white ... hover:bg-blue-700` (Next button) -> `bg-primary text-on-primary ... hover:bg-primary`
+
+**ReviewStep.tsx** (6 occurrences):
+- `text-green-700 bg-green-50` (client-side notice) -> `text-success bg-on-success`
+- `bg-yellow-50 border border-yellow-300` (security warning box) -> `bg-on-warning border border-warning`
+- `text-yellow-800` (warning title) -> `text-warning`
+- `text-yellow-900` (warning label) -> `text-warning`
+- `border border-gray-300 ... hover:bg-gray-50` (Back button) -> `border border-outline ... hover:bg-surface`
+- `bg-blue-600 text-white ... hover:bg-blue-700` (Download button) -> `bg-primary text-on-primary ... hover:bg-primary`
+
+After all files migrated, run verification grep to confirm zero hardcoded color classes remain.
+
+
+ cd C:/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npm test 2>&1 | tail -20 && echo "---GREP CHECK---" && grep -rn "text-gray-\|bg-gray-\|border-gray-\|bg-blue-\|text-blue-\|border-blue-\|text-white\|bg-white\|text-red-\|border-red-\|text-green-\|bg-green-\|bg-yellow-\|text-yellow-\|border-yellow-\|hover:bg-gray-\|hover:bg-blue-\|hover:border-blue-\|focus:ring-red-\|focus:ring-blue-" src/components/ src/App.tsx 2>/dev/null; echo "Exit: $?"
+
+ All 9 component files migrated. Grep for hardcoded color classes in src/components/ and src/App.tsx returns 0 matches. All existing tests pass without modification.
+
+
+
+ Task 3: Visual verification of theme system across all wizard steps
+ none
+
+Present the completed theme system for user visual verification. Start the dev server if not already running.
+
+ Complete MD3 token system with dark mode toggle and all 63 hardcoded color classes migrated to semantic tokens across 10 files.
+
+ 1. Run `npm run dev` and open http://localhost:5173
+ 2. Verify the app renders correctly in light mode (indigo primary accents, gray surfaces)
+ 3. Click "Dark" in the theme toggle (top-right, next to "Ready2Blob" title) -- entire UI should switch to dark palette instantly
+ 4. Click "Light" -- UI returns to light palette
+ 5. Click "System" -- follows your OS preference
+ 6. With "Dark" selected, hard-reload the page (Ctrl+Shift+R) -- should load directly in dark mode with NO flash of light theme
+ 7. Navigate through all 4 wizard steps checking:
+ - Backend cards have correct border/background colors in both themes
+ - Form inputs have visible borders and labels in both themes
+ - Code output blocks (ReviewStep) are readable in both themes
+ - Warning/success notices in ReviewStep are visible in both themes
+ - All buttons (Back/Next/Download) are visible and styled in both themes
+
+ User confirms visual correctness
+ User approves visual appearance of both light and dark themes across all wizard steps.
+ Type "approved" or describe any visual issues
+
+
+
+
+
+1. `npm test` -- all tests pass (existing + ThemeToggle tests from Plan 01)
+2. `grep -rn "text-gray-\|bg-gray-\|border-gray-\|bg-blue-\|text-blue-\|border-blue-\|text-white\|bg-white\|text-red-\|border-red-\|text-green-\|bg-green-\|bg-yellow-\|text-yellow-\|border-yellow-" src/components/ src/App.tsx` returns 0 matches
+3. `npx vite build` completes without errors
+
+
+
+- Zero hardcoded Tailwind color classes remain in src/components/ and src/App.tsx
+- All 9 component files use only semantic token classes (bg-surface, text-on-surface, bg-primary, etc.)
+- No `dark:` utility prefixes used anywhere -- the CSS variable cascade handles both themes
+- All existing tests pass without modification
+- Visual verification confirms both light and dark mode render correctly across all wizard steps
+
+
+