Scroll Fade
Indicate hidden content at the edges of a scrollable region.
Preview
These examples compose Scroll Area with Scroll Fade on a shared viewport for custom scrollbars. Fades appear only at edges with more content beyond them. Scroll to an edge and its fade disappears. The viewport remains natively scrollable; it does not add drag gestures or scroll buttons.
Installation
pnpm dlx shadcn@latest add http://127.0.0.1:3017/r/scroll-fade.jsonInstalls one client component using React and your existing cn utility. No additional dependencies or theme changes are required. See the registry guide for local endpoint details.
This is a local Migoy implementation. The suggested shadcn Scroll Fade reference was unavailable when this utility was added; its API is not assumed to be interchangeable.
Usage
import { ScrollFade } from '@/components/ui/scroll-fade';
<ScrollFade className="max-w-sm" role="region" aria-label="Project sections" tabIndex={0}>
<div className="flex w-max gap-6">
{/* Content wider than the viewport */}
</div>
</ScrollFade>Composition
ScrollFade (the scrolling viewport)
└── Content larger than the viewportScrollFade owns native scrolling and a transparent CSS mask. Constrain its width or height to create overflow. Keep the overflow content inside the component; wrapping another independently scrolling viewport will not track that viewport's position.
The mask works over varied backgrounds, without painting an overlay or intercepting pointer events. A ResizeObserver tracks the viewport and its direct children, and a MutationObserver updates measurements when content changes. Without ResizeObserver, scroll, window resize, and content mutation still trigger updates.
Vertical scrolling
<ScrollFade orientation="vertical" className="h-40" role="region" aria-label="Project activity" tabIndex={0}>
{/* Content taller than the viewport */}
</ScrollFade>Tabs
Tabs uses this utility internally with a 16px horizontal fade. It preserves the active underline and padding around the keyboard focus outline. Other scrolling controls and lists can use ScrollFade independently.
With Scroll Area
Use Scroll Area’s viewportProps={{ render: <ScrollFade /> }} to share its actual scrolling element. For vertical content, use <ScrollFade orientation="vertical" />. Install Scroll Area separately and compose this in a Client Component. Its scrollbar is a sibling of the viewport and remains outside the fade mask. See the Scroll Area example.
Accessibility and behavior
- Fades clear while the viewport or any descendant has focus, keeping keyboard focus and labels fully visible.
- For a non-interactive scrolling region, add
tabIndex={0}and an accessible name so keyboard users can reach and scroll it. Regions with their own focusable controls may already provide keyboard access. - The utility does not create a tab stop or assign a role automatically.
- Horizontal measurements handle both left-to-right and right-to-left scroll positions. Overflow data attributes describe physical edges: start means left or top; end means right or bottom.
- No animated fade transitions or smooth scrolling are added.
- A fade is a visual hint; do not rely on it to communicate essential instructions.
API
| Prop | Default | Purpose |
|---|---|---|
orientation | horizontal | Horizontal or vertical overflow |
fadeSize | 16 | Fade length in CSS pixels, clamped to 25% of the viewport |
| Native div props | — | Includes ref, style, scroll handlers, and accessibility attributes |
Set fadeSize={0} to disable the visual fade. The utility measures after mounting, so server-rendered content starts without a mask until overflow is known. data-overflow-start and data-overflow-end are present only when more content exists beyond the corresponding physical edge.