Components
Fade Scroll
A scroll container that swaps the native scrollbar for soft gradient edge fades. The fade only shows on edges that still have more content to reveal, and the component works in either vertical or horizontal orientation.
Installation
bunx @happlyui/cli@latest add fade-scroll
Dependencies
Registry dependencies
These are automatically installed when you add this component.
happly-ui-utils
Usage
import * as FadeScroll from "@/components/ui/fade-scroll"
<FadeScroll.Root className="h-64">
{/* scrollable content */}
</FadeScroll.Root>
Examples
Vertical
The default orientation. The top fade appears once you scroll past the first line; the bottom fade disappears at the end.
<FadeScroll.Root className="h-64 max-w-md space-y-3">
{paragraphs.map((p, i) => <p key={i}>{p}</p>)}
</FadeScroll.Root>
Horizontal
Set orientation='horizontal' and lay out children with flex/gap. Left and right fades replace the horizontal scrollbar.
<FadeScroll.Root orientation="horizontal" className="flex max-w-lg gap-3">
{cards.map((c) => <Card key={c.id} {...c} />)}
</FadeScroll.Root>
Vertical List
A scrollable list inside a bordered card. Children's natural size is preserved — the [&>*]:shrink-0 rule prevents flex/grid from compressing items below their content size.
<FadeScroll.Root className="h-72 w-72 rounded-lg border">
<ul>
{items.map((item) => <li key={item.id}>{item.label}</li>)}
</ul>
</FadeScroll.Root>
Custom Fade Size
Override the fade distance with the fadeSize prop (in pixels). Smaller values give a sharper edge; larger values give a softer, more dramatic fade.
fadeSize=8 (subtle)
fadeSize=64 (dramatic)
<FadeScroll.Root orientation="horizontal" fadeSize={64} className="flex max-w-lg gap-3">
{items}
</FadeScroll.Root>
No Overflow
When the content fits inside the container, no fades render — the component is visually inert until scrolling becomes possible.
<FadeScroll.Root className="h-64 max-w-md">
<p>Short content that doesn't overflow.</p>
</FadeScroll.Root>
API Reference
FadeScroll.Root
Scroll container with hidden native scrollbar and gradient mask fades on edges with overflowing content. Forwards a ref to the underlying div and accepts all standard div props (className, style, onScroll, etc.).
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | 'vertical' | 'horizontal' | 'vertical' | Scroll axis. 'vertical' uses overflow-y-auto with top/bottom fades; 'horizontal' uses overflow-x-auto with left/right fades. |
fadeSize | number | 24 | Length of the gradient fade in pixels. Larger values produce a softer fade; smaller values produce a sharper edge. |
className | string | - | Additional class names. Use this to set the container's height (vertical) or width (horizontal), padding, layout (flex/grid), and any other styling. Sizing is intentionally not opinionated. |
style | React.CSSProperties | - | Inline styles. Merged with the mask styles applied internally — your styles take precedence on conflicting keys. |
onScroll | (event: React.UIEvent<HTMLDivElement>) => void | - | Forwarded to the inner scroll div. Called after the internal scroll-state update so fades stay accurate. |