Theming
@headless-primitives/styles es el paquete opcional de estilos base. Está construido enteramente sobre CSS custom properties, lo que te permite personalizar toda la apariencia visual cambiando únicamente variables en tu propio CSS, sin tocar el paquete.
Todos los tokens de color por defecto cumplen WCAG 2.1 AA:
- Texto normal → 4.5:1 mínimo (criterio 1.4.3)
- Componentes UI (bordes de checkbox, radio…) → 3:1 mínimo (criterio 1.4.11)
- Estados
disabled→ exentos por la excepción de 1.4.3
Instalación
pnpm add @headless-primitives/stylesnpm install @headless-primitives/stylesyarn add @headless-primitives/stylesbun add @headless-primitives/stylesUso
Todo de una vez
@import "@headless-primitives/styles/index.css";Selectivo por componente
Necesitas el tema base más el CSS del componente que quieras:
@import "@headless-primitives/styles/theme.css"; /* tokens — siempre requerido */
@import "@headless-primitives/styles/button.css";
@import "@headless-primitives/styles/tabs.css";Tokens disponibles
Todas las propiedades se definen bajo :root y pueden ser sobrescritas en cualquier selector.
Acento
El color de acento controla el estado activo/seleccionado de switches, checkboxes, radios, tabs, etc.
--hp-accent#0369a15.93:1 ✓Fondo activo + texto seleccionado--hp-accent-hover#0759857.56:1 ✓Estado hover del acento--hp-accent-active#0c4a6e10.4:1 ✓Estado active/pressed--hp-accent-foreground#ffffff5.93:1 ✓Texto sobre fondo de acentoSuperficies
--hp-bg#ffffffbaseFondo base de la aplicación--hp-bg-subtle#f8fafcbaseFondo sutil para zonas en reposo--hp-bg-muted#f1f5f9baseFondo hover de botones y triggers--hp-surface#ffffffbaseFondo de componentes (cards, dialogs)Bordes
--hp-borderes para separadores visuales no interactivos (líneas, dividers).--hp-border-stronges para bordes que definen un componente interactivo (checkbox, radio, input) y debe superar el ratio de no-texto 3:1.
--hp-border#e2e8f0dividerBorde de separación visual (non-interactive)--hp-border-strong#64748b4.76:1 ✓Borde de componentes interactivosTexto
--hp-text#0f172a18.1:1 ✓Texto primario--hp-text-secondary#64748b4.76:1 ✓Texto secundario y labels--hp-text-disabled#94a3b8exentoTexto disabled (WCAG 1.4.3 excepción)Radio de borde
--hp-radius-sm4px—Checkboxes, tags--hp-radius6px—Botones, inputs--hp-radius-md8px—Cards, dialogs, toasts--hp-radius-lg12px—Modales, popovers--hp-radius-full9999px—Switch, avatar, pillsSombras
--hp-shadow-smSutil — hover states--hp-shadowBase — cards, botones elevados--hp-shadow-mdMedia — popovers--hp-shadow-lgFuerte — dialogs, toastsError y Backdrop
--hp-text-error#dc26265.93:1 ✓Mensajes de error en hp-field-error--hp-backdrop-bgrgb(0 0 0 / 0.5)—Fondo del backdrop en dialogs (dark: 0.65)Foco y Z-indexes (de base.css)
--hp-focus-outline-color#2563eb5.12:1 ✓Color del anillo de foco--hp-focus-outline-width2px—Grosor del anillo de foco| Token | Valor | Usado en |
|---|---|---|
--hp-z-index-backdrop | 1000 | hp-dialog-backdrop |
--hp-z-index-overlay-content | 1100 | hp-dialog-content, hp-toast-container |
--hp-z-index-popover | 1200 | hp-popover-content |
--hp-z-index-tooltip | 1300 | hp-tooltip-content |
Tokens en Dark Mode
En dark mode los tokens se ajustan automáticamente vía @media (prefers-color-scheme: dark).
| Token | Valor dark | Contraste |
|---|---|---|
--hp-accent | #38bdf8 (sky-400) | 8.96:1 vs bg ✓ / 6.22:1 vs surface ✓ |
--hp-accent-foreground | #0c1a29 | 8.54:1 vs accent ✓ |
--hp-text | #f8fafc | 17.6:1 vs bg ✓ |
--hp-text-secondary | #94a3b8 (slate-400) | 7.63:1 vs bg ✓ / 5.31:1 vs surface ✓ |
--hp-border-strong | #64748b (slate-500) | 4.60:1 vs bg ✓ / 3.20:1 vs surface ✓ |
Personalizar el tema
Cambiar el color de acento
Al cambiar el acento debes asegurarte de mantener el ratio 4.5:1 contra los fondos donde aparezca como texto, y que el accent-foreground tenga 4.5:1 contra el nuevo acento.
:root {
--hp-accent: #7c3aed; /* violet-700 → 5.08:1 vs blanco ✓ */
--hp-accent-hover: #6d28d9; /* violet-800 */
--hp-accent-active: #5b21b6; /* violet-900 */
--hp-accent-foreground: #ffffff; /* 5.08:1 ✓ */
--hp-focus-outline-color: #7c3aed;
}Esquinas más rectas (estilo "enterprise")
:root {
--hp-radius-sm: 2px;
--hp-radius: 3px;
--hp-radius-md: 4px;
--hp-radius-lg: 6px;
}Anular tokens en un contexto específico
/* Solo dentro de un formulario */
.my-form {
--hp-accent: #d97706; /* amber-600 → 4.54:1 vs blanco ✓ */
--hp-border-strong: #92400e; /* amber-800 */
}Dark Mode
El paquete incluye un bloque @media (prefers-color-scheme: dark) que ajusta automáticamente todos los tokens de color. No necesitas hacer nada adicional.
Si prefieres controlar el modo oscuro manualmente con una clase:
/* Desactiva el dark mode automático sobreescribiendo los tokens */
@media (prefers-color-scheme: dark) {
:root {
--hp-bg: #ffffff;
--hp-surface: #ffffff;
--hp-text: #0f172a;
/* ... resto de tokens light */
}
}
/* Aplica dark mode con tu propia clase */
.dark {
--hp-bg: #0f172a;
--hp-surface: #1e293b;
--hp-text: #f8fafc;
--hp-border: #334155;
--hp-border-strong: #64748b;
--hp-accent: #38bdf8;
--hp-accent-foreground: #0c1a29;
--hp-text-secondary: #94a3b8;
}Especificidad mínima
Todos los selectores del paquete usan un único selector de elemento (hp-button, hp-switch, etc.), con especificidad (0,0,1). Cualquier clase CSS de tu proyecto tiene precedencia automática:
/* Esto SIEMPRE gana sobre los estilos base */
.my-custom-button {
background-color: tomato;
border-radius: 0;
}
/* O redefinir tokens en tu selector raíz */
:root {
--hp-accent: #e11d48; /* rose-600 → 4.65:1 vs blanco ✓ */
}El paquete no usa !important en ninguna regla (excepto .hp-visually-hidden en base.css por accesibilidad).
