# Scroll area

Scrolls content inside a fixed box, with a scrollbar styled to the theme.

- **Import:** `import { ScrollArea, ScrollBar } from "@booleanpress/ui/scroll-area"`
- **Radix Scroll Area:** <https://www.radix-ui.com/primitives/docs/components/scroll-area>
- **Page:** <https://ui.booleanpress.com/components/scroll-area> · @booleanpress/ui 0.1.0

## Usage

Give the scroll area a size. Content that is taller or wider than the box scrolls inside it.

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

export function Events({ events }: { events: string[] }) {
  return (
    <ScrollArea className="h-56 w-64 rounded-md border">
      <ul className="p-4">
        {events.map((event) => (
          <li key={event}>{event}</li>
        ))}
      </ul>
    </ScrollArea>
  )
}
```

It draws a vertical scrollbar. For sideways scrolling, add `<ScrollBar orientation="horizontal" />` as a child, and make the content wider than the box (`w-max`). The scrollbar shows while the pointer is over the area or the content scrolls. The area has no padding and no border: give them with `className` (on the area) or on the content.

## Examples

### Vertical

A list of 20 events in a box 14 rem tall, scrolled with the wheel, touch or the scrollbar.

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

const EVENTS = Array.from({ length: 20 }, (_, i) => `Email ${1000 + i} delivered to a recipient`)

export default function ScrollAreaVertical() {
  return (
    <ScrollArea className="h-56 w-64 rounded-md border">
      <div className="p-4">
        <h4 className="mb-3 text-sm font-medium">Recent events</h4>
        {EVENTS.map((event) => (
          <div key={event}>
            <div className="text-sm">{event}</div>
            <Separator className="my-2" />
          </div>
        ))}
      </div>
    </ScrollArea>
  )
}
```

### Horizontal

A row of mailers wider than the box; `ScrollBar` with `orientation="horizontal"` adds the bar.

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

const MAILERS = ["Amazon SES", "Postmark", "SendGrid", "Mailgun", "Brevo", "SMTP relay", "Sendmail", "Resend"]

export default function ScrollAreaHorizontal() {
  return (
    <ScrollArea className="w-72 rounded-md border whitespace-nowrap">
      <div className="flex w-max gap-3 p-4">
        {MAILERS.map((name) => (
          <div key={name} className="rounded-md border px-3 py-2 text-sm">
            {name}
          </div>
        ))}
      </div>
      <ScrollBar orientation="horizontal" />
    </ScrollArea>
  )
}
```

## Accessibility

**Semantics.** A `div` for the area and a `div` viewport that holds the content. The scrollbars carry no role or name. The content stays in reading order.

**Labels.** The area has no role or name. Add `role="region"` and `aria-label` to the area when the box is a distinct part of the page that people should be able to find.

**Focus.** The viewport is not focusable. A keyboard user can scroll it only when content inside it takes focus (a link, a button), because the browser scrolls a focused control into view.

**Known limits.**

- Content with no focusable control cannot be scrolled from the keyboard in every browser: WCAG 2.1.1 asks for it. `ScrollArea` does not pass props to its viewport, so it cannot take `tabIndex={0}`. Put a focusable element in the content, or make the first item focusable.
- The 10 px scrollbar is a small target for a pointer; the wheel, touch and trackpad scroll without it.

### Keyboard

| Key | Behaviour |
| --- | --- |

## API

### ScrollArea

Renders Radix ScrollArea.Root and passes it every other prop.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` |  |  |
| `dir` | `"ltr" \| "rtl"` |  | The reading direction. It follows the provider's `dir` unless set here. |
| `scrollHideDelay` | `number` |  | Milliseconds before the scrollbar hides again (default 600). |
| `type` | `"auto" \| "hover" \| "always" \| "scroll"` |  | When the scrollbar shows: `hover` (default), `scroll`, `auto` or `always`. |

### ScrollBar

Renders Radix ScrollArea.ScrollAreaScrollbar and passes it every other prop.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` |  |  |
| `forceMount` | `true` |  |  |
| `orientation` | `"horizontal" \| "vertical"` | `vertical` | `vertical` (default) or `horizontal`. |

Every part takes `className`, merged with its defaults by `cn()`, and `ref`, which reaches the element it renders.

**Data attributes:** `data-slot="scroll-area"` (ScrollArea), `data-slot="scroll-area-scrollbar"` (ScrollBar).

## Theming

| Token | Used for |
| --- | --- |
| `--border` | background |
| `--ring` | focus ring |
