Gradual Blur

A liquid blur effect using layered backdrop filters with gradient masks. Perfect for hero sections, image overlays, and content fades.

Preview

Loading preview…

Customize

Strength1.5
Layers7
Position
Height40
"use client";

import { useMemo } from "react";
import styles from "./GradualBlur.module.css";

type GradualBlurProps = {
  position?: "top" | "bottom" | "block";
  strength?: number;
  height?: string;
  divCount?: number;
  zIndex?: number;
  inline?: boolean;
  className?: string;
};

const DIRECTION = { top: "to top", bottom: "to bottom" } as const;

export default function GradualBlur({
  position = "top",
  strength = 2,
  height = "6rem",
  divCount = 5,
  zIndex = 100,
  inline = false,
  className,
}: GradualBlurProps) {
  const blurDivs = useMemo(() => {
    const increment = 100 / divCount;
    const direction = DIRECTION[position];
    const divs: React.ReactNode[] = [];

    for (let i = 1; i <= divCount; i++) {
      const blur = 0.0625 * i * strength;

      const p1 = Math.round((increment * i - increment) * 10) / 10;
      const p2 = Math.round(increment * i * 10) / 10;
      const p3 = Math.round((increment * i + increment) * 10) / 10;
      const p4 = Math.round((increment * i + increment * 2) * 10) / 10;

      let gradient = `transparent ${p1}%, black ${p2}%`;
      if (p3 <= 100) gradient += `, black ${p3}%`;
      if (p4 <= 100) gradient += `, transparent ${p4}%`;

      const mask = `linear-gradient(${direction}, ${gradient})`;

      divs.push(
        <div
          key={i}
          className={styles.layer}
          style={{
            maskImage: mask,
            WebkitMaskImage: mask,
            backdropFilter: `blur(${blur.toFixed(3)}rem)`,
            WebkitBackdropFilter: `blur(${blur.toFixed(3)}rem)`,
          }}
        />
      );
    }
    return divs;
  }, [position, strength, divCount]);

  const cls = [
    styles.container,
    inline ? styles.inline : styles.fixed,
    className,
  ]
    .filter(Boolean)
    .join(" ");

  return (
    <div
      className={cls}
      style={{ [position]: 0, height, zIndex }}
    >
      {blurDivs}
    </div>
  );
}