A native scroller with a thin themed overlay scrollbar and edge fades, built on Base UI Scroll Area.
Motion signature: the scrollbar is a 4px pill that appears while you scroll or hover and thickens to 6px under the pointer, and each edge feathers in only as far as there is content past it, so a list at rest at its top shows no top fade at all.
Usage
import { ScrollArea } from "@/components/eased/scroll-area/scroll-area";
<ScrollArea className="h-72" aria-label="Activity">
<ul>…</ul>
</ScrollArea>
<ScrollArea orientation="horizontal" fade={24} aria-label="Filters">
<div className="flex w-max gap-2 px-4">…</div>
</ScrollArea>Size the root with className (h-72, max-h-80, or flex-1 min-h-0 in a column). Put padding in contentClassName so it scrolls with the content. Scrolling, thumb dragging and keyboard scrolling are Base UI's: the viewport is a tab stop while it overflows, arrow keys and Page Up/Down scroll it, and the focus ring is drawn inset on the root (-outline-offset-2, a deliberate exception to the usual 2px outset ring, R65) because the faded, clipped viewport would mask an outline outside it.
Props
| Prop | Default | |
|---|---|---|
orientation | "vertical" | "horizontal" or "both"; the other axis is locked. |
fade | 20 | Edge feather in px; 0 turns it off. Each feather grows with the distance left to scroll (scrollFadeMask from the scroll-fade primitive). |
className | Root classes; size it here. | |
contentClassName | Content wrapper classes. | |
viewportRef | The scrolling element, for scrollTo or a scroll-linked Progressive Blur. | |
aria-label / aria-labelledby | Names the region; give one whenever it scrolls. | |
style | Root style. |
Reduced motion: the bar and pill change without transitions.