--- phase: 08-theme-foundation plan: 02 subsystem: ui tags: [tailwindcss, css-custom-properties, dark-mode, theme, react] # Dependency graph requires: - phase: 08-01 provides: MD3 color token system, bg-surface/text-on-surface/bg-primary/etc. Tailwind utility classes provides: - Zero hardcoded Tailwind color classes in all 9 component files (BackendCard, FieldRenderer, PasswordField, AzureAuthToggle, SftpAuthToggle, DeploymentStep, OutputBlock, RemoteConfigStep, ReviewStep) - Full semantic token adoption: bg-primary, text-on-primary, border-outline, bg-surface-container, text-on-surface-container, text-success, text-warning, bg-surface-variant, text-on-surface-variant affects: [09-component-migration, 10-content-refinement, 11-polish] # Tech tracking tech-stack: added: [] patterns: - Mechanical find-and-replace migration: hardcoded Tailwind color classes -> semantic MD3 token utility classes - Opacity modifier pattern: text-on-surface-container/70, text-on-surface-container/50, bg-primary/10, border-primary/30, focus:ring-error/50, focus:ring-primary/50 - replace_all cautious: when two branches have visually identical but structurally different code, replace_all can miss second instance — verify grep after key-files: created: [] 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 key-decisions: - "Mechanical migration only — zero DOM structure changes, only className strings replaced" - "replace_all: true used where both branches of if/else had identical class strings, then post-verified with grep" - "ReviewStep warning colors: text-yellow-800 and text-yellow-900 both map to text-warning (single token covers both title and label)" patterns-established: - "Migration pattern: grep check after migration is mandatory — replace_all can silently miss occurrences in structurally different branches" - "Opacity pattern: text-on-surface-container/70 (helpText), /50 (muted icons), bg-primary/10 (info backgrounds), border-primary/30 (info borders), focus:ring-error/50 and focus:ring-primary/50 (focus rings)" requirements-completed: [THEME-01, THEME-02] # Metrics duration: 6min completed: 2026-04-01 --- # Phase 8, Plan 02: Component Color Migration Summary **63 hardcoded Tailwind color classes migrated to semantic MD3 tokens across 9 components — entire UI now responds to theme changes via CSS variable cascade with zero dark: prefixes** ## Performance - **Duration:** 6 min - **Started:** 2026-04-01T05:03:01Z - **Completed:** 2026-04-01T05:09:00Z - **Tasks:** 2 completed (Task 3 is a human-verify checkpoint — paused) - **Files modified:** 9 ## Accomplishments - All 63 hardcoded color class occurrences removed from 9 component files (BackendCard, FieldRenderer, PasswordField, AzureAuthToggle, SftpAuthToggle, DeploymentStep, OutputBlock, RemoteConfigStep, ReviewStep) - Semantic tokens applied: primary family (bg-primary, text-on-primary, text-primary, border-primary, bg-primary/10, border-primary/30), surface family (bg-surface, bg-surface-container, bg-surface-variant, text-on-surface, text-on-surface-container, text-on-surface-variant), error (border-error, text-error, focus:ring-error/50), success (text-success, bg-success/10), warning (text-warning, bg-warning/10, border-warning), outline (border-outline, focus:ring-primary/50) - All 166 tests pass without modification (existing tests use semantic queries, not class selectors) - grep -rn for all hardcoded color patterns in src/components/ and src/App.tsx returns 0 matches ## Task Commits Each task was committed atomically: 1. **Task 1: Migrate UI primitives (BackendCard, FieldRenderer, PasswordField)** - `4e30f2e` (feat) 2. **Task 2: Migrate wizard components (toggles, steps, output block)** - `1ac32ea` (feat) 3. **Task 3: Visual verification** - checkpoint: human-verify (pending) **Plan metadata:** _(docs commit follows)_ ## Files Created/Modified - `src/components/ui/BackendCard.tsx` — selected/unselected card border/bg colors, name/description text colors - `src/components/ui/FieldRenderer.tsx` — label, required asterisk, tooltip toggle, tooltip box, input borders, focus rings, help text, error message (both select and text-input branches) - `src/components/ui/PasswordField.tsx` — label, tooltip toggle, tooltip box, input borders, focus ring, eye icon, help text, error message - `src/components/wizard/AzureAuthToggle.tsx` — segmented control border, active/inactive tab colors - `src/components/wizard/SftpAuthToggle.tsx` — auth method label, info tooltip toggle + box, segmented control border, active/inactive tab colors - `src/components/wizard/DeploymentStep.tsx` — Back and Next navigation buttons - `src/components/wizard/OutputBlock.tsx` — label, Copy/Download button borders, code pre background/text - `src/components/wizard/RemoteConfigStep.tsx` — Back and Next navigation buttons - `src/components/wizard/ReviewStep.tsx` — client-side notice, security warning box (bg/border/title/label), Back button, Download All ZIP button ## Decisions Made - **Mechanical migration only:** Zero DOM structure changes — only className string replacements as specified by the plan's migration mapping. Ensures no regressions. - **Both yellow variants -> text-warning:** `text-yellow-800` (warning title) and `text-yellow-900` (warning label) both map to `text-warning`. Single token is sufficient and avoids creating artificial token variants. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Bug] FieldRenderer text branch not fully migrated by replace_all** - **Found during:** Task 2 verification grep - **Issue:** FieldRenderer has two conditional branches (select and text) with visually identical but structurally different JSX. The `replace_all` edits during Task 1 missed the text branch's label className and tooltip paragraph because the select branch was matched first (different aria-label attribute present). Post-task grep revealed 2 remaining occurrences. - **Fix:** Added targeted Edit for the text branch's label (`text-gray-700` -> `text-on-surface-container`) and tooltip paragraph (`text-blue-700 bg-blue-50 border border-blue-200` -> `text-primary bg-primary/10 border border-primary/30`). - **Files modified:** `src/components/ui/FieldRenderer.tsx` - **Verification:** grep returns 0 matches after fix; 166 tests still pass - **Committed in:** `1ac32ea` (Task 2 commit, staged with wizard files) --- **Total deviations:** 1 auto-fixed (Rule 1 - missed occurrence in dual-branch component) **Impact on plan:** Caught by mandatory post-migration grep check. Fix was a 2-line edit. No scope creep. ## Issues Encountered - FieldRenderer's dual-branch structure (select vs text-input, both with identical tooltip JSX) caused a missed replacement when using `replace_all`. The post-migration grep verification step is confirmed mandatory for any component with if/else branches that share className strings. ## User Setup Required None — no external service configuration required. ## Next Phase Readiness - Zero hardcoded Tailwind color classes remain in `src/components/` and `src/App.tsx` - The entire UI is now driven by CSS custom properties — light/dark theme switching is fully functional via the `.dark` class on `` - Task 3 is a human-verify checkpoint: user must visually confirm both light and dark themes render correctly in the browser (`npm run dev`, http://localhost:5173) - After visual approval, Phase 8 is complete (THEME-01 and THEME-02 requirements satisfied) - No blockers for Phase 9+ (content refinement, polish) --- *Phase: 08-theme-foundation* *Completed: 2026-04-01*