Skip to content

Scroll Area

Thin-scrollbar wrapper with vertical, horizontal, or both overflow directions.

Preview

Installation

$pnpm exec dgadd add ui/scroll-area
[Installs to]src/components/ui/scroll-area[Item]ui/scroll-area

dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.

Usage

tsx
import { ScrollArea } from "@/components/ui/scroll-area";export default function ScrollAreaDefault() {  const items = Array.from({ length: 20 }, (_, i) => `Item ${i + 1}`);  return (    <ScrollArea aria-label="Item list" className="h-48 border border-border p-2">      {items.map((item) => (        <div key={item} className="py-1 px-2 text-sm text-foreground border-b border-border/30">          {item}        </div>      ))}    </ScrollArea>  );}

Examples

Horizontal

Preview

Both Directions

Preview

Keyboard Region

Preview

Overlay

Preview

API Reference

ScrollArea

NameTypeDefaultDescription
orientation"vertical" | "horizontal" | "both""vertical"Axes that overflow. Other axes are clipped.
overlaybooleanfalseHides the native scrollbar and floats a draggable thumb above the content, so rows can run border-to-border instead of stopping at a reserved track. Applies only with the vertical orientation — other orientations keep their native bar — and only on hover-capable devices; touch keeps the native indicator. Renders a zero-height rail as the container's first DOM child, ahead of children. The thumb hides when content fits, and it follows the same --scrollbar-thumb / --scrollbar-thumb-active tokens as the thin scrollbar.
keyboardScrollablebooleantrueWhen true and the region has an accessible name (aria-label or aria-labelledby), wires Arrow/PageUp/PageDown/Home/End to scroll the container and gives it tabIndex={0}. role="region" follows the accessible name alone, so a named region keeps its role when this is false — hand key handling to a parent composite only when that parent also owns the tab stop.
childrenReactNodeContent rendered inside the scrollable container.

Accessibility

Keyboard Navigation

ScrollArea exposes role="region" whenever it has an accessible name (aria-label or aria-labelledby). When keyboardScrollable is also true, the named region becomes focusable and scrolls itself from keyboard events.

KeyAction
ArrowUp / ArrowDownScrolls vertical or both-axis regions by 40px.
ArrowLeft / ArrowRightScrolls horizontal or both-axis regions by 40px.
PageUp / PageDownScrolls vertical regions by 80% of height; horizontal-only regions scroll by 80% of width.
Home / EndMoves to the start or end of each enabled scroll axis.

Focusable region

Preview

Both directions

Preview

Notes

Orientation

3 modes via orientation prop — vertical (default), horizontal, or both. The thin bar comes from the library's own unlayered .scrollbar-thin utility, which deliberately outranks Tailwind v4's same-named built-in. The overlay prop applies to the vertical mode only.

Wrapper Only

Without overlay, ScrollArea is a pure wrapper that adds thin scrollbar styling and renders no visual output of its own. With overlay it renders one zero-height rail as the container's first DOM child (ahead of children) carrying the floating thumb — account for it in position-keyed styling of direct children (first:, nth-child, space-y-*).

Resting Thumb

The thumb is visible at rest (foreground at 35%) and strengthens on hover or focus-within, so overflow is signalled before the pointer arrives. Chromium and WebKit get that resting thumb from the ::-webkit-scrollbar tree, and the standard scrollbar-width/scrollbar-color pair is confined to engines without that pseudo-element: declaring both on one element makes Chromium fall back to a platform overlay scrollbar that stays invisible until you scroll. Override --scrollbar-thumb and --scrollbar-thumb-active on the ScrollArea or any ancestor to retune both steps — the overlay mode's floating thumb consumes the same two tokens, so one retune moves both presentations. Give the scroll container at least 1px of inset from a surrounding border so the track and the border do not read as one doubled edge.

Source

Install via CLI: pnpm exec dgadd add ui/scroll-area.

Highlighted source loads after this disclosure opens. Browse the source repository.