diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 4ca359a..096437a 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -51,7 +51,10 @@ 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**: TBD +**Plans:** 2 plans +Plans: +- [ ] 08-01-PLAN.md — Token system, flash prevention, ThemeToggle component +- [ ] 08-02-PLAN.md — Color class migration across all components ### Phase 9: MD3 Components **Goal**: All interactive elements use MD3-styled primitives with consistent visual language across the entire wizard @@ -98,7 +101,7 @@ Full phase details: [.planning/milestones/v1.1-ROADMAP.md](milestones/v1.1-ROADM | 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/? | Not started | - | +| 8. Theme Foundation | v1.2 | 0/2 | Planning | - | | 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/phases/08-theme-foundation/08-01-PLAN.md b/.planning/phases/08-theme-foundation/08-01-PLAN.md new file mode 100644 index 0000000..3f84e6d --- /dev/null +++ b/.planning/phases/08-theme-foundation/08-01-PLAN.md @@ -0,0 +1,225 @@ +--- +phase: 08-theme-foundation +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - src/index.css + - index.html + - src/components/ui/ThemeToggle.tsx + - src/components/ui/ThemeToggle.test.tsx + - src/App.tsx +autonomous: true +requirements: + - THEME-01 + - THEME-02 + +must_haves: + truths: + - "Tailwind utility classes bg-surface, text-on-surface, bg-primary, text-on-primary, border-outline etc. are available and resolve to correct hex values" + - "Adding .dark class to html element switches all token values to dark palette" + - "ThemeToggle renders three options: Light, Dark, System" + - "Clicking a theme option applies the correct class to document.documentElement and persists to localStorage" + - "On hard reload with r2b-theme=dark in localStorage, the .dark class is present before React mounts (no flash)" + artifacts: + - path: "src/index.css" + provides: "MD3 color token definitions (light + dark) and @theme mapping" + contains: "@theme" + - path: "index.html" + provides: "Inline blocking script for flash prevention" + contains: "r2b-theme" + - path: "src/components/ui/ThemeToggle.tsx" + provides: "Segmented theme toggle component" + exports: ["ThemeToggle"] + - path: "src/components/ui/ThemeToggle.test.tsx" + provides: "Unit tests for ThemeToggle behavior" + contains: "describe" + - path: "src/App.tsx" + provides: "ThemeToggle wired into header, bg-surface applied to shell" + contains: "ThemeToggle" + key_links: + - from: "src/index.css" + to: "src/components/ui/ThemeToggle.tsx" + via: ".dark class on html toggles CSS variable cascade" + pattern: "classList\\.toggle.*dark" + - from: "index.html" + to: "localStorage" + via: "inline script reads r2b-theme before paint" + pattern: "localStorage\\.getItem.*r2b-theme" + - from: "src/App.tsx" + to: "src/components/ui/ThemeToggle.tsx" + via: "import and render in header" + pattern: "import.*ThemeToggle" +--- + + +Build the MD3 color token system, dark mode infrastructure, and ThemeToggle component. + +Purpose: Establish the CSS custom property foundation that all components will reference instead of hardcoded Tailwind colors. This is the infrastructure layer that Plan 02 (migration) and Phases 9-11 depend on. +Output: Working token system with dark mode toggle, flash prevention, and ThemeToggle component with unit tests. + + + +@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 + + + + + +From src/App.tsx (current structure): +```tsx +import { useWizard } from './store/context'; +import { WizardProvider } from './store/context'; +import { StepIndicator } from './components/wizard/StepIndicator'; +// ... step imports + +function WizardShell() { + const { state } = useWizard(); + // ... + return ( +
+
+

Ready2Blob

