--- phase: 11-polish-responsiveness plan: 02 subsystem: ui tags: [react, tailwind, css-animation, react-hook-form, accessibility, reduced-motion] # Dependency graph requires: - phase: 11-01 provides: focus rings and BackendCard hover states (polish foundation) - phase: 10-content-clarity provides: App.tsx WizardShell structure with step content wrapper provides: - CSS @keyframes step-in animation with 200ms ease-out fade/slide-up on step change - prefers-reduced-motion guard disabling animation for accessibility - Auto-scroll to first errored field on validation failure in BackendSelectionStep and RemoteConfigStep affects: [11-03, any future plan modifying App.tsx WizardShell or wizard form steps] # Tech tracking tech-stack: added: [] patterns: - "key={state.currentStep} on step wrapper forces React remount, triggering CSS animation from initial state" - "@theme --animate-step-in token pattern for Tailwind v4 custom animation utility" - "onInvalidSubmit second arg to handleSubmit for scroll-to-error without JS media query listener" - "document.getElementById(firstKey)?.scrollIntoView for registry-driven field error targeting" key-files: created: [] modified: - src/index.css - src/App.tsx - src/components/wizard/BackendSelectionStep.tsx - src/components/wizard/BackendSelectionStep.test.tsx - src/components/wizard/RemoteConfigStep.tsx - src/components/wizard/RemoteConfigStep.test.tsx key-decisions: - "CSS-only reduced-motion guard via @media (prefers-reduced-motion: reduce) — no JS listener needed" - "key={state.currentStep} on step wrapper (not on step components themselves) to trigger animation" - "onInvalidSubmit hardcodes getElementById('remote-name') in BackendSelectionStep — only one validatable field exists" - "FieldErrors type added to react-hook-form imports in RemoteConfigStep for typed errors param" patterns-established: - "Tailwind v4 custom animation: register --animate-X in @theme, define @keyframes, add reduced-motion guard" - "react-hook-form scroll-to-error: pass onInvalidSubmit as second arg to handleSubmit" requirements-completed: [POLISH-03, POLISH-04] # Metrics duration: 3min completed: 2026-04-01 --- # Phase 11 Plan 02: Polish & Responsiveness — Animation & Scroll-to-Error Summary **CSS step-in animation (200ms fade/slide-up) with reduced-motion guard, plus auto-scroll to first errored form field via react-hook-form onInvalidSubmit handler** ## Performance - **Duration:** 3 min - **Started:** 2026-04-01T11:28:03Z - **Completed:** 2026-04-01T11:31:00Z - **Tasks:** 2 (Task 2 used TDD: RED → GREEN) - **Files modified:** 6 ## Accomplishments - Wizard step transitions now play a 200ms opacity/translateY(8px) fade-slide animation on step change - Users with `prefers-reduced-motion` enabled see zero animation (declarative CSS guard, no JS) - BackendSelectionStep scrolls to `remote-name` input when form submitted with invalid/empty name - RemoteConfigStep scrolls to the first errored field (by key) when form submitted with missing required fields - Both scroll behaviors verified by unit tests with `vi.fn()` mock on `Element.prototype.scrollIntoView` - Full test suite: 204 tests pass, 0 failures, 0 regressions ## Task Commits Each task was committed atomically: 1. **Task 1: Step transition animation with reduced-motion guard** - `9af58bc` (feat) 2. **Task 2 RED: Failing tests for auto-scroll to first error** - `8b7daa8` (test) 3. **Task 2 GREEN: Auto-scroll to first errored field implementation** - `90cdeda` (feat) **Plan metadata:** (docs commit follows) _Note: Task 2 used TDD — separate test commit (RED) then implementation commit (GREEN)._ ## Files Created/Modified - `src/index.css` - Added `--animate-step-in` token in `@theme`, `@keyframes step-in`, and `@media prefers-reduced-motion: reduce` guard - `src/App.tsx` - Step content wrapper: added `key={state.currentStep}` and `animate-step-in` class - `src/components/wizard/BackendSelectionStep.tsx` - Added `onInvalidSubmit` scrolling to `remote-name`, wired to form `onSubmit` and `handleCardClick` - `src/components/wizard/BackendSelectionStep.test.tsx` - Added `scrollIntoView` mock in `beforeEach`, added POLISH-04 scroll test - `src/components/wizard/RemoteConfigStep.tsx` - Added `FieldErrors` import, `onInvalidSubmit` scrolling to first error key, wired to form `onSubmit` - `src/components/wizard/RemoteConfigStep.test.tsx` - Added `scrollIntoView` mock in `beforeEach`, added POLISH-04 scroll test ## Decisions Made - CSS-only reduced-motion guard via `@media (prefers-reduced-motion: reduce)` — consistent with existing `prefers-reduced-motion: no-preference` pattern at line 45 of index.css. No JS listener. - `key={state.currentStep}` on the step content wrapper div (not on step components themselves) — forces React to unmount/remount the wrapper, reliably triggering the CSS animation from its `from` state on every step change. - BackendSelectionStep `onInvalidSubmit` uses `getElementById('remote-name')` directly (hardcoded) — only one validatable field exists in that step, no dynamic key needed. - Added `FieldErrors` to react-hook-form imports in RemoteConfigStep (alongside existing `FieldError`) for typed `errors` parameter in `onInvalidSubmit`. ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered None. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - POLISH-03 (step animation) and POLISH-04 (scroll-to-error) are complete - Phase 11 plan 02 satisfies all must_haves and success criteria - Ready for plan 11-03 if it exists, or phase 11 completion --- *Phase: 11-polish-responsiveness* *Completed: 2026-04-01*