# Scroll Area

A scrolling region with a thin scrollbar that looks the same on every platform.

```tsx
import { ScrollArea } from "@zenoxai/ui/components/scroll-area"
import { Separator } from "@zenoxai/ui/components/separator"

function ScrollAreaDemo() {
  const domains = Array.from(
    { length: 30 },
    (_, index) => `acme-${String(index + 1).padStart(2, "0")}.example.com`
  )

  return (
    <ScrollArea className="h-72 w-64 rounded-md border border-border">
      <div className="p-4">
        <div className="mb-3 text-sm font-medium text-text-primary">
          Monitored domains
        </div>
        {domains.map((domain) => (
          <div key={domain}>
            <div className="py-1.5 font-mono text-sm text-text-secondary">
              {domain}
            </div>
            <Separator />
          </div>
        ))}
      </div>
    </ScrollArea>
  )
}
```

## Usage

```tsx
import { ScrollArea } from "@zenoxai/ui/components/scroll-area"
```

```tsx
<ScrollArea className="h-72 rounded-md border border-border">
  {content}
</ScrollArea>
```

Use a scroll area for a region with a fixed size inside a page: a list in a card, a log panel,
a popover with many options. Give it a height (or a width, for horizontal content); without
one there is nothing to scroll. The page itself scrolls natively and needs no scroll area.

`ScrollArea` renders a vertical scrollbar. The scrollbar appears only when the content
overflows.

## Examples

### Horizontal

Add `<ScrollBar orientation="horizontal" />` as the last child for content wider than the
area. Give the content a width it cannot shrink below, such as `w-max`.

```tsx
import { ScrollArea, ScrollBar } from "@zenoxai/ui/components/scroll-area"

function ScrollAreaHorizontal() {
  const ports = [
    { port: 22, service: "SSH" },
    { port: 80, service: "HTTP" },
    { port: 443, service: "HTTPS" },
    { port: 3306, service: "MySQL" },
    { port: 3389, service: "RDP" },
    { port: 5432, service: "PostgreSQL" },
    { port: 6379, service: "Redis" },
    { port: 8080, service: "HTTP proxy" },
    { port: 9200, service: "Elasticsearch" },
  ]

  return (
    <ScrollArea className="w-full max-w-md rounded-md border border-border">
      <div className="flex w-max gap-3 p-4">
        {ports.map(({ port, service }) => (
          <div
            key={port}
            className="flex w-28 shrink-0 flex-col gap-1 rounded-md bg-surface-muted p-3"
          >
            <span className="font-mono text-sm font-medium text-text-primary">
              {port}/tcp
            </span>
            <span className="text-xs text-text-muted">{service}</span>
          </div>
        ))}
      </div>
      <ScrollBar orientation="horizontal" />
    </ScrollArea>
  )
}
```

### Scrollbar on hover

While the content overflows, the scrollbar stays visible. On a panel that is always on
screen, fade it in on hover and while scrolling with `scrollbarClassName` and the
`data-hovering` and `data-scrolling` attributes.

```tsx
import { ScrollArea } from "@zenoxai/ui/components/scroll-area"

function ScrollAreaFadingScrollbar() {
  const events = Array.from(
    { length: 20 },
    (_, index) =>
      `10:${String(59 - index).padStart(2, "0")} Login from 203.0.113.${index + 10}`
  )

  return (
    <ScrollArea
      className="h-56 w-72 rounded-md border border-border"
      scrollbarClassName="opacity-0 transition-opacity data-hovering:opacity-100 data-scrolling:opacity-100"
    >
      <ul className="flex flex-col gap-2 p-4 font-mono text-sm text-text-secondary">
        {events.map((event) => (
          <li key={event}>{event}</li>
        ))}
      </ul>
    </ScrollArea>
  )
}
```

## Accessibility

- The scrolling element takes keyboard focus when its content overflows, so keyboard users
  can scroll it with the arrow keys, Page Up and Page Down. It shows a focus ring.
- Scrolling stays native: wheel, touch, find-in-page and screen readers work as in any
  overflowing element.

## API reference

`import { ... } from "@zenoxai/ui/components/scroll-area"`

### ScrollArea

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| overflowEdgeThreshold | `number \| Partial<{ xStart: number; xEnd: number; yStart: number; yEnd: number; }>` | `0` | The threshold in pixels that must be passed before the overflow edge attributes are applied. Accepts a single number for all edges or an object to configure them individually. |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ScrollAreaRootState>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| scrollbarClassName | `string` |  | Classes for the scrollbar track. Use it to size the bar or to fade it in on `data-hovering` / `data-scrolling` — the primitive keeps the track visible at all times, which reads as clutter on a permanently mounted surface. |

### ScrollBar

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| keepMounted | `boolean` | `false` | Whether to keep the HTML element in the DOM when the viewport isn't scrollable. |
| orientation | `"horizontal" \| "vertical"` | `vertical` | Whether the scrollbar controls vertical or horizontal scroll. |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ScrollAreaScrollbarState>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

