scroll-fade is part of the shared Tailwind utilities that init imports in your global CSS file. If your CSS file does not import them yet, install the package:
pnpm add shadcn
Then import the shared utilities in your global CSS file:
The fade is scroll-aware and tracks the scroll position:
At rest, the top edge is crisp and the bottom edge fades to hint at more content.
As you scroll, a fade appears at the top and both edges stay faded mid-scroll.
At the end, the bottom edge sharpens to show you have reached the last item.
The fade is applied with mask-image, so it dissolves the content itself rather than overlaying a color. The mask uses a linear fade from transparent to black, so it adapts to any background without configuration. If your scroll area sits inside a card, put the background and border on a wrapper and scroll-fade on the inner scroller, so the fade dissolves the content and not the card.
The horizontal fade is direction-aware. In RTL layouts, the crisp edge and the fade follow the reading direction with no extra classes needed. scroll-fade-<number> and scroll-fade-none work the same for both axes.
The edge utilities are scroll-aware. Start edges fade in after you scroll away from the start, and end edges fade out when you reach the end. Use scroll-fade-t, scroll-fade-b, scroll-fade-l, and scroll-fade-r for physical edges. Use scroll-fade-s and scroll-fade-e for logical inline edges that mirror in RTL.
The fade depth defaults to 12% of the container, capped at 40px so tall scrollers stay subtle. Use scroll-fade-<number> to set a fixed size on the spacing scale instead, the same way scroll-mt-<number> works.
To fade opposite edges by different amounts, use the per-edge modifiers scroll-fade-t-<number>, scroll-fade-b-<number>, scroll-fade-s-<number>, and scroll-fade-e-<number>. They override scroll-fade-<number> on the edge they target and accept arbitrary values too.
Use the logical s/e modifiers for horizontal scrollers so the sizes mirror in RTL.
The fade eases in and out over a fixed scroll distance rather than appearing instantly. That distance is the --scroll-fade-reveal variable, 96px by default and independent of the fade depth. Lower it for a snappier reveal or raise it for a more gradual one:
The scroll-aware behavior is implemented with CSS scroll-driven animations, with no JavaScript and no scroll listeners. In browsers that do not support scroll-driven animations, scroll-fade falls back to a static fade on both edges, and edge utilities fall back to a static fade on the selected edge.
Since the mask is applied to the scroll container itself, a visible scrollbar fades with the content at the edges. Pair scroll-fade with no-scrollbar, which ships in the same package, if you want to hide the scrollbar entirely.
scroll-fade-x follows the reading direction. At rest, the start edge is crisp and the end edge fades. In RTL layouts that means a crisp right edge and a fade on the left, mirrored from LTR.