Component

HeroParallax

Full-viewport hero with 5 independent depth planes driven by mousemove. Each layer has a distinct transition duration — the lag differential between layers creates the sense of depth. Clip-path wipe reveal on mount with opacity fade-in.

Preview

Documentary Photography

The World Through Glass

View the Work
Move cursor to parallax

No CTA

Documentary Photography

The World Through Glass

Move cursor to parallax

Depth planes

z:1Photo±40px opposite cursor0.15s ease-out

Deepest plane — most movement, fastest response

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

Radial gradient — 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

Foreground — moves forward, snaps fast

Mount animation

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

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, href }Optional CTA button (frosted glass variant)
classNamestring"hero-section"Root class — controls sizing via globals.css

Usage

tsx
import { HeroParallax } from '@/components/hero-parallax'

<HeroParallax
  src="https://cdn.sanity.io/images/orbu72vm/production/..."
  alt="Columbia River Gorge at golden hour"
  eyebrow="Documentary Photography"
  headline={
    <h1 className="hero-headline">
      The World<br />Through Glass
    </h1>
  }
  cta={{ label: 'View the Work', href: '/landscape' }}
/>