# onaris-ui · Componentes

Generado desde la guía (pages/components-*.html). 32 componentes en 5 categorías.

---

## Forms

### Button

`<Button />`

Acciones primarias y secundarias. Seis variantes semánticas, tres tamaños, cinco estados. El variante primary es el CTA global; secondary para acciones neutras; ghost para acciones terciarias; danger para destructivas; link para navegación inline. Todos los tamaños por defecto son de 32px para maximizar densidad en backoffice.

**Variantes / estados documentados**

- Variantes — primary · secondary · ghost · danger · soft-accent · link
- Tamaños — sm (28) · md (32, default) · lg (40)
- Estados · Primary
- Estados · Secondary
- Loading y block

**Props · Button**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `variant` | `'primary' \| 'secondary' \| 'ghost' \| 'danger' \| 'soft-accent' \| 'link'` | 'primary' | Estilo visual y semántica. |
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | Altura del botón. md = 32px. |
| `loading` | `boolean` | false | Muestra spinner y bloquea clicks. |
| `disabled` | `boolean` | false | Desactiva el botón (opacity 0.5, no cursor). |
| `block` | `boolean` | false | Ocupa todo el ancho disponible. |
| `type` | `'button' \| 'submit' \| 'reset'` | 'button' | Tipo nativo HTML. |
| `onClick` | `(e) => void` | — | Handler de click. |
| `children` | `ReactNode` | — | Contenido del botón. |

```tsx
// Uso básico
<Button variant="primary">Guardar</Button>
<Button variant="secondary" size="sm">Cancelar</Button>
<Button variant="danger" loading>Eliminar</Button>
<Button variant="primary" block>Continuar</Button>
```

### IconButton

`<IconButton />`

Botón cuadrado de icono puro. Para barras de acciones densas, toolbars y controles inline donde el texto ocuparía demasiado. Requiere aria-label obligatorio. Cuando se use junto a iconos de Onaris UI, el contenedor aplica color vía currentColor.

**Variantes / estados documentados**

- Variantes — ghost · outline · primary · circle
- Tamaños — sm (28) · md (32) · lg (40)

**Props · IconButton**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `variant` | `'ghost' \| 'outline' \| 'primary'` | 'ghost' | Estilo visual. |
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | 28 / 32 / 40 px. |
| `shape` | `'square' \| 'circle'` | 'square' | Forma del contenedor. |
| `aria-label` | `string` | — (requerido) | Etiqueta accesible. Obligatoria. |
| `disabled` | `boolean` | false | Desactiva el botón. |

```tsx
<IconButton variant="ghost" aria-label="Editar">
  <IconLapiz />
</IconButton>
```

### Input

`<Input />`

Campo de texto de una línea. Soporta todos los type HTML (text, number, password, email, search, tel, url). Acepta prefix y suffix para unidades, iconos o acciones. Siempre se usa envuelto en <Field> con label y hint opcional.

**Variantes / estados documentados**

- Estructura base — Label + Input + Hint
- Tamaños — sm (28) · md (32, default) · lg (40)
- Estados
- Con prefix / suffix
- Con botón de limpiar — aparece al enfocar un campo con texto
- Error

**Props · Input**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `type` | `'text' \| 'number' \| 'password' \| 'email' \| 'search' \| 'tel' \| 'url'` | 'text' | Tipo nativo HTML. |
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | 28 / 32 / 40 px. |
| `prefix` | `ReactNode` | — | Contenido en el borde izquierdo (unidad, icono…). |
| `suffix` | `ReactNode` | — | Contenido en el borde derecho. |
| `value` | `string` | — | Valor controlado. |
| `onChange` | `(e) => void` | — | Handler de cambio. |
| `placeholder` | `string` | — | Texto de ayuda inline. |
| `disabled` | `boolean` | false | Desactiva el input. |
| `readOnly` | `boolean` | false | Solo lectura. |
| `invalid` | `boolean` | false | Aplica estilo de error. |
| `clearable` | `boolean` | false | Muestra el botón ✕ al enfocar si hay texto. Lo borra de un clic. |

```tsx
<Field label="Nombre" hint="Aparecerá en la ficha">
  <Input placeholder="Camiseta algodón" />
</Field>

<Field label="Precio">
  <Input type="number" prefix="€" />
</Field>

<Field label="Email" error="Formato no válido">
  <Input type="email" />
</Field>
```

### PasswordInput

`<PasswordInput />`

