Skip to main content

Edge Veil Blur

A veil of progressive blur pinned to one edge of a scroll container: a masked backdrop-filter band fades from full blur at the edge to nothing over the content, with an optional surface tint riding the same gradient so rows dissolve instead of hitting a hard clip line. Four sides, tint color and strength all configurable.

Micro InteractionReactTailwind CSS
CSSTailwind

Manual

Create a file and paste the following code into it.

edge-veil-blur.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
"use client";

import { type CSSProperties, type HTMLAttributes } from "react";
import { cn } from "@/lib/cn";

/* ------------------------------------------------------------------ */
/*  EdgeVeilBlur                                                       */
/* ------------------------------------------------------------------ */

export type VeilSide = "top" | "bottom" | "left" | "right";

interface EdgeVeilBlurProps extends HTMLAttributes<HTMLDivElement> {
  /** Edge the blur is strongest at [Optional, default: "bottom"] */
  side?: VeilSide;
  /** Backdrop blur in px [Optional, default: 4] */
  strength?: number;
  /** Thickness of the veiled band [Optional, default: "160px"] */
  size?: string | number;
  /** Fade a surface tint alongside the blur [Optional, default: true] */
  tint?: boolean;
  /** Tint color (defaults to the page surface in both modes) [Optional] */
  tintColor?: string;
  /** Tint opacity at the solid edge 0-1 [Optional, default: 1] */
  tintStrength?: number;
}

const IS_HORIZONTAL: Record<VeilSide, boolean> = {
  top: false,
  bottom: false,
  left: true,
  right: true,
};

/* The gradient runs FROM the edge (opaque) TO the content (transparent). */
const FADE_DIR: Record<VeilSide, string> = {
  top: "to bottom",
  bottom: "to top",
  left: "to right",
  right: "to left",
};

const POSITION: Record<VeilSide, CSSProperties> = {
  top: { top: 0, left: 0 },
  bottom: { bottom: 0, left: 0 },
  left: { top: 0, left: 0 },
  right: { top: 0, right: 0 },
};

/**
 * A veil of progressive blur pinned to one edge of a scroll container:
 * a masked backdrop-filter band that fades from full blur at the edge to
 * nothing over the content, with an optional surface tint riding the same
 * gradient so text dissolves instead of hitting a hard clip line.
 * @param {VeilSide} side - Strongest edge [Optional, default: "bottom"]
 * @param {number} strength - Backdrop blur px [Optional, default: 4]
 */
export function EdgeVeilBlur({
  side = "bottom",
  strength = 4,
  size = "160px",
  tint = true,
  tintColor = "light-dark(#ffffff, #09090b)",
  tintStrength = 1,
  className,
  style,
  ...props
}: EdgeVeilBlurProps) {
  const isHorizontal = IS_HORIZONTAL[side];
  const fadeDir = FADE_DIR[side];
  const sizeValue = typeof size === "number" ? `${size}px` : size;

  const sizeStyle: CSSProperties = isHorizontal
    ? { width: sizeValue, height: "100%" }
    : { height: sizeValue, width: "100%" };

  const maskImage = `linear-gradient(${fadeDir}, black 50%, transparent 100%)`;
  const background = tint
    ? `linear-gradient(${fadeDir}, color-mix(in oklch, ${tintColor} ${Math.round(
        tintStrength * 100,
      )}%, transparent) 0%, transparent 100%)`
    : undefined;

  return (
    <div
      aria-hidden="true"
      className={cn("pointer-events-none absolute z-10", className)}
      style={{
        ...sizeStyle,
        ...POSITION[side],
        background,
        maskImage,
        WebkitMaskImage: maskImage,
        backdropFilter: `blur(${strength}px)`,
        WebkitBackdropFilter: `blur(${strength}px)`,
        willChange: "backdrop-filter",
        ...style,
      }}
      {...props}
    />
  );
}

Update the import paths to match your project setup.

Similar components

Tilt Pointer

Halftone Portrait

Spotlight Follow

Keycap Hint

Install via CLI

Resource details

PublishedJuly 17, 2026
CategoryMicro Interaction
ReactTailwind CSS