Skip to content
Component installs need the registry setup— the Base UI shadcn project, the @vegastack namespace and the Cloudflare Access service token.
VegaStack Design

Scroll Fade

Mask-based fade hints on the edges of a scroll container — pure CSS, scroll-position aware, and RTL-safe.

Last updated

Row 1
Row 2
Row 3
Row 4
Row 5
Row 6
Row 7
Row 8
Row 9
Row 10
Row 11
Row 12
Row 13
Row 14
Row 15
Row 16
Row 17
Row 18
Row 19
Row 20
Row 21
Row 22
Row 23
Row 24

scroll-fade is a Tailwind @utility that fades the edges of a scroll container to hint at more content. It uses mask-image with a scroll-driven timeline — no JavaScript and no scroll listeners. The fade is crisp at the start/end and softens mid-scroll; without scroll-driven-animation support it degrades to a static fade.

Install

These utilities ship with @vegastack/design-tokens — no shadcn add step:

@import "@vegastack/design-tokens/utilities.css";

The @vegastack/design imports it for you, so it is available automatically alongside shimmer.

Usage

<div className="scroll-fade h-72 overflow-y-auto">{/* content */}</div>

The container needs a bounded size and overflow so it can actually scroll.

Row 1
Row 2
Row 3
Row 4
Row 5
Row 6
Row 7
Row 8
Row 9
Row 10
Row 11
Row 12
Row 13
Row 14
Row 15
Row 16
Row 17
Row 18
Row 19
Row 20
Row 21
Row 22
Row 23
Row 24

Examples

Horizontal

scroll-fade-x fades the start/end edges of a horizontally-scrolling row.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Single edge

Fade only one edge with scroll-fade-t or scroll-fade-b (and -l/-r or the logical -s/-e).

scroll-fade-t
Row 1
Row 2
Row 3
Row 4
Row 5
Row 6
Row 7
Row 8
Row 9
Row 10
Row 11
Row 12
Row 13
Row 14
Row 15
Row 16
Row 17
Row 18
Row 19
Row 20
scroll-fade-b
Row 1
Row 2
Row 3
Row 4
Row 5
Row 6
Row 7
Row 8
Row 9
Row 10
Row 11
Row 12
Row 13
Row 14
Row 15
Row 16
Row 17
Row 18
Row 19
Row 20

Fixed size

scroll-fade-<n> sets a fixed fade depth from the spacing scale.

scroll-fade-4
Row 1
Row 2
Row 3
Row 4
Row 5
Row 6
Row 7
Row 8
Row 9
Row 10
Row 11
Row 12
Row 13
Row 14
Row 15
Row 16
Row 17
Row 18
Row 19
Row 20
scroll-fade-24
Row 1
Row 2
Row 3
Row 4
Row 5
Row 6
Row 7
Row 8
Row 9
Row 10
Row 11
Row 12
Row 13
Row 14
Row 15
Row 16
Row 17
Row 18
Row 19
Row 20

Classes

ClassEffect
scroll-fade / scroll-fade-yVertical fade (top + bottom).
scroll-fade-xHorizontal fade (start + end).
scroll-fade-t / scroll-fade-bTop / bottom edge only.
scroll-fade-l / scroll-fade-rLeft / right edge only.
scroll-fade-s / scroll-fade-eLogical start / end edge (RTL-aware).
scroll-fade-*Fixed fade depth for all active edges (spacing scale or a length/percentage).
scroll-fade-t-*Fixed fade depth for the top edge only (spacing scale or a length/percentage).
scroll-fade-b-*Fixed fade depth for the bottom edge only (spacing scale or a length/percentage).
scroll-fade-s-*Fixed fade depth for the logical start edge only, RTL-aware (spacing scale or a length/percentage).
scroll-fade-e-*Fixed fade depth for the logical end edge only, RTL-aware (spacing scale or a length/percentage).
scroll-fade-noneDisable the fade.

Notes

  • Relies on CSS scroll-driven animations (animation-timeline: scroll()); in engines without support it falls back to a static bilateral fade.
  • It is the edge treatment used by the Message Scroller viewport.
  • It clips the element's own focus outline. mask-image limits everything the element paints to its border box, so on a keyboard-focusable scroll container the shared :focus-visible outline — drawn at a positive offset, outside that box — is not painted at all. Pull it inside with focus-visible:-outline-offset-2. Do not substitute a border tint: forced-colors: active replaces border-color, which would leave the container with no focus indicator whatsoever. See Focus ring.

Do / Don't

Do
Apply scroll-fade to a bounded, overflowing container to hint that more content is scrollable, and inset the focus outline if that container is focusable.
Don't
Apply it to a container that does not scroll — there is nothing to fade toward — or pair it with outline-none on a focusable container.

On this page