Campo de contraseña con botón para mostrar/ocultar el texto, usando los iconos ojo / ojo-oculto de Onaris. Por defecto oculta el contenido.

**Variantes / estados documentados**

- Oculta / visible

**Props · PasswordInput**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `string` | — | Valor controlado. |
| `onChange` | `(e) => void` | — | Handler de cambio. |
| `defaultVisible` | `boolean` | false | Empieza mostrando el texto. |
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | Altura del campo. |
| `disabled` | `boolean` | false | Desactiva el campo. |

```tsx
<Field label="Contraseña">
  <PasswordInput value={pw} onChange={setPw} />
</Field>
```

### Textarea

`<Textarea />`

Campo de texto multilínea, redimensionable en vertical. Mismas convenciones que Input: se usa dentro de Field con label y hint.

**Variantes / estados documentados**

- Estados

**Props · Textarea**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `rows` | `number` | 3 | Alto inicial en líneas. |
| `maxLength` | `number` | — | Límite de caracteres. |
| `resize` | `'none' \| 'vertical' \| 'both'` | 'vertical' | Comportamiento de redimensionado. |
| `value` | `string` | — | Valor controlado. |
| `onChange` | `(e) => void` | — | Handler de cambio. |
| `disabled` | `boolean` | false | Desactiva el campo. |
| `invalid` | `boolean` | false | Aplica estilo de error. |

```tsx
<Field label="Descripción">
  <Textarea rows={4} placeholder="Describe el artículo…" />
</Field>
```

### Select

`<Select />`

Desplegable de selección única. Es un combobox custom, no un <select> nativo: así el menú desplegado usa los mismos tokens que el resto del sistema en todos los navegadores. Prueba a abrir cualquiera de los ejemplos — el menú es idéntico al documentado abajo.

**Variantes / estados documentados**

- Tamaños — S (28) · M (32, default) · L (40) — clicables
- Estados
- Desplegable abierto — estado is-open, con la opción seleccionada marcada

**Props · Select**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `string \| number` | — | Opción seleccionada. |
| `onChange` | `(value) => void` | — | Handler de cambio. |
| `options` | `{ value, label }[]` | — | Alternativa a children. |
| `searchable` | `boolean` | false | Activa combobox con filtro. |
| `clearable` | `boolean` | false | Botón para limpiar selección. |
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | Altura del control. |
| `placeholder` | `string` | — | Texto cuando no hay selección. |
| `disabled` | `boolean` | false | Desactiva el control. |
| `invalid` | `boolean` | false | Aplica estilo de error. |

```tsx
<Field label="Categoría">
  <Select value={category} onChange={setCategory}>
    <Select.Option value="camisetas">Camisetas</Select.Option>
    <Select.Option value="pantalones">Pantalones</Select.Option>
  </Select>
</Field>

// Con búsqueda
<Select searchable options={cats} />
```

### DatePicker

`<DatePicker />`

Selección de fecha con icono calendario de Onaris. Al enfocar despliega un calendario; admite rango con range. El input es editable manualmente respetando el formato.

**Variantes / estados documentados**

- Campo

**Props · DatePicker**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `Date \| [Date, Date]` | — | Fecha o rango seleccionado. |
| `onChange` | `(value) => void` | — | Handler de cambio. |
| `range` | `boolean` | false | Selección de rango. |
| `format` | `string` | 'dd/MM/yyyy' | Formato de visualización. |
| `min / max` | `Date` | — | Límites seleccionables. |
| `disabled` | `boolean` | false | Desactiva el campo. |

```tsx
<DatePicker value={date} onChange={setDate} format="dd/MM/yyyy" />
<DatePicker range value={[from, to]} onChange={setRange} />
```

### TimePicker

`<TimePicker />`

Selección de hora con icono reloj de Onaris. Formato 24h por defecto, con paso configurable (minutos).

**Variantes / estados documentados**

- Campo

**Props · TimePicker**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `string` | — | Hora en formato 'HH:mm'. |
| `onChange` | `(value) => void` | — | Handler de cambio. |
| `format` | `'24h' \| '12h'` | '24h' | Formato horario. |
| `step` | `number` | 30 | Intervalo en minutos. |
| `disabled` | `boolean` | false | Desactiva el campo. |

```tsx
<TimePicker value={time} onChange={setTime} step={15} />
```

### ColorPicker

`<ColorPicker />`

Selección de color con muestra (swatch) a la izquierda y valor hexadecimal editable. Al abrir despliega un selector. Útil para marcar familias, etiquetas o temas de catálogo.

**Variantes / estados documentados**

- Campo

**Props · ColorPicker**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `string` | — | Color en hex (#RRGGBB). |
| `onChange` | `(value) => void` | — | Handler de cambio. |
| `presets` | `string[]` | — | Colores predefinidos (paleta de marca). |
| `format` | `'hex' \| 'rgb'` | 'hex' | Formato del valor. |
| `disabled` | `boolean` | false | Desactiva el campo. |

```tsx
<ColorPicker value={color} onChange={setColor} />
```

### Checkbox

`<Checkbox />`

Selección múltiple. Admite estado indeterminate para headers de tabla que controlan la selección de todas las filas. Label clickeable por defecto.

**Variantes / estados documentados**

- Variantes
- Estados
- Grupo vertical

**Props · Checkbox**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `checked` | `boolean` | false | Estado controlado. |
| `defaultChecked` | `boolean` | false | Estado inicial no controlado. |
| `indeterminate` | `boolean` | false | Tercer estado (ni on ni off). |
| `onChange` | `(checked) => void` | — | Handler de cambio. |
| `disabled` | `boolean` | false | Desactiva el checkbox. |
| `children` | `ReactNode` | — | Etiqueta inline (clickeable). |

```tsx
<Checkbox checked={value} onChange={setValue}>
  Destacar en catálogo
</Checkbox>

<Checkbox indeterminate>Todos</Checkbox>
```

### Radio

`<Radio /> · <RadioGroup />`

Selección única exclusiva. Siempre se usa dentro de RadioGroup, que coordina el estado y el name del grupo.

**Variantes / estados documentados**

- Estados
- Grupo vertical

**Props · RadioGroup**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `name` | `string` | — | Nombre del grupo (para accesibilidad). |
| `value` | `string` | — | Valor seleccionado. |
| `onChange` | `(value) => void` | — | Handler de cambio. |
| `orientation` | `'vertical' \| 'horizontal'` | 'vertical' | Dirección de layout. |

**Props · Radio**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `string` | — | Valor que representa este radio. |
| `disabled` | `boolean` | false | Desactiva este radio. |
| `children` | `ReactNode` | — | Etiqueta. |

```tsx
<RadioGroup name="visibilidad" value={value} onChange={setValue}>
  <Radio value="clientes">Solo clientes</Radio>
  <Radio value="todos">Todos</Radio>
  <Radio value="admin">Privado</Radio>
</RadioGroup>
```

### Switch

`<Switch />`

Toggle de activación instantánea. Usar para ajustes que se aplican en el momento (modo oscuro, visibilidad, notificaciones). Para opciones que se confirman con submit, usar Checkbox.

**Variantes / estados documentados**

- Estados
- Con label inline

**Props · Switch**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `checked` | `boolean` | false | Estado controlado. |
| `defaultChecked` | `boolean` | false | Estado inicial no controlado. |
| `onChange` | `(checked) => void` | — | Handler de cambio. |
| `size` | `'md' \| 'lg'` | 'md' | 28×16 o 36×20 px. |
| `disabled` | `boolean` | false | Desactiva el switch. |
| `children` | `ReactNode` | — | Label inline (clickeable). |

```tsx
<Switch checked={dark} onChange={setDark}>
  Modo oscuro
</Switch>
```

---

## Feedback

### Badge

`<Badge />`

Etiqueta corta para marcar estados, categorías o conteos. Por defecto en variante soft (fondo tintado + texto oscuro); variante solid cuando se necesita mayor contraste (sobre imágenes, tarjetas oscuras).

**Variantes / estados documentados**

- Variantes soft (default)
- Variantes solid
- Tamaños — sm (16) · md (18, default) · lg (22)
- Count y dot — Variantes especiales

**Props · Badge**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `variant` | `'neutral' \| 'accent' \| 'success' \| 'warning' \| 'danger' \| 'info'` | 'neutral' | Color semántico. |
| `solid` | `boolean` | false | Fondo sólido en vez de soft. |
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | Tamaño del badge. |
| `count` | `boolean` | false | Forma circular para números. |
| `dot` | `boolean` | false | Punto de 8px sin texto. |
| `children` | `ReactNode` | — | Contenido (ignorado si dot). |

```tsx
<Badge variant="success">Activo</Badge>
<Badge variant="danger" solid>Crítico</Badge>
<Badge variant="accent" count>12</Badge>
<Badge variant="success" dot />
```

### Pill

`<Pill />`

Indicador de estado con semáforo de color integrado (dot delante). Se usa en tablas, headers de entidad y listas para comunicar el estado de un registro a golpe de vista.

**Variantes / estados documentados**

- Variantes semánticas
- Tamaños — sm (18) · md (22, default) · lg (26)
- Uso en contexto

**Props · Pill**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `variant` | `'neutral' \| 'accent' \| 'success' \| 'warning' \| 'danger' \| 'info'` | 'neutral' | Color semántico. |
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | Tamaño de la pill. |
| `children` | `ReactNode` | — | Etiqueta del estado. |

```tsx
<Pill variant="success">Publicado</Pill>
<Pill variant="warning" size="sm">Borrador</Pill>
```

### Alert

`<Alert />`

Mensaje informativo inline, embebido en el flujo de la página. Para notificaciones no intrusivas usar Toast; para bloqueos usar Modal.

**Variantes / estados documentados**

- Variantes

**Props · Alert**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `variant` | `'info' \| 'success' \| 'warning' \| 'danger' \| 'neutral'` | 'info' | Color semántico. |
| `title` | `string` | — | Título del alert. |
| `icon` | `ReactNode \| false` | auto | Icono custom o false para ocultar. |
| `closable` | `boolean` | false | Muestra botón de cierre. |
| `onClose` | `() => void` | — | Handler de cierre. |
| `children` | `ReactNode` | — | Descripción / contenido. |

```tsx
<Alert variant="warning" title="Hay cambios sin guardar" closable>
  Si sales ahora perderás las modificaciones.
</Alert>
```

### Toast

`<Toast /> · toast()`

Notificación flotante que aparece en la esquina (por defecto bottom-right), auto-desaparece a los 5s y puede apilarse. Se dispara imperativamente con la función toast(), no se renderiza en JSX salvo para el <Toaster /> provider.

**Variantes / estados documentados**

- Variantes

**Props · Toast**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `title` | `string` | — | Primer argumento, título del toast. |
| `options.description` | `string` | — | Línea secundaria. |
| `options.duration` | `number` | 5000 | Ms antes de auto-cerrar. 0 = no cierra. |
| `options.actions` | `{ label, onClick }[]` | — | Hasta 2 acciones inline. |
| `options.closable` | `boolean` | true | Muestra botón de cierre. |

**Props · <Toaster />**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `position` | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left' \| 'top-center'` | 'bottom-right' | Esquina de aparición. |
| `max` | `number` | 5 | Máximo de toasts visibles simultáneamente. |

```tsx
// 1. Añadir el Toaster una vez en el root de la app
<Toaster position="bottom-right" />

// 2. Disparar toasts imperativamente
toast.success("Artículo guardado", { description: "Se ha publicado." });
toast.error("No se pudo eliminar");
toast.warning("Sincronización parcial", {
  description: "3 de 24 no se actualizaron.",
  actions: [
    { label: "Ver detalle", onClick: openDetail },
    { label: "Reintentar", onClick: retry },
  ]
});
toast.info("Exportación en curso", { duration: 0 });
```

### Progress

`<Progress />`

Barra de progreso lineal. Usa value determinado (0–100) para operaciones medibles; indeterminate para procesos sin cuantificar (cargando datos, guardando).

**Variantes / estados documentados**

- Valores
- Estados — en progreso (naranja) · finalizado · error
- Indeterminado

**Props · Progress**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `number (0–100)` | 0 | Porcentaje completado. |
| `indeterminate` | `boolean` | false | Animación sin valor concreto. |
| `variant` | `'progress' \| 'success' \| 'danger'` | 'progress' | Estado: en progreso (naranja) · finalizado (success) · error (danger). |
| `label` | `string` | — | Etiqueta encima de la barra. |
| `showValue` | `boolean` | false | Muestra el % al lado del label. |

```tsx
<Progress value={62} />
<Progress value={100} variant="success" />
<Progress indeterminate />
```

### Spinner

`<Spinner />`

Indicador circular de carga. Para cargas dentro de botones usar <Button loading />; para áreas de contenido mayores, combinar con overlay o usar Skeleton.

**Variantes / estados documentados**

- Tamaños — sm (16) · md (24, default) · lg (32)
- Colores — accent (sin fondo) · white (sobre leading)
- Con label

**Props · Spinner**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | 16 / 24 / 32 px. |
| `color` | `'accent' \| 'white'` | 'accent' | accent sin fondo · white sobre pastilla leading. |
| `label` | `string` | — | Texto accesible (sr-only). |

```tsx
<Spinner />
<Spinner size="sm" />
<Spinner color="white" />  // sobre fondo leading
```

### Skeleton

`<Skeleton />`

Placeholder animado que reproduce la silueta del contenido real mientras se carga. Preferible al spinner para listas y cards: reduce la percepción de espera y mantiene la estabilidad del layout.

**Variantes / estados documentados**

- Primitivas
- Card de artículo — Ejemplo compuesto

**Props · Skeleton**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `variant` | `'text' \| 'title' \| 'circle' \| 'rect'` | 'text' | Forma del placeholder. |
| `width` | `string \| number` | '100%' | Ancho (px o %). |
| `height` | `string \| number` | auto | Alto (px o %). Ignorado si variant text/title. |
| `lines` | `number` | 1 | Solo variant 'text': repite líneas con ancho decreciente. |
| `size` | `number` | 40 | Solo variant 'circle': diámetro. |

```tsx
<Skeleton variant="title" width="60%" />
<Skeleton variant="text" lines={3} />
<Skeleton variant="circle" size={40} />
<Skeleton variant="rect" width={56} height={56} />
```

### Empty state

`<Empty />`

Pantalla de contenido vacío. Obligatorio incluir al menos una acción primaria que desbloquee al usuario (crear, importar, invitar). Nunca mostrar solo el mensaje.

**Variantes / estados documentados**

- Sin resultados de búsqueda
- Colección vacía (first run)
- Error de carga

**Props · Empty**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `icon` | `ReactNode` | — | Icono superior (64×64 contenedor). |
| `tone` | `'neutral' \| 'accent' \| 'danger'` | 'neutral' | Color del círculo del icono. |
| `title` | `string` | — | Título principal. |
| `description` | `string` | — | Descripción de la situación. |
| `actions` | `ReactNode[]` | — | Array de acciones (1–2 botones). |

```tsx
<Empty
  icon={<IconSearch />}
  title="No encontramos artículos"
  description="Prueba con otros términos o quita los filtros."
  actions={[
    <Button variant="secondary">Quitar filtros</Button>,
    <Button variant="primary">Nuevo artículo</Button>
  ]}
/>
```

---

## Overlays

### Modal

`<Modal />`

Diálogo centrado con overlay que bloquea la interacción de fondo. Para acciones que requieren foco total del usuario: confirmaciones, formularios cortos, decisiones críticas. Tres composiciones: estándar, confirmación (con icono semántico) y formulario.

**Variantes / estados documentados**

- Confirmación destructiva — size sm + icono danger
- Formulario — size md + footer between

**Props · Modal**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `open` | `boolean` | false | Controla la visibilidad. |
| `onClose` | `() => void` | — | Se invoca al cerrar (Esc, overlay, botón). |
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | 380 / 480 / 720 px. |
| `closeOnOverlay` | `boolean` | true | Cerrar al clicar el fondo. |
| `closeOnEsc` | `boolean` | true | Cerrar con tecla Escape. |
| `initialFocus` | `RefObject` | — | Elemento que recibe foco al abrir. |

```tsx
<Modal open={open} onClose={close} size="sm">
  <Modal.Header>Eliminar artículo</Modal.Header>
  <Modal.Body icon={<IconTrash />} tone="danger">
    ¿Seguro que quieres eliminar este artículo?
  </Modal.Body>
  <Modal.Footer>
    <Button variant="secondary">Cancelar</Button>
    <Button variant="danger">Eliminar</Button>
  </Modal.Footer>
</Modal>
```

### Drawer

`<Drawer />`

Panel lateral deslizante. Ideal para edición contextual sin perder el contexto de la lista (configurar un artículo, filtros avanzados, detalle de pedido). Posiciones: right (default), left, bottom.

**Variantes / estados documentados**

- Panel derecho de edición

**Props · Drawer**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `open` | `boolean` | false | Controla la visibilidad. |
| `onClose` | `() => void` | — | Handler de cierre. |
| `position` | `'right' \| 'left' \| 'bottom'` | 'right' | Borde de aparición. |
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | 320 / 380 / 480 px (ancho). |
| `closeOnOverlay` | `boolean` | true | Cerrar al clicar el fondo. |

```tsx
<Drawer open={open} onClose={close} position="right" size="md">
  <Drawer.Header kicker="Editar artículo">
    Camiseta algodón orgánico
  </Drawer.Header>
  <Drawer.Body>{/* campos */}</Drawer.Body>
  <Drawer.Footer>
    <Button variant="secondary">Cancelar</Button>
    <Button>Guardar</Button>
  </Drawer.Footer>
</Drawer>
```

### Tooltip

`<Tooltip />`

Etiqueta contextual que aparece al hover/focus. Solo para texto breve aclaratorio (nunca interactivo — para contenido con acciones usar Popover). Aparece tras 400ms de delay.

**Variantes / estados documentados**

- Simple · 4 posiciones
- Rich (con título)

**Props · Tooltip**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `content` | `string \| ReactNode` | — | Texto o cuerpo del tooltip. |
| `title` | `string` | — | Título (activa variante rich). |
| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | 'top' | Posición relativa al trigger. |
| `delay` | `number` | 400 | Ms antes de mostrarse. |
| `children` | `ReactElement` | — | Elemento disparador. |

```tsx
<Tooltip content="Duplicar artículo">
  <IconButton aria-label="Duplicar"><IconDuplicar /></IconButton>
</Tooltip>

<Tooltip title="SKU sincronizado" content="Hace 3 min" placement="top">
  <Badge variant="success">Sync</Badge>
</Tooltip>
```

### Popover & Menu

`<Popover /> · <Menu />`

Panel flotante anclado a un disparador, con contenido interactivo (a diferencia del Tooltip). Menu es la variante especializada en listas de acciones: items, atajos, separadores y acciones destructivas.

**Variantes / estados documentados**

- Popover genérico
- Menu de acciones — con secciones, atajos y acción destructiva

**Props · Popover / Menu**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `trigger` | `ReactElement` | — | Elemento que abre el panel. |
| `placement` | `'bottom-start' \| 'bottom-end' \| 'top-start' \| …` | 'bottom-start' | Anclaje respecto al trigger. |
| `open` | `boolean` | — | Modo controlado (opcional). |
| `onOpenChange` | `(open) => void` | — | Cambio de estado. |

**Props · Menu.Item**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `icon` | `ReactNode` | — | Icono a la izquierda. |
| `shortcut` | `string` | — | Atajo de teclado mostrado a la derecha. |
| `danger` | `boolean` | false | Estilo destructivo (rojo). |
| `onSelect` | `() => void` | — | Handler al elegir el item. |

```tsx
// Popover
<Popover trigger={<Button>Precio</Button>} placement="bottom-start">
  {/* contenido interactivo */}
</Popover>

// Menu
<Menu trigger={<IconButton aria-label="Opciones">…</IconButton>}>
  <Menu.Label>Artículo</Menu.Label>
  <Menu.Item icon={<IconLapiz />} shortcut="E">Editar</Menu.Item>
  <Menu.Divider />
  <Menu.Item danger icon={<IconEliminar />}>Eliminar</Menu.Item>
</Menu>
```

---

## Navigation

### Tabs

`<Tabs />`

Conmutación entre vistas de un mismo contexto. Tres variantes: underline (default, para navegación de página), pill (para filtros suaves) y solid (segmented, para alternar modos de vista).

**Variantes / estados documentados**

- Underline (default)
- Pill
- Solid (segmented)

**Props · Tabs**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `string` | — | Tab activa (controlado). |
| `onChange` | `(value) => void` | — | Handler de cambio. |
| `variant` | `'underline' \| 'pill' \| 'solid'` | 'underline' | Estilo visual. |

**Props · Tabs.Tab**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `string` | — | Identificador de la tab. |
| `count` | `number` | — | Contador opcional. |
| `disabled` | `boolean` | false | Desactiva la tab. |

```tsx
<Tabs value={tab} onChange={setTab} variant="underline">
  <Tabs.Tab value="articulos" count={128}>Artículos</Tabs.Tab>
  <Tabs.Tab value="familias" count={12}>Familias</Tabs.Tab>
</Tabs>
```

### Accordion

`<Accordion />`

Secciones colapsables para organizar contenido extenso (configuración de artículo, FAQs, filtros agrupados). Modo single (solo una abierta) o multiple.

**Variantes / estados documentados**

- Modo single

**Props · Accordion**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `type` | `'single' \| 'multiple'` | 'single' | Cuántos paneles abiertos a la vez. |
| `defaultValue` | `string \| string[]` | — | Panel(es) abierto(s) al inicio. |
| `collapsible` | `boolean` | true | Permite cerrar todos (solo single). |

**Props · Accordion.Item**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `string` | — | Identificador del panel. |
| `title` | `string` | — | Texto del trigger. |

```tsx
<Accordion type="single" defaultValue="general">
  <Accordion.Item value="general" title="Información general">
    {/* contenido */}
  </Accordion.Item>
</Accordion>
```

### Breadcrumbs

`<Breadcrumbs />`

Ruta jerárquica que indica dónde está el usuario y permite subir niveles. Esencial en la navegación profunda de catálogos (Catálogo › Familia › Subfamilia › Artículo).

**Variantes / estados documentados**

- Estándar
- Colapsado — rutas largas con menú intermedio

**Props · Breadcrumbs**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `maxItems` | `number` | — | Colapsa el centro si se supera. |
| `separator` | `ReactNode` | chevron | Separador custom. |

**Props · Breadcrumbs.Item**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `href` | `string` | — | Destino del enlace. |
| `current` | `boolean` | false | Marca el item actual (no clickable). |

```tsx
<Breadcrumbs>
  <Breadcrumbs.Item href="/catalogos">Catálogos</Breadcrumbs.Item>
  <Breadcrumbs.Item href="/c/primavera">Primavera 2026</Breadcrumbs.Item>
  <Breadcrumbs.Item current>Algodón orgánico</Breadcrumbs.Item>
</Breadcrumbs>
```

### Pagination

`<Pagination />`

Navegación entre páginas de resultados. Muestra primera/última, vecinas y elipsis para rangos largos. Acompañar con el rango de resultados visible.

**Variantes / estados documentados**

- Numerada con info
- Tamaño sm

**Props · Pagination**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `page` | `number` | 1 | Página actual. |
| `pageCount` | `number` | — | Total de páginas. |
| `onPageChange` | `(page) => void` | — | Handler de cambio. |
| `siblingCount` | `number` | 1 | Vecinas a cada lado de la actual. |
| `size` | `'sm' \| 'md'` | 'md' | 28 o 32 px por botón. |

```tsx
<Pagination
  page={1}
  pageCount={13}
  onPageChange={setPage}
  siblingCount={1}
/>
```

### Tree

`<Tree />`

Navegación jerárquica expandible. Es la columna vertebral del backoffice de catálogos: árbol de catálogos › familias › subfamilias. Nodo activo con barra accent a la izquierda.

**Variantes / estados documentados**

- Árbol de catálogo

**Props · Tree**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `value` | `string` | — | Id del nodo seleccionado. |
| `onSelect` | `(id) => void` | — | Handler de selección. |
| `defaultExpanded` | `string[]` | — | Nodos abiertos al inicio. |

**Props · Tree.Node**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `id` | `string` | — | Identificador único. |
| `label` | `string` | — | Texto del nodo. |
| `icon` | `ReactNode` | — | Icono a la izquierda. |
| `count` | `number` | — | Contador a la derecha. |

```tsx
<Tree value={selectedId} onSelect={setSelected}>
  <Tree.Node id="primavera" label="Primavera 2026" count={128}>
    <Tree.Node id="camisetas" label="Camisetas" count={42} />
    <Tree.Node id="pantalones" label="Pantalones">…</Tree.Node>
  </Tree.Node>
</Tree>
```

---

## Data

### Table

`<Table />`

Tabla de datos con cabecera ordenable, selección múltiple y acciones por fila. Las columnas numéricas se alinean a la derecha con tabular-nums para que los dígitos cuadren verticalmente. Dos densidades: normal (40px de fila) y compacta (32px).

**Variantes / estados documentados**

- Tabla completa — toolbar + orden + selección + acciones + footer
- Densidad compacta — filas de 32px para listados largos

**Props · Table**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `columns` | `Column[]` | — | Definición de columnas (ver abajo). |
| `data` | `T[]` | [] | Filas a renderizar. |
| `density` | `'default' \| 'compact'` | 'default' | Alto de fila: 40px ó 32px. |
| `striped` | `boolean` | false | Bandas alternas en las filas. |
| `selectable` | `boolean` | false | Añade columna de checkbox y selección múltiple. |
| `selectedKeys` | `string[]` | — | Filas seleccionadas (controlado). |
| `onSelectionChange` | `(keys) => void` | — | Cambio de selección. |
| `sort` | `{ key, dir }` | — | Estado de ordenación. |
| `onSortChange` | `(sort) => void` | — | Click en cabecera ordenable. |
| `onRowClick` | `(row) => void` | — | Click en fila. |
| `loading` | `boolean` | false | Muestra filas Skeleton. |
| `empty` | `ReactNode` | — | Contenido cuando no hay datos (usar Empty). |

**Props · Column**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `key` | `string` | — | Campo del dato. |
| `label` | `string` | — | Texto de cabecera. |
| `align` | `'left' \| 'right' \| 'center'` | 'left' | Alineación. Numéricas siempre a la derecha. |
| `numeric` | `boolean` | false | Aplica fuente mono y tabular-nums. |
| `sortable` | `boolean` | false | Cabecera clicable para ordenar. |
| `width` | `string \| number` | auto | Ancho fijo de la columna. |
| `render` | `(row) => ReactNode` | — | Render personalizado de la celda. |

```tsx
<Table
  columns={[
    { key: 'name',  label: 'Artículo', sortable: true },
    { key: 'sku',   label: 'SKU', mono: true },
    { key: 'state', label: 'Estado', render: r => <Pill variant={r.tone}>{r.state}</Pill> },
    { key: 'price', label: 'Precio', align: 'right', numeric: true },
  ]}
  data={items}
  selectable
  density="compact"
  onRowClick={openDetail}
/>
```

### Card

`<Card />`

Contenedor de contenido agrupado. Las cards se separan por borde, nunca por sombra: la elevación se reserva para elementos que flotan sobre el contenido. Tres composiciones: básica, interactiva (clicable) y de métrica.

**Variantes / estados documentados**

- Básica — header + body + footer
- Interactiva con media — card de artículo · estados default / hover / selected
- Métrica — para dashboards · MetricCard

**Props · Card**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `interactive` | `boolean` | false | Cursor pointer y hover de borde. |
| `selected` | `boolean` | false | Borde accent. |
| `onClick` | `() => void` | — | Handler de click (implica interactive). |
| `padding` | `'none' \| 'sm' \| 'md'` | 'md' | Padding del body. |

**Props · MetricCard**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `label` | `string` | — | Etiqueta superior en mayúsculas. |
| `value` | `string \| number` | — | Cifra principal. |
| `delta` | `{ value, direction }` | — | direction: 'up' | 'down' | 'flat'. |

```tsx
<Card>
  <Card.Header title="Temporada Primavera 2026" subtitle="Distribuciones García" />
  <Card.Body>248 artículos en 12 familias.</Card.Body>
  <Card.Footer><Button size="sm">Abrir</Button></Card.Footer>
</Card>

<MetricCard label="Artículos activos" value="1.284"
  delta={{ value: '+12,4%', direction: 'up' }} />
```

### Avatar

`<Avatar /> · <AvatarGroup />`

Representación de una persona o entidad. Prioriza imagen; si no hay, cae a iniciales; si tampoco, a icono. Admite indicador de estado y agrupación con solapamiento.

**Variantes / estados documentados**

- Tipos — imagen · iniciales · icono
- Tamaños — sm (24) · md (32) · lg (40)
- Con estado
- Grupo — solapado, con contador de resto

**Props · Avatar**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `src` | `string` | — | URL de la imagen. Si falla, cae a iniciales. |
| `name` | `string` | — | Nombre completo. Genera las iniciales y el alt. |
| `size` | `'sm' \| 'md' \| 'lg'` | 'md' | 24 / 32 / 40 px. |
| `shape` | `'circle' \| 'square'` | 'circle' | Forma del contenedor. |
| `tone` | `'leading' \| 'accent' \| 'soft' \| 'neutral'` | 'leading' | Color de fondo cuando no hay imagen. |
| `status` | `'online' \| 'busy' \| 'away' \| 'offline'` | — | Punto indicador en la esquina. |

**Props · AvatarGroup**

| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| `max` | `number` | 4 | Avatares visibles antes del contador. |
| `size` | `AvatarSize` | 'sm' | Tamaño aplicado a todos los hijos. |

```tsx
<Avatar src={user.photo} name="María García" size="md" />
<Avatar name="María García" status="online" />

<AvatarGroup max={3}>
  {users.map(u => <Avatar key={u.id} name={u.name} src={u.photo} />)}
</AvatarGroup>
```
