Pho Design System

Scroll Area

A capped scroll region with overlay scrollbars and progressive edge fade. Built on Base UI ScrollArea — native scroll still does the work, so keyboard scrolling and momentum are unchanged. Approaching rows blur (0 → 1px) and wash into the surface; strength tracks how far content has scrolled under the edge.

Import

import { ScrollArea } from "@photon-ai/pho-ui/components/scroll-area";

Basic

Give ScrollArea.Root a fixed size, put the content in ViewportContent, and add a ScrollbarThumb. The bar sits over the content and fades in on hover or while scrolling, so it never shifts layout. Edges blur and wash in as you scroll.

<ScrollArea.Root className="h-44 w-72">
  <ScrollArea.Viewport>
    <ScrollArea.Content className="p-3">…</ScrollArea.Content>
  </ScrollArea.Viewport>
  <ScrollArea.Scrollbar>
    <ScrollArea.Thumb />
  </ScrollArea.Scrollbar>
</ScrollArea.Root>

Both axes

Add a Scrollbar per axis — the default orientation is vertical; set orientation="horizontal" for the second. A Corner fills the square where the two bars meet.

<ScrollArea.Root className="h-48 w-80">
  <ScrollArea.Viewport>
    <ScrollArea.Content className="p-3">…</ScrollArea.Content>
  </ScrollArea.Viewport>
  <ScrollArea.Scrollbar orientation="vertical">
    <ScrollArea.Thumb />
  </ScrollArea.Scrollbar>
  <ScrollArea.Scrollbar orientation="horizontal">
    <ScrollArea.Thumb />
  </ScrollArea.Scrollbar>
  <ScrollArea.Corner />
</ScrollArea.Root>

Anatomy

Props

Every part renders a <div> and forwards className plus native <div> attributes. The parts that add their own props:

Root exposes overflow state as data-* attributes (data-has-overflow-x, data-overflow-y-end, …) and the active scrollbar carries data-hovering, data-scrolling, and data-orientation for styling.

Accessibility

Keyboard: when the content overflows, ScrollArea.Viewport is placed in the tab order (tabIndex={0}) so a keyboard user can focus the region and scroll it; a non-scrollable viewport is removed from the tab order (tabIndex={-1}). Once focused it scrolls with the platform’s native keys:

Best practices