A stacked-layer blur that ramps smoothly from sharp to fully blurred toward an edge, for fading content under a fixed header or above a scroll boundary.
Made by AxylInstallation
File Structure
Usage
The wrapping element needs position: relative (or any positioning context) — ProgressiveBlur
renders as an absolutely positioned overlay pinned to one edge via direction.
Keep scrolling content as a sibling
ProgressiveBlur blurs whatever sits behind it in the same stacking context. Mount it as a
sibling of the scrollable element (both inside the relative wrapper), not as a child of it —
otherwise it scrolls away with the content instead of staying pinned to the edge. Also avoid
overflow: hidden or a transform on an ancestor between the two: either creates a new
stacking/containing context and the blur will sample the wrong content, or get clipped
entirely.
How it works
A single backdrop-filter: blur() with a gradient mask blurs uniformly, then fades in opacity —
it doesn't get blurrier toward the edge, just fainter. ProgressiveBlur instead stacks several
layers, each with its own blur radius and a mask that isolates a band of the fade zone, so the
blur radius itself ramps up toward the edge. More layers reads smoother, at the cost of more
backdrop-filter paints:
Set fade={false} to skip the trailing solid-color layer and let the blurred content show
through all the way to the edge, instead of fading out to --pb-background:
Customizing the fade color
ProgressiveBlur reads --pb-background for its solid-fade layer, defaulting to your app's own
--background shadcn token when present. Override it directly if the edge sits on a different
surface than the page background:
Props
| Prop | Type | Default |
|---|---|---|
direction? | "top" | "bottom" | "left" | "right" | "bottom" |
size? | string | "6rem" |
maxBlur? | number | 12 |
layers? | number | 6 |
fade? | boolean | true |
className? | string | - |
When to Use
Reach for ProgressiveBlur at the boundary of any scrollable region where content needs to fade
out gracefully instead of clipping abruptly — under a sticky header, above a fixed footer, or at
the edges of a horizontally scrolling rail. It reads as glass rather than a hard cutoff because
the blur intensity itself increases toward the edge, matching how out-of-focus content actually
looks.
Accessibility
ProgressiveBlur is aria-hidden and pointer-events-none — it's a purely visual overlay and
never intercepts clicks or scroll. backdrop-filter itself has no motion to disable, but stacking
6–16 of them is a real paint cost, so when the OS Reduced Motion setting is on, ProgressiveBlur
collapses to a single layer regardless of layers — same silhouette, far less work per frame.
Adapted from gxuri (Skiper UI). Independent implementation for the Sora UI registry. Not affiliated with the original authors.
Built by Axyl. A motion-first component registry for React.
Last updated: 7/4/2026