Media

Progressive Blur

A progressive blur mask at the edge of scrolling content — the closer to the edge, the blurrier — so content fades in and out smoothly.

A stacked backdrop-filter mask: several blur layers are each cut into one window, doubled layer by layer, and offset toward the edge, accumulating into a "blurrier the closer to the edge" progression. Just drop it at the edge of any relative container.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add progressive-blur.en

New project or installation trouble? Start here →

Progressive Blur is a React progressive blur mask component built with Tailwind CSS. The effect comes entirely from stacked CSS backdrop-filter and mask-image layers, with no animation library and no canvas. Use it at the edges of scrolling lists, marquees or image walls so content softens before it leaves the viewport.

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

96
6
0.5
Active edited usageDefault props
<ProgressiveBlur/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

When to use

  • Top and bottom edges of a long list or chat thread, hinting that there is more to scroll
  • Left and right ends of a horizontally scrolling card row or customer logo marquee
  • Under a fixed top navbar, so content scrolling past blurs beneath it
  • The bottom of an image wall or portfolio grid, ahead of a "Load more" button
  • Not a good fit: if you want content to sharpen into view as it scrolls in, use an entrance component such as Blur Reveal Text

Installation

npx webberui@latest add https://webberui.com/r/progressive-blur.en.json

Or, once registries are configured in components.json, install it as @webberui/progressive-blur.

Usage

Put ProgressiveBlur inside the relative container you want to mask and name the edge it hugs. It is a pointer-events-none purely visual overlay, so it never blocks scrolling or clicking underneath.

import { ProgressiveBlur } from "@/components/ui/progressive-blur";

<div className="relative h-80 overflow-auto">
  {/* your scrolling content */}
  <ProgressiveBlur side="top" size={80} />
  <ProgressiveBlur side="bottom" size={80} />
</div>

side can be "top", "bottom", "left", or "right", and one container can carry several edges at once.

Examples

Fade both ends of a horizontal card row

Set side to "left" and "right"; size then controls the width.

import { ProgressiveBlur } from "@/components/ui/progressive-blur";

<div className="relative">
  <div className="flex gap-4 overflow-x-auto px-6 py-4">
    {["Plan comparison", "Customer feedback", "Integrations", "Changelog", "Security"].map((label) => (
      <div key={label} className="w-56 shrink-0 rounded-xl border p-4 text-sm">
        {label}
      </div>
    ))}
  </div>
  <ProgressiveBlur side="left" size={48} />
  <ProgressiveBlur side="right" size={48} />
</div>

Blend into the background with tint

Pass tint when backdrop-filter is unavailable, or when you want the edge to melt into the page background. It accepts any CSS color string; usually you set it to the container background color.

<div className="relative h-96 overflow-auto bg-white">
  <ul className="space-y-2 p-4">{/* order list */}</ul>
  <ProgressiveBlur
    side="bottom"
    size={120}
    tint="#ffffff"
  />
</div>

Heavier blur with a percentage size

size can also be a string such as "30%", so the mask follows the container height. Raise blur or layers to make the outermost layer blurrier.

<div className="relative aspect-video overflow-hidden rounded-2xl">
  <img src="/screenshots/dashboard.png" alt="Dashboard screenshot" className="size-full object-cover" />
  <ProgressiveBlur side="bottom" size="30%" layers={8} blur={0.75} />
</div>

Props

PropTypeDefaultDescription
side"top" | "bottom" | "left" | "right""bottom"The edge the blur hugs and deepens toward
sizenumber | string96Thickness of the mask along that edge (a number means px)
layersnumber6Number of stacked layers; the more layers, the smoother the gradation
blurnumber0.5Blur radius of the first layer (px), doubling with each layer after it
tintstring—Fades from transparent into this color along the edge, which can carry on into the background and cover the content
classNamestring—Forwarded to the overlay container

How it works

  • Each layer occupies a window 100 / layers wide, with adjacent windows deliberately overlapping to eliminate hard-edged banding; the blur radius doubles layer by layer, so the layer closest to the edge is the blurriest.
  • With blur at 0.5 and layers at 6, the outermost layer blurs by about 16px; raise blur or layers if you want it heavier.
  • The overlay is pointer-events-none, so the content underneath stays scrollable and clickable.
  • If the browser does not support backdrop-filter, this layer silently does nothing without affecting the content underneath; when you still need to cover the content in that case, pair it with tint to supply a gradient backing color.

FAQ

Does it work with the Next.js App Router?

Yes. The file already starts with "use client", so import it from a Server Component page as usual. The component never touches window, so the server output matches the client.

Why do I not see any effect?

ProgressiveBlur is absolutely positioned, so its parent needs relative (or any non-static position). It also blurs whatever is behind it, so over a flat single-color background there is nothing visible to blur.

Does it affect performance?

Each layer is one element with backdrop-filter, and layers defaults to 6. There is no animation or JavaScript loop, but on low-end devices with a large area you can lower layers and shrink size.

What do I need to install?

Only clsx and tailwind-merge, which the cn utility uses. If you prefer not to use the CLI, switch to the Manual tab in the installation section and copy the source.