# ScrollArea

> Una caja con scroll y una barra propia, discreta: aparece al pasar el mouse o al scrollear.

```tsx
import { ScrollArea, ScrollAreaScrollbar } from "sebs7n-ui/scroll-area"
```

## Ejemplos

### Una lista larga dentro de un panel

La caja necesita un alto propio: sin límite no hay desborde y no hay nada que scrollear. El padding va en `contentClassName`, no en el viewport, para que el contenido no se corte contra la barra.

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

function Basico() {
  return (
    <ScrollArea className="h-56 w-full max-w-sm rounded-surface border border-separator bg-surface" contentClassName="p-3">
      <div className="flex flex-col">
        {MOVIMIENTOS.map((movimiento, indice) => (
          <div key={movimiento.detalle + movimiento.fecha}>
            {indice > 0 && <Separator className="my-2" />}
            <div className="flex items-baseline justify-between gap-4">
              <div className="flex min-w-0 flex-col">
                <span className="truncate text-callout text-label">{movimiento.detalle}</span>
                <span className="text-callout text-label-secondary">{movimiento.fecha}</span>
              </div>
              <span className="shrink-0 text-mono-body text-label-secondary">{movimiento.monto}</span>
            </div>
          </div>
        ))}
      </div>
    </ScrollArea>
  )
}
```

### Los dos ejes

`orientation="both"` agrega la barra de abajo y la esquina entre las dos. Se pone solo cuando de verdad desborda en los dos ejes; si no, sobra una barra.

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

function DosEjes() {
  const columnas = ["Cliente", "CUIT", "Condición IVA", "Provincia", "Último pago", "Saldo"]
  const filas = [
    ["Acme S.A.", "30-71234567-8", "Responsable Inscripto", "Santa Fe", "01/09/2026", "$ 0"],
    ["Bruma SRL", "30-71987654-3", "Monotributo", "Buenos Aires", "29/08/2026", "$ 96.000"],
    ["Cortina SA", "30-70111222-9", "Responsable Inscripto", "Córdoba", "24/08/2026", "$ 0"],
    ["Delta", "27-33444555-1", "Exento", "Mendoza", "18/08/2026", "$ 74.500"],
    ["Everest SRL", "30-71555444-2", "Monotributo", "Santa Fe", "11/08/2026", "$ 33.900"],
    ["Fénix SA", "30-70999888-7", "Responsable Inscripto", "Neuquén", "04/08/2026", "$ 128.000"],
  ]
  return (
    <ScrollArea
      className="h-48 w-full max-w-sm rounded-surface border border-separator bg-surface"
      contentClassName="p-3"
      orientation="both"
    >
      <table className="w-max border-separate border-spacing-x-6 border-spacing-y-1 text-left">
        <thead>
          <tr>
            {columnas.map((columna) => (
              <th className="text-footnote whitespace-nowrap text-label-secondary" key={columna} scope="col">
                {columna}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {filas.map((fila) => (
            <tr key={fila[0]}>
              {fila.map((celda) => (
                <td className="text-callout whitespace-nowrap text-label" key={celda}>
                  {celda}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </ScrollArea>
  )
}
```

### Solo horizontal

Una fila de tarjetas que se corre de costado. Con `orientation="horizontal"` no se dibuja la barra vertical, que acá no tendría nada que hacer.

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

function Horizontal() {
  const meses = ["Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre"]
  return (
    <ScrollArea className="w-full max-w-sm" contentClassName="pb-3" orientation="horizontal">
      <div className="flex w-max gap-3">
        {meses.map((mes, indice) => (
          <div className="flex w-40 flex-col gap-1 rounded-surface border border-separator bg-surface p-4" key={mes}>
            <span className="text-footnote text-label-secondary">{mes}</span>
            <span className="text-title-3 text-label">$ {(420 + indice * 37).toLocaleString("es-AR")}k</span>
          </div>
        ))}
      </div>
    </ScrollArea>
  )
}
```

## Props

### ScrollArea

Hereda las props de `ScrollArea.Root`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `contentClassName` | `string` | — | Clases del contenido, dentro del viewport. Ahí va el padding. |
| `orientation` | `"horizontal" \| "vertical" \| "both"` | `"vertical"` | `vertical` (default) · `horizontal` · `both`, que agrega la esquina. |
| `viewportClassName` | `string` | — | Clases del viewport: el elemento que scrollea y recibe el foco. |
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

### ScrollAreaScrollbar

Hereda las props de `ScrollArea.Scrollbar`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

## Teclado

| Tecla | Qué hace |
|---|---|
| Tab | Llega al viewport cuando hay desborde. |
| ↑ ↓ ← → | Scrollean, como en cualquier caja con overflow. |
| Re Pág · Av Pág · Inicio · Fin | Saltan de a una pantalla o a los extremos. |

## Accesibilidad

- Adentro hay un `div` con `overflow` nativo: la rueda, el trackpad y el arrastre táctil funcionan como siempre. Lo único que cambia es que se oculta la barra del sistema.
- Base UI le pone `tabIndex={0}` al viewport cuando hay desborde, así que se llega con Tab y se scrollea con las flechas. Por eso el foco es visible.
- En táctil la barra propia no se muestra: ahí la nativa ya es un overlay que aparece y se va.
- `overscroll-contain` evita que el scroll se escape a la página al llegar al final.

## Reglas de uso

- **No para la página entera.** El scroll del documento es del navegador; esto es para una caja: una lista dentro de un panel, un log, una tabla ancha.
- La caja necesita un alto (o un ancho) propio: sin límite no hay desborde y no hay nada que scrollear.
- El padding va en `contentClassName`, no en el viewport: si no, el contenido se corta contra la barra.
- `orientation="both"` solo cuando de verdad desborda en los dos ejes; si no, sobra una barra.

## Relacionados

[table](/docs/components/table.md) · [card](/docs/components/card.md) · [sidebar](/docs/components/sidebar.md)