+ +
{CurrentStep}
+
+
+ ); +} +``` +
+
+ + + + + Task 1: Define MD3 color tokens and flash prevention script + src/index.css, index.html + +**src/index.css** — Replace the single `@import "tailwindcss"` line with the complete token system. Structure (in this exact order): + +1. `@import "tailwindcss";` +2. `@custom-variant dark (&:where(.dark, .dark *));` — immediately after import, before any @layer or @theme +3. `@layer base` block with `:root` (light values) and `.dark` (dark values) for all tokens: + - `--r2b-primary`: light #4338CA, dark #A5B4FC + - `--r2b-on-primary`: light #FFFFFF, dark #1E1B4B + - `--r2b-surface`: light #F9FAFB, dark #111827 + - `--r2b-on-surface`: light #111827, dark #F9FAFB + - `--r2b-surface-variant`: light #1F2937, dark #0F172A (code blocks) + - `--r2b-on-surface-variant`: light #F3F4F6, dark #E5E7EB + - `--r2b-surface-container`: light #FFFFFF, dark #1F2937 (cards/forms) + - `--r2b-on-surface-container`: light #374151, dark #D1D5DB + - `--r2b-outline`: light #D1D5DB, dark #4B5563 + - `--r2b-error`: light #EF4444, dark #FCA5A5 + - `--r2b-on-error`: light #FFFFFF, dark #7F1D1D + - Also add warning/success tokens per research recommendation: + - `--r2b-success`: light #15803D (green-700), dark #86EFAC (green-300) + - `--r2b-on-success`: light #F0FDF4 (green-50), dark #14532D (green-900) + - `--r2b-warning`: light #D97706 (amber-600), dark #FCD34D (amber-300) + - `--r2b-on-warning`: light #FFFBEB (amber-50), dark #78350F (amber-900) + Also add `body` transition in `@layer base`: `transition: background-color 200ms ease, color 200ms ease, border-color 200ms ease` with `@media (prefers-reduced-motion: reduce)` wrapper that sets `transition: none`. +4. `@theme` block mapping each `--color-*` to `var(--r2b-*)` for all 15 tokens (the 11 core + warning, on-warning, success, on-success). + +**index.html** — Add inline blocking script inside ``, BEFORE any stylesheet or script tags. The script: +1. Reads `localStorage.getItem('r2b-theme')` +2. If `'dark'` -> effective = dark. If `'light'` -> effective = light. Otherwise -> check `window.matchMedia('(prefers-color-scheme: dark)').matches` +3. If effective is dark, add `.dark` to `document.documentElement.classList` +4. Wrap in try/catch for private browsing mode safety + +Use vanilla JS (no arrow functions) for maximum browser compat in the inline script. Use the exact pattern from 08-RESEARCH.md Pattern 2. + + + cd C:/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npx vite build 2>&1 | tail -5 + + src/index.css contains @custom-variant, @layer base with :root and .dark blocks (15 token pairs), @theme mapping all tokens. index.html has inline blocking script reading r2b-theme from localStorage. Vite build succeeds with no errors. + + + + Task 2: Create ThemeToggle component with tests and wire into App.tsx + src/components/ui/ThemeToggle.tsx, src/components/ui/ThemeToggle.test.tsx, src/App.tsx + + - ThemeToggle renders three buttons: Light, Dark, System + - Each button has role="button" and aria-pressed reflecting current selection + - The component group has aria-label="Theme" + - Default selection is "system" when localStorage is empty + - Clicking "Dark" adds .dark class to document.documentElement + - Clicking "Light" removes .dark class from document.documentElement + - Clicking any option writes the value to localStorage key "r2b-theme" + - Active button uses bg-primary text-on-primary classes + - Inactive buttons use bg-surface-container text-on-surface-container classes + + +**ThemeToggle.test.tsx** (RED first): +Write tests using Vitest + jsdom (add `// @vitest-environment jsdom` pragma). Test: +1. Renders three buttons with text containing "Light", "Dark", "System" +2. Default state is "System" (aria-pressed="true" on System button) when localStorage is empty +3. Clicking "Dark" -> `document.documentElement.classList.contains('dark')` is true +4. Clicking "Light" -> `document.documentElement.classList.contains('dark')` is false +5. Clicking "Dark" -> `localStorage.getItem('r2b-theme')` === `'dark'` +6. Active button has aria-pressed="true", others have aria-pressed="false" + +Run tests — they must FAIL (RED). + +**ThemeToggle.tsx** (GREEN): +Create the component following 08-RESEARCH.md Pattern 3: +- Type `ThemeValue = 'light' | 'dark' | 'system'` +- `STORAGE_KEY = 'r2b-theme'` +- `getStored()` reads from localStorage, returns ThemeValue (default 'system') +- `applyTheme(value)` toggles `.dark` on `document.documentElement` and writes to localStorage +- `useState(getStored)` for lazy init +- Render as a segmented control: `
` 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 + + + +After completion, create `.planning/phases/08-theme-foundation/08-01-SUMMARY.md` + 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 + + + +After completion, create `.planning/phases/08-theme-foundation/08-02-SUMMARY.md` +