¿Qué son las variables de CSS?
Las variables de CSS, llamadas correctamente custom properties, son valores que defines una sola vez y haces referencia a ellos en cualquier lugar. Se parecen a las propiedades ordinarias pero comienzan con --, y se leen mediante la función var().
/* tokens.css */
:root {
--color-brand: #2563eb;
--radius: 0.5rem;
--space-4: 1rem;
}
.button {
background: var(--color-brand);
border-radius: var(--radius);
padding: var(--space-4) calc(var(--space-4) * 1.5);
}
La diferencia clave con una variable de preprocesador es que las custom properties son parte de la cascada del navegador. Se heredan, pueden tener un alcance (scope) definido y pueden cambiar en tiempo de ejecución. Esto las convierte en la capa natural para implementar temas (theming) y design tokens.
Declaración y uso
Declara una propiedad personalizada con un nombre que comience con dos guiones y cualquier valor válido.
/* declare.css */
:root {
--gap: 1.5rem;
--shadow: 0 1px 2px rgb(0 0 0 / 0.08);
--font-sans: "Inter", system-ui, sans-serif;
}
.card {
gap: var(--gap);
box-shadow: var(--shadow);
font-family: var(--font-sans);
}
Los valores pueden ser casi cualquier cosa: colores, longitudes, sombras, pilas de fuentes, degradados o incluso fragmentos de un valor. Debido a que se sustituyen antes de que la propiedad sea analizada, pueden combinarse con calc().
La cascada y la herencia
Las propiedades personalizadas siguen las reglas normales de CSS. Una declaración en un elemento es heredada por sus descendientes, y prevalece la declaración más cercana.
/* scope.css */
:root {
--accent: #2563eb;
}
.sidebar {
--accent: #f97316;
}
.sidebar a {
color: var(--accent); /* orange inside the sidebar */
}
Esto es lo que hace que el alcance (scoping) sea tan potente: puedes cambiar el tema de un componente o de una sección sin tocar los tokens globales. También es la razón por la cual definir tokens en :root hace que estén disponibles en todas partes.
Fallbacks
var() acepta un fallback como segundo argumento, el cual se utiliza cuando la propiedad no existe o no es válida.
/* fallback.css */
.button {
color: var(--button-color, #ffffff);
background: var(--button-bg, var(--color-brand, #2563eb));
}
Los fallbacks son útiles para componentes reutilizables que deben funcionar incluso cuando los tokens no están definidos, y pueden anidarse. No obstante, utilízalos con moderación; si un token siempre debería existir, es mejor definirlo claramente en lugar de ocultar el problema.
Tematización
La tematización es el caso de uso estrella. Cambia un conjunto de variables y todo un subárbol se actualiza.
/* theme.css */
:root {
--bg: #ffffff;
--surface: #f8fafc;
--text: #0f172a;
--border: #e2e8f0;
}
[data-theme="dark"] {
--bg: #0b0d0c;
--surface: #14161a;
--text: #e5e7eb;
--border: #26292e;
}
body {
background: var(--bg);
color: var(--text);
}
.card {
background: var(--surface);
border: 1px solid var(--border);
}
Activa un atributo data-theme en el elemento raíz y toda la página cambia de tema instantáneamente, sin reglas duplicadas. También puedes limitar un tema a un componente, permitiendo que una tarjeta oscura esté dentro de una página clara.
Valores dinámicos con JavaScript
Debido a que las propiedades personalizadas residen en el DOM, puedes leerlas y escribirlas desde JavaScript.
// progress.js
const bar = document.querySelector(".progress");
function setProgress(percent) {
bar.style.setProperty("--progress", `${percent}%`);
}
setProgress(72);
/* progress.css */
.progress__fill {
width: var(--progress, 0%);
}
Así es como se suelen construir las barras de progreso, las interacciones de arrastre, los efectos de seguimiento del cursor y los selectores de tema. El valor permanece en CSS y JavaScript solo actualiza un número.
Propiedades tipadas con @property
Por defecto, las propiedades personalizadas son cadenas de texto sin tipo, por lo que el navegador no puede interpolarlas. La regla-at @property registra un tipo, un valor inicial y un comportamiento de herencia.
/* property.css */
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.spinner {
background: conic-gradient(from var(--angle), #2563eb, transparent);
animation: rotate 1s linear infinite;
}
@keyframes rotate {
to { --angle: 360deg; }
}
Con un tipo registrado, la propiedad se vuelve animable y el navegador valida el valor, lo que evita toda una categoría de errores.
Mejores prácticas
- Define tokens globales en
:rooty utilízalos en todas partes. - Nombra los tokens según su propósito (
--color-text) y no según su apariencia (--gray-900). - Sobrescribe variables para temas y variantes de componentes en lugar de duplicar reglas.
- Proporciona fallbacks para componentes reutilizables que puedan ejecutarse sin tokens.
- Actualiza las variables desde JavaScript en lugar de escribir estilos inline en todas partes.
- Registra las propiedades animables con
@property. - Mantén el conjunto de tokens pequeño y consistente.
Errores comunes
- Esperar que las propiedades personalizadas se comporten como variables de Sass en tiempo de compilación.
- Usar nombres ambiguos que dificulten el mantenimiento del conjunto de tokens.
- Definir cada valor como una variable, perdiendo así la legibilidad.
- Olvidar que los valores no tienen tipo a menos que se registren con
@property. - Sobrescribir tokens de forma global cuando solo debería cambiar un subárbol.
- Almacenar secretos o datos de usuario en variables CSS, ya que son visibles en el DOM.
Próximos pasos
Las propiedades personalizadas son el tejido conectivo de una hoja de estilos mantenible. Aplícalas al diseño con Diseño Responsivo, anima las propiedades tipadas con Animaciones y Transiciones y comprende la cascada que hay detrás de ellas en la guía de CSS. Para variables de tiempo de compilación que las complementen, consulta Sass.