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-out

Deepest, most movement, fastest response

z:2Vignette±22px opposite cursor0.28s ease-out

Radial gradient overlay — lags behind photo

z:3Film grain±9px opposite cursor0.45s ease-out

SVG noise texture — barely moves, most lag

z:4Bottom scrimFixedNone

Linear gradient — no parallax

z:5Text±14px WITH cursor0.12s ease-out

Moves 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.3s
opacity012.0s[0.64, 0, 0.78, 0] expo-in0.3s

expo-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

PropTypeDefaultDescription
srcstringHero image URL. Loaded with next/image priority.
altstringAlt text for the hero image.
eyebrowstringSmall uppercase label above the headline.
headlineReactNodeHeadline — typically an <h1> with className="hero-headline".
cta{ label: string; href: string }Optional CTA button rendered below the headline.
classNamestring"hero-section"Root element class — controls sizing via globals.css.