feat(web): hero photo slideshow with Ken Burns pan + crossfade#67
Merged
Conversation
Replace the looped <video> hero with a Ken Burns photo slideshow: shuffled per load, 8s visible / 1.5s crossfade, ±2% random pan per photo, prefers-reduced-motion honored. Spec update + photo pipeline + HeroSlideshow component + Home wiring. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec change driving plans/hero-slideshow. Describes <HeroSlideshow />, timing (8s visible / 1.5s crossfade), pan envelope (±2% on scale 1.05→1.10), and prefers-reduced-motion handling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
optimize-hero-photos.sh: ImageMagick pipeline that resizes/center-crops to 1920x1280, strips EXIF, emits JPG (q82) + WebP (q80), and writes manifest.json. Re-running clears and regenerates deterministically. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HeroSlideshow shuffles the manifest on mount and cycles indefinitely (8s visible, 1.5s crossfade). Each layer mounts with a fresh randomized pan vector (±2% translate over a 1.05→1.10 scale ramp); the keyframes live in index.css. Next image is preloaded before the crossfade kicks off, capped at 3s so the cycle never stalls. usePrefersReducedMotion follows the matchMedia pattern from useOnline and disables only the pan animation — crossfades still play. Home swaps the never-loaded <video> for HeroSlideshow + a dark gradient overlay for legibility; hero headline + subhead go to white to read against the photos. Home.test.tsx mock now returns [] for /hero/manifest.json so the slideshow renders nothing in JSDOM. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
<video>hero on/with a<HeroSlideshow />that cycles through a 16-photo set (shuffled per page load): 8 s visible, 1.5 s crossfade, with each layer mounting under a fresh randomized ±2 % pan vector on a 1.05 → 1.10 scale ramp.prefers-reduced-motion: the Ken Burns pan is suppressed while the crossfade continues to run.apps/web/scripts/optimize-hero-photos.shpipeline (ImageMagick → 1920×1280 cover, EXIF stripped, JPG q82 + WebP q80) plus the first 16 optimized assets and amanifest.jsonindex.specs/screens/home.mdSection 1 to describe the slideshow as the new background substrate, per the repo's spec-first convention.Plan:
plans/hero-slideshow.md(status: in-progress— will flip todonein the merge commit with validation checked).Test plan
npm run -w apps/web test— 30/30 pass (Home.test.tsx mock now also handles/hero/manifest.json→[])npm run type-checkcleannpm run lintcleannpm run -w apps/web build—dist/hero/contains 16 JPG + 16 WebP +manifest.jsonmatchMediapatched to returnmatches: true, mid-crossfade inspection shows both layers at ~equal opacity withanimationName: "none"— crossfade preserved, pan suppressed🤖 Generated with Claude Code