# Reglas de uso

> Las decisiones que no se ven en una tabla de props.

Las decisiones que no se ven en una tabla de props. Cada una existe porque romperla se nota.

## Jerarquía

**Un solo acento por pantalla.** `Button variant="accent"` o `Switch variant="accent"` para la acción principal; el CTA por defecto es el negro (`variant="default"`). Dos acentos compitiendo no son dos acciones importantes: son ninguna.

**`shape="pill"` solo en los CTA de un hero o de una sección de marketing.** Es `rounded-full` con un escalón más de padding horizontal (`sm` 20px, `md` 24px, `lg` 28px). **Nunca en el chrome de una app** —nav, tablas, formularios, diálogos—: dos formas de botón en la misma pantalla se leen como un descuido, no como una jerarquía. En `icon-*` se ignora, que ya es cuadrado con su propio radio.

**`variant="destructive"` solo cuando la acción borra algo**, y siempre detrás de un `AlertDialog`.

## Links

**Links con forma de botón o card:** `buttonVariants()` / `cardVariants()` sobre `<a>` o `<Link>`. **No uses `render` para links:** Base UI les pone `role="button"` y dejan de ser links para un lector de pantalla.

**Links de texto:** `linkVariants({ variant })`.

| Variante | Cuándo |
|---|---|
| `inline` | Dentro de una frase. Subrayado siempre: una línea tenue que se refuerza en hover. |
| `subtle` | Suelto y secundario. Sin subrayado en reposo; en hover sube a `gray-1000` y aparece la línea. |
| `row` | El nombre clickeable de una fila de tabla. |

Traen `rounded-sm`, `transition-control`, `focus-visible:focus-ring` y `underline-offset-4`: no los repitas en el llamador. `icon: true` alinea una flecha con el texto.

**Un link que solo se revela en hover no existe en un celular.** Si es la acción principal de la sección, va `inline`.

## Menús y navegación

**`NavigationMenu` si los ítems navegan, `DropdownMenu` si ejecutan algo.** No es cosmético: `DropdownMenu` emite `role="menu"` / `role="menuitem"`, atrapa el foco y se recorre con las flechas, así que un lector anuncia «menú, 3 elementos» en vez de una lista de links, y el modo de navegación por links no los ve. El menú de idioma y el de usuario siguen siendo `DropdownMenu` (cambian el estado, no la página).

**`DropdownMenuLabel` va dentro de `DropdownMenuGroup`.** Suelto, Base UI tira la página abajo.

**`NavigationMenuViewport` va una sola vez**, hermano de la lista: el panel es uno para todos los ítems.

## Superposiciones

**Los triggers usan `render={<Button … />}`, no `asChild`.** Vale para Dialog, Popover, Tooltip, DropdownMenu y Sheet.

**Dialog vs AlertDialog vs Sheet vs Popover vs Tooltip:**

| Uso | Componente |
|---|---|
| Una tarea corta sin perder el contexto | `Dialog` |
| Confirmar algo que no se puede deshacer | `AlertDialog` |
| Un panel lateral: filtros, un formulario largo | `Sheet` |
| Contenido interactivo anclado a un control | `Popover` |
| Una línea de texto que aclara un control | `Tooltip` |
| Confirmar que algo salió bien | `toast()` |
| Algo que sigue siendo verdad en la página | `Alert` |

**`AlertDialogAction` no cierra sola** — a propósito, para poder mostrar `loading` mientras corre la acción. O controlás `open` y cerrás al terminar, o la envolvés: `<AlertDialogClose render={<AlertDialogAction variant="destructive" />}>Eliminar</AlertDialogClose>`. `AlertDialogCancel` sí cierra.

## Formularios

**El tamaño se elige una vez por formulario**, no por campo: `md` (40px) es el de una app.

**Select, Combobox o Autocomplete:**

| Situación | Componente |
|---|---|
| Hasta ~8 opciones fijas | `Select` |
| Muchas opciones, el valor tiene que ser una de ellas | `Combobox` |
| Muchas opciones, se acepta texto libre | `Autocomplete` |
| 2 a 5 opciones, todas visibles | `RadioGroup` |
| Una opción con efecto inmediato | `Switch` |
| Varias opciones no excluyentes | `Checkbox` |

## Fondos

| Token | Rol |
|---|---|
| `bg-background` | **La página.** `body` y la raíz del `AppShell`. |
| `bg-background-100` | **La superficie**: lo que flota sobre la página. |
| `bg-background-200` | **El fondo sutil / banda**: Sidebar, `thead`, `Card subtle`, `EmptyState`. |

La regla: si el elemento **es** la página, `bg-background`; si flota **sobre** ella, `bg-background-100`.

## Server Components

No llevan `"use client"` y se pueden usar desde un Server Component:

- Las variantes: `buttonVariants`, `badgeVariants`, `cardVariants`, `linkVariants`, `toggleVariants`, `sidebarItemVariants`, y las clases de `menu` e `input`.
- Los componentes sin estado: `Kbd`, `PageHeader`, `EmptyState`, `Stat` y `AppShellContent`.

Todo lo demás es `"use client"`, que es lo que corresponde: un `DropdownMenu` necesita estado.

## Tipografía

**No pises el peso de un `heading` con `font-semibold`.** Los `heading` llevan la corrección óptica del peso; para más presencia está el paso de arriba de la escala.

## Lo que no hace el paquete, y es a propósito

| No hace | Por qué |
|---|---|
| Guardar el estado de colapsado del sidebar | La app decide dónde vive. Con una cookie el server ya renderiza el ancho correcto y no hay salto. |
| Registrar atajos de teclado | `shortcut` y `DropdownMenuShortcut` solo muestran y anuncian. Escuchar la tecla es de la app. |
| Cerrar el `AlertDialogAction` | Para poder mostrar `loading`. |
| Crecer solo el `Textarea` | Necesita JS que mida; no vale el peso en el bundle de todos. |
| Una variable global de densidad | Reducir la altura de todos los controles a la vez rompe el tamaño mínimo de target en mobile. |
| Validar formularios | Es de la app. El paquete solo lee `aria-invalid`. |
