framer-motion's useReducedMotion() hook caches its result at the module level
and never re-reads window.matchMedia on subsequent calls. This caused the second
test to fail because the cache was locked to the first test's matchMedia mock.
Fixed by using vi.doMock() with vi.resetModules() to mock useReducedMotion
directly with the desired value for each test, ensuring each test runs with a
fresh module context where the hook returns the expected value.
Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH
- Implements Hero3D component with CSS 3D transforms
- Uses framer-motion's useScroll/useTransform for scroll-linked rotation
- Respects prefers-reduced-motion preference for static rendering
- Component structure: wrapper with perspective, inner motif with 3D rotation
- Test setup: added window.matchMedia mock for framer-motion compatibility
Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH
- Create Layout component that renders Nav > Outlet > Footer
- Wire Layout as parent route in App.tsx to wrap all page routes
- Update App.test.tsx to verify Nav/Footer render on every route
Claude-Session: https://claude.ai/code
Implement ScrollReveal component that fades and rises sections into view
on scroll with framer-motion. Respects prefers-reduced-motion by
rendering children unanimated when that setting is enabled.
- Add ScrollReveal component with fade+rise animation on scroll
- Add test coverage for both animated and reduced-motion paths
- Add IntersectionObserver mock to test setup
Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH