# Collapsible

> Mostrar y ocultar un bloque con un botón, con el disclosure de iCloud opcional. La pieza simple detrás del Accordion.

```tsx
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "sebs7n-ui/collapsible"
```

## Ejemplos

### El detalle de una fila

`chevron` suma el disclosure de iCloud: un › que gira a ⌄ al abrir. Sin él, el trigger no trae estilo y va `render={<Button … />}`.

```tsx
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "sebs7n-ui/collapsible"

function Basico() {
  return (
    <Collapsible className="w-full max-w-sm gap-1">
      <CollapsibleTrigger chevron className="h-8 px-1 text-callout text-label">
        Factura 0012 · $ 128.400
      </CollapsibleTrigger>
      <CollapsibleContent className="flex flex-col gap-1 rounded-control bg-fill-1 p-3">
        <span>Consultoría — 40 h — $ 96.000</span>
        <span>Hosting — 1 mes — $ 18.400</span>
        <span>Soporte — 1 mes — $ 14.000</span>
      </CollapsibleContent>
    </Collapsible>
  )
}
```

### Filtros avanzados, controlado por la app

Con `open` en el estado, la app sabe si están abiertos: por ejemplo para dejarlos abiertos cuando hay un filtro aplicado.

```tsx
import { Button } from "sebs7n-ui/button"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "sebs7n-ui/collapsible"
import { DatePicker } from "sebs7n-ui/date-picker"
import { Input } from "sebs7n-ui/input"
import { Label } from "sebs7n-ui/label"
import { useId, useState } from "react"

function Controlado() {
  const [abierto, setAbierto] = useState(false)
  const desde = useId()
  const hasta = useId()
  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      <Input placeholder="Buscar factura" />
      <Collapsible className="gap-2" onOpenChange={setAbierto} open={abierto}>
        <CollapsibleTrigger render={<Button className="self-start" size="sm" variant="ghost" />}>
          {abierto ? "Ocultar filtros avanzados" : "Filtros avanzados"}
        </CollapsibleTrigger>
        <CollapsibleContent className="flex flex-col gap-3 pt-1">
          <div className="flex flex-col gap-1.5">
            <Label htmlFor={desde}>Desde</Label>
            <DatePicker id={desde} />
          </div>
          <div className="flex flex-col gap-1.5">
            <Label htmlFor={hasta}>Hasta</Label>
            <DatePicker id={hasta} />
          </div>
        </CollapsibleContent>
      </Collapsible>
    </div>
  )
}
```

### Contenido que tiene que existir cerrado

`keepMounted` lo deja en el DOM, oculto: sirve cuando un crawler o el ⌘F del navegador tienen que verlo. Sin él, el contenido cerrado no existe.

```tsx
import { Button } from "sebs7n-ui/button"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "sebs7n-ui/collapsible"

function KeepMounted() {
  return (
    <Collapsible className="w-full max-w-sm gap-1">
      <CollapsibleTrigger render={<Button size="sm" variant="secondary" />}>Ver claves de recuperación</CollapsibleTrigger>
      <CollapsibleContent className="flex flex-col gap-1 text-mono-callout" keepMounted>
        <span>alien-bean-pasta</span>
        <span>wild-irish-burrito</span>
        <span>horse-battery-staple</span>
      </CollapsibleContent>
    </Collapsible>
  )
}
```

## Props

### Collapsible

Hereda las props de `Collapsible.Root`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |
| `defaultOpen` | `boolean` | — | **Heredada de Base UI.** Si arranca abierto. Es la versión no controlada de `open`. |
| `disabled` | `boolean` | — | **Heredada de Base UI.** Apaga la interacción y lo marca con `data-disabled`, que es el atributo del que cuelgan los estilos de apagado. |
| `onOpenChange` | `((open: boolean, eventDetails: CollapsibleRootChangeEventDetails) => void)` | — | **Heredada de Base UI.** Se llama con el estado nuevo cada vez que se abre o se cierra. |
| `open` | `boolean` | — | **Heredada de Base UI.** Si está abierto. Pasarla lo vuelve controlado: sin `onOpenChange` ya no se cierra solo. |

### CollapsibleContent

Hereda las props de `Collapsible.Panel`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `panelClassName` | `string` | — | Clases del panel que anima el alto. El `className` viaja al contenido, no acá. |
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |
| `keepMounted` | `boolean` | — | **Heredada de Base UI.** Deja el contenido en el DOM mientras está cerrado, en vez de desmontarlo. Cuesta peso; sirve para que un crawler lo vea. |

### CollapsibleTrigger

Hereda las props de `Collapsible.Trigger`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `chevron` | `boolean` | `false` | Suma el chevron › después del texto, que gira a ⌄ al abrir (sin recorrido con movimiento reducido). |
| `className` | `string \| ((state: State) => string)` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

## Teclado

| Tecla | Qué hace |
|---|---|
| Enter · Espacio | Abre y cierra. |
| Tab | Entra y sale del trigger. |

## Accesibilidad

- El trigger lleva `aria-expanded` y `aria-controls`, puestos por Base UI.
- El contenido cerrado no está en el DOM salvo `keepMounted`; con `hiddenUntilFound` queda y lo encuentra el buscador del navegador.
- El alto pasa por `motion-reduce`, además del reset global del paquete.

## Reglas de uso

- Con `chevron`, el disparador trae el estilo del paquete: 24 de alto mínimo (44 de área con el dedo), foco y el chevron que gira como en `Accordion`. El tamaño de letra lo hereda del contenedor, como en 2.0 (para el del cromo, `text-callout`). Una clase propia (`min-h-*`) le gana: la regla va en `:where()`. Sin `chevron` no trae estilo: lo pone el `render`.
- **Si hay varias secciones que son un grupo, es un `Accordion`**: trae el `<h3>` por sección.
- El trigger no trae estilo a propósito: va `render={<Button variant="ghost" />}`. Con `chevron` suma el disclosure de iCloud (› que gira a ⌄) y el foco interior.
- `className` cae en el contenido, no en el elemento que anima el alto: ahí va el padding.
- Lo que está plegado no se lee ni se indexa: `keepMounted` si esos links importan para el crawler.

## Relacionados

[accordion](/docs/components/accordion.md) · [card](/docs/components/card.md) · [button](/docs/components/button.md)
