# Splitter

Duas áreas com uma divisória que se arrasta: lista à esquerda e detalhe à
direita, árvore e conteúdo, tabela e inspetor.

A divisória é um `separator` de verdade, com valor, mínimo e máximo, e anda
pelas setas: `Home` e `End` vão aos extremos. Arrastar com o mouse é metade da
peça: sem teclado, quem não usa ponteiro fica preso na proporção que o
desenvolvedor escolheu, e essa proporção costuma ser a que serve para a tela de
quem escreveu.

O alvo da divisória tem 25px enquanto a linha desenha 1. O número vem da WCAG
2.5.8 (Target Size Minimum, AA), que pede 24: um `::after` transparente estica
12px para cada lado, e o desenho não engorda um pixel. Ele já esticou 6px, o
alvo media 13px, e era a única mira abaixo de 24 do catálogo inteiro. Uma
divisória fácil de pegar é a diferença entre a peça funcionar e a pessoa
desistir dela.

A divisória diz a medida com unidade. `aria-valuenow` sozinho faz o leitor de
tela anunciar "50" pelado, que não é medida de coisa nenhuma; o `aria-valuetext`
diz "50%". E ela aponta para o lado que mede, por `aria-controls`: o valor
descreve sempre o primeiro lado, e sem a referência não há como saber qual dos
dois é.

O nome mora no `separator`, e não na moldura. `label` nomeia o nó que tem o
papel, porque é ele que o leitor de tela expõe; um `aria-label` escrito por quem
chama cai no mesmo lugar e vence o `label`. Antes ele parava na `div` externa,
que o Chrome guarda como um nó `generic` nomeado e nenhum leitor anuncia.

No celular os dois lados empilham e a divisória some. Duas colunas de 190px não
são duas colunas: são duas listas ilegíveis, e arrastar uma borda de 4px com o
dedo não é gesto que exista.

## A 200% de zoom a divisória some, e isso é a resposta

Zoom de 200% numa tela de 1280 deixa a viewport efetiva em 640px, que é o que a
peça já trata como estreito. Medido no Chrome a 640px e a 400px: a moldura vira
`flex-direction: column` e a alça sai em `display: none`.

É desenho, e não defeito. `display: none` tira a alça do ciclo de tabulação
junto com o desenho, então não sobra parada órfã nem alvo invisível. E o
controle perdeu o trabalho no mesmo movimento em que sumiu: empilhados, os dois
lados aparecem inteiros, um embaixo do outro, e não há proporção para negociar
entre eles. A WCAG 1.4.10 cobra o conteúdo e a função alcançáveis a 320px, e o
conteúdo fica.

Também não há aviso de que o controle sumiu, e isso é escolha. Anunciar o
desaparecimento de um controle que deixou de ter função é ruído numa região
viva, e o tamanho fica congelado no valor que estava: nada se perde ao voltar
para a largura de mesa.

Na orientação `vertical` a alça não some em largura nenhuma, porque empilhado já
é o desenho dela.

## Sentido da escrita

Em `dir="rtl"` a divisória vira junto. O `start` passa a ser o lado direito, o
arraste mede a partir da borda onde a leitura começa e as setas andam para o
lado que a pessoa vê: `→` empurra a divisória para a direita, `←` para a
esquerda, mesmo que o número de `size` ande no sentido contrário. `Home` e
`End` continuam lógicos: o mínimo e o máximo do primeiro lado, e não a esquerda
e a direita.

A direção vem do `RivoProvider`, e não de um `dir` escrito à mão num elemento
acima da peça. É o mesmo `dir` que o resto do catálogo lê, e sem ele a
divisória espelharia o desenho sem espelhar a conta: arrastar o ponteiro 120px
para a direita a moveria 118px para a esquerda.

## Quando não usar

Para esconder e mostrar uma área inteira, use `Collapsible` ou a `Sidebar`: o
splitter existe para quando as duas áreas ficam visíveis ao mesmo tempo e a
proporção entre elas é a decisão.

## No React Native

Não porta, por decisão - duas áreas lado a lado não cabem em tela estreita; no celular a lista e o detalhe são duas telas do router. Não é fila: não vai existir. A [tabela de paridade](/react-native) diz o porquê de cada uma.

## Importação

```tsx
import { Splitter } from '@rivocode/ui'
```

## Exemplos

### Lista e detalhe

```tsx
import { Card, CardContent, Splitter } from '@rivocode/ui'

const NOTAS = ['4813 · Clínica São Lucas', '4814 · Transportes Cabo Branco', '4815 · Padaria Aurora']

export function ListAndDetail() {
  return (
    <div className="h-64 w-[36rem]">
      <Splitter
        label="Lista e detalhe"
        defaultSize={38}
        min={25}
        className="h-full rounded-lg border border-border"
        start={
          <ul className="flex flex-col">
            {NOTAS.map((nota) => (
              <li key={nota} className="truncate px-3 py-2 text-sm text-fg-muted">
                {nota}
              </li>
            ))}
          </ul>
        }
        end={
          <Card className="m-3 border-0">
            <CardContent className="text-base text-fg">
              Escolha uma nota à esquerda para ver o detalhe aqui.
            </CardContent>
          </Card>
        }
      />
    </div>
  )
}
```

## Props

| Prop | Tipo | Obrigatória | Desde | O que faz |
| --- | --- | --- | --- | --- |
| `end` | `ReactNode` | sim | 0.5.0 |  |
| `label` | `string` | sim | 0.5.0 | O que o leitor de tela chama a divisoria. |
| `start` | `ReactNode` | sim | 0.5.0 | O lado que a medida descreve: a esquerda na horizontal, o topo na vertical. |
| `classNames` | `Partial<Record<"end" \| "handle" \| "start", string>>` |  | 0.5.0 | Classe por parte: `start`, `end`, `handle`. |
| `defaultSize` | `number` |  | 0.5.0 | Tamanho do primeiro lado, em porcentagem. |
| `min` | `number` |  | 0.5.0 | Quanto cada lado precisa ter, em porcentagem. |
| `onSizeChange` | `((size: number) => void)` |  | 0.5.0 |  |
| `orientation` | `"horizontal" \| "vertical"` |  | 0.5.0 |  |
| `size` | `number` |  | 0.5.0 | Controlado, quando o app quer guardar a escolha entre sessoes. |

Além dessas: repassa `className`, `style`, `id` e os demais atributos do elemento raiz.

## Ver também

- [Accordion](/componentes/accordion.md)
- [AspectRatio](/componentes/aspect-ratio.md)
- [Avatar](/componentes/avatar.md)
- [Card](/componentes/card.md)
- [Collapsible](/componentes/collapsible.md)
- [DataTable](/componentes/data-table.md)
- [Convenções da biblioteca](/convencoes.md): Provider, tokens e as regras que valem para toda peça
- [Índice completo](/llms.txt)
