Skip to content

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

bash
pnpm add @headless-primitives/styles
bash
npm install @headless-primitives/styles
bash
yarn add @headless-primitives/styles
bash
bun add @headless-primitives/styles

Uso

Todo de una vez

css
@import "@headless-primitives/styles/index.css";

Selectivo por componente

Necesitas el tema base más el CSS del componente que quieras:

css
@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.

TokenValor (light)ContrasteUsado en
--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
Aa--hp-accent-foreground#ffffff5.93:1 ✓Texto sobre fondo de acento

Superficies

--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-border es para separadores visuales no interactivos (líneas, dividers). --hp-border-strong es 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 interactivos

Texto

--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-sm4pxCheckboxes, tags
--hp-radius6pxBotones, inputs
--hp-radius-md8pxCards, dialogs, toasts
--hp-radius-lg12pxModales, popovers
--hp-radius-full9999pxSwitch, avatar, pills

Sombras

--hp-shadow-smSutil — hover states
--hp-shadowBase — cards, botones elevados
--hp-shadow-mdMedia — popovers
--hp-shadow-lgFuerte — dialogs, toasts

Error y Backdrop

TokenValor (light)ContrasteUsado en
--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-width2pxGrosor del anillo de foco
TokenValorUsado en
--hp-z-index-backdrop1000hp-dialog-backdrop
--hp-z-index-overlay-content1100hp-dialog-content, hp-toast-container
--hp-z-index-popover1200hp-popover-content
--hp-z-index-tooltip1300hp-tooltip-content

Tokens en Dark Mode

En dark mode los tokens se ajustan automáticamente vía @media (prefers-color-scheme: dark).

TokenValor darkContraste
--hp-accent#38bdf8 (sky-400)8.96:1 vs bg ✓ / 6.22:1 vs surface ✓
--hp-accent-foreground#0c1a298.54:1 vs accent ✓
--hp-text#f8fafc17.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.

css
: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")

css
:root {
  --hp-radius-sm: 2px;
  --hp-radius: 3px;
  --hp-radius-md: 4px;
  --hp-radius-lg: 6px;
}

Anular tokens en un contexto específico

css
/* 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:

css
/* 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:

css
/* 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).

Lanzado bajo la MIT License.