Component
HeroParallax
Full-viewport hero with 5 independent depth planes driven by mousemove. Clip-path reveal on mount with opacity fade-in. Implemented in apps/web/components/hero-parallax.tsx.
Depth planes
z:1Photo
±40px opposite cursor0.15s ease-outDeepest, most movement, fastest response
z:2Vignette
±22px opposite cursor0.28s ease-outRadial gradient overlay — lags behind photo
z:3Film grain
±9px opposite cursor0.45s ease-outSVG noise texture — barely moves, most lag
z:4Bottom scrim
FixedNoneLinear gradient — no parallax
z:5Text
±14px WITH cursor0.12s ease-outMoves forward — snaps fast, creates pop
Mount animation
The section uses Framer Motion with two independent property timings to create a silky reveal:
clipPathinset(0 100% 0 0) → inset(0 0% 0 0)1.5s[0.22, 1, 0.36, 1] expo-out0.3sopacity0 → 12.0s[0.64, 0, 0.78, 0] expo-in0.3sexpo-in on opacity means the image stays dark while the wipe opens, then snaps bright at the end — curtain effect.
Usage
tsx
import { HeroParallax } from '@/components/hero-parallax'
<HeroParallax
src="https://images.unsplash.com/photo-xxx?w=1920"
alt="Columbia River Gorge"
eyebrow="Documentary Photography"
headline={<h1 className="hero-headline">The World<br />Through Glass</h1>}
cta={{ label: 'View the Work', href: '/landscape' }}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | — | Hero image URL. Loaded with next/image priority. |
| alt | string | — | Alt text for the hero image. |
| eyebrow | string | — | Small uppercase label above the headline. |
| headline | ReactNode | — | Headline — typically an <h1> with className="hero-headline". |
| cta | { label: string; href: string } | — | Optional CTA button rendered below the headline. |
| className | string | "hero-section" | Root element class — controls sizing via globals.css. |