Depth Mask Hero
A lock-screen depth hero — a cut-out subject partially occludes the oversized headline, and on scroll the subject, headline, and background separate at different rates to produce real depth.
npx shadcn@latest add https://webberui.com/r/depth-mask-hero.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<DepthMaskHero />
Installation
npx shadcn@latest add https://webberui.com/r/depth-mask-hero.jsonOr, once registries are configured in components.json, install it as @webberui/depth-mask-hero.
Usage
import { DepthMaskHero } from "@/components/ui/depth-mask-hero";
<DepthMaskHero
title="OUTSIDE"
kicker="Summer feature"
date="Monday, July 13"
time="9:41"
subject="/people/hiker.png"
subjectAlt="A hiker"
>
<section className="px-6 py-16">The body copy starts here…</section>
</DepthMaskHero>When no subject is provided, a built-in neutral-toned figure silhouette is rendered, so you can show off the masked composition without any external asset. The hero pins itself with position: sticky and only lets the children body copy come up once you have scrolled past it; if it scrolls inside a nested overflow container, remember to pass that container's ref to container.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | React.ReactNode | — | Main headline, set at a large size and partially occluded by the subject |
titleAs | "h1" | "h2" | "h3" | "h1" | Semantic tag for the headline, to avoid clashing with the page title |
kicker | React.ReactNode | — | Kicker (small label) shown above the headline |
subject | string | — | URL of the cut-out subject image (a transparent PNG is recommended) |
subjectAlt | string | — | Alt text for the subject; treated as decorative when not provided |
background | string | — | Background image URL; falls back to a neutral gradient when not provided |
time | React.ReactNode | — | Lock-screen time text, for example "9:41" |
date | React.ReactNode | — | Lock-screen date text, shown above the time |
height | string | "100svh" | Height of the hero panel (any CSS length) |
scrollDistance | string | "80vh" | Extra scroll distance needed for the depth separation |
stickyTop | number | 0 | Pinned position of the panel (px) |
depth | number | 1 | Offset multiplier for each layer's depth separation — larger makes the layering more pronounced |
pointerParallax | boolean | true | Whether pointer parallax is enabled |
parallaxStrength | number | 20 | Maximum offset of the pointer parallax (px) |
springConfig | SpringOptions | { stiffness: 140, damping: 20, mass: 0.4 } | Spring parameters for the parallax spring back |
container | React.RefObject<HTMLElement | null> | — | Ref of the nested scroll container; defaults to the window as scroll container |
children | React.ReactNode | — | Body copy revealed after scrolling past the hero |
heroClassName | string | — | Class applied to the hero panel |
titleClassName | string | — | Class applied to the headline |
How it works
- Three-layer depth separation: the background (
z-0) moves the least and against the pointer, the headline (z-10) sits in the middle, and the cut-out subject (z-20) moves the most and with the pointer; on scroll the three layers move at different rates, and that rate difference is the depth. - Depth mask: when the subject is a cut-out image (a transparent PNG), the transparent areas naturally expose the headline behind it while the opaque areas cover it, producing the lock-screen effect of "text wrapping behind the subject" without any extra mask asset.
- Lock-screen details:
timeanddateare shown pinned at the top in a lock-screen style, and fade out first as you scroll. - Container-query layout: the panel establishes a containment context with
container-type: size, and the headline and details all usecqwunits, so they scale proportionally at any panel size. - Timer / listener cleanup: pointer parallax is driven by
motion'sMotionValueanduseSpring, and scroll progress is bound throughuseScroll; events are only registered onpointermove/pointerleave, and React removes them on unmount.
Accessibility
- When the user has "reduce motion" enabled at the system level, scroll parallax and pointer parallax are disabled and the hero renders as static stacked layers, with the layout structure unchanged.
- The headline is output as a real heading tag (
titleAs),h1by default; on a page that already has a main heading you can drop it toh2/h3. - When
subjectAltis provided, the subject image is announced as a meaningful image; when it is not, the image isaria-hiddenand treated as decorative only. - Purely visual layers such as the background, vignette, and lock-screen time are all marked
aria-hidden, so they do not interfere with the reading order for assistive technology.
Queue Waitlist Section
A gamified waitlist section — submit an email and a 3D card flip reveals your queue number and the crowd ahead of you; copy the referral link and you move up the line with a queue-jump animation.
Quest Map Section
A winding quest map section — as you scroll, a marker travels the path clearing stations, and each one it reaches pops its content open, raises a flag, and stamps itself complete.