Variables CSS (custom properties)
En esta lección
En una web real, el mismo azul aparece en decenas de reglas: botones, enlaces, títulos, bordes. Si un día cambia el color de la marca, tienes que buscarlo y sustituirlo en todas partes. Las variables CSS (su nombre oficial es custom properties, propiedades personalizadas) resuelven esto: guardas el valor una vez y lo reutilizas donde quieras. Pero son mucho más que un “buscar y reemplazar”: heredan, se pueden redefinir en cada parte de la página y se pueden cambiar en vivo con JavaScript. Con ellas harás un modo oscuro en pocas líneas.
Declarar y usar una variable
Una variable se declara como cualquier propiedad, pero su nombre empieza por dos guiones. Se usa con la función var():
:root {
--color-principal: #2d6cdf;
--color-texto: #1b2d34;
--radio: 8px;
}
h1 {
color: var(--color-principal);
}
.boton {
background: var(--color-principal);
border-radius: var(--radio);
}
Detalles importantes:
- El nombre distingue mayúsculas y minúsculas:
--Colory--colorson variables distintas. - Puede guardar cualquier valor: un color, una medida, una lista de fuentes, una sombra entera.
:rootes el elemento raíz del documento (<html>). Declararlas ahí las hace disponibles en toda la página.
:root {
--fuente-titulos: Georgia, "Times New Roman", serif;
--sombra-suave: 0 1px 2px rgb(0 0 0 / 0.08), 0 8px 24px rgb(0 0 0 / 0.08);
--transicion: 0.2s ease-out;
}
.tarjeta {
box-shadow: var(--sombra-suave);
transition: box-shadow var(--transicion);
}
Valor por defecto
var() acepta un segundo argumento: el valor que se usa si la variable no existe:
.aviso {
color: var(--color-aviso, #e76f51); /* si no hay --color-aviso, naranja */
}
.boton {
/* Se pueden encadenar: primero --color, si no --principal, si no azul */
background: var(--color, var(--principal, blue));
}
Todo lo que va después de la primera coma es el valor por defecto, incluidas otras comas: var(--fuente, Arial, sans-serif) usa Arial, sans-serif si falta --fuente.
Ámbito y herencia
Aquí está la gran diferencia con las variables de otros lenguajes. Una variable CSS se hereda como color o font-family: la declaras en un elemento y la ven él y todos sus descendientes.
:root {
--principal: #2d6cdf; /* global: toda la página */
}
.zona-ofertas {
--principal: #e76f51; /* dentro de esta zona, el principal es naranja */
}
.boton {
background: var(--principal);
}
<button class="boton">Azul</button>
<section class="zona-ofertas">
<button class="boton">Naranja</button> <!-- hereda el valor de la sección -->
</section>
La regla del botón es la misma; lo que cambia es el valor que hereda según dónde esté. Esto permite hacer variantes de un componente sin repetir CSS.
Variables como “parámetros” de un componente
.boton {
--color: var(--principal); /* valor por defecto del componente */
padding: 8px 16px;
background: var(--color);
border: 2px solid var(--color);
color: white;
}
.boton.peligro { --color: #d62839; }
.boton.exito { --color: #2a9d5c; }
Cada variante solo cambia una variable, y el fondo y el borde se actualizan solos. Sin variables tendrías que repetir las dos propiedades en cada variante.
Consejo: declara en
:rootlos valores globales (colores de marca, espaciados, fuentes) y en cada componente sus variables locales. Así se lee enseguida qué es global y qué no.
Tema claro y oscuro
Con variables, un modo oscuro consiste en redefinir unas pocas variables. El resto del CSS no cambia.
Siguiendo la preferencia del sistema
La media query prefers-color-scheme detecta si el usuario tiene el sistema operativo en modo oscuro:
:root {
--fondo: #ffffff;
--texto: #1b2d34;
--tarjeta: #f3f5f9;
--principal: #2d6cdf;
}
@media (prefers-color-scheme: dark) {
:root {
--fondo: #12181f;
--texto: #e3e8ef;
--tarjeta: #1d2630;
--principal: #6f9cf0; /* un azul más claro para que contraste sobre oscuro */
}
}
body {
background: var(--fondo);
color: var(--texto);
}
.tarjeta {
background: var(--tarjeta);
}
Con una clase que activa el usuario
Muchas webs ofrecen además un botón para elegir el tema. La forma habitual es poner una clase en <html>:
:root.oscuro {
--fondo: #12181f;
--texto: #e3e8ef;
--tarjeta: #1d2630;
--principal: #6f9cf0;
}
const boton = document.querySelector('#cambiar-tema');
boton.addEventListener('click', () => {
document.documentElement.classList.toggle('oscuro');
});
Un paso más, que se suele añadir: guardar la elección en localStorage para que se recuerde en la siguiente visita. Lo tienes en Web Storage.
Consejo: añade
color-scheme: light dark;en:root. Así el navegador también adapta al tema los controles de formulario y las barras de scroll.
Cambiar variables desde JavaScript
Las variables CSS existen mientras la página funciona (no solo al escribir el código), así que JavaScript puede leerlas y cambiarlas. Todos los elementos que las usan se actualizan al instante.
const raiz = document.documentElement; // el <html>, donde está :root
// Cambiar (o crear) una variable
raiz.style.setProperty('--principal', '#8e44ad');
// Leer el valor que tiene en ese momento
const valor = getComputedStyle(raiz).getPropertyValue('--principal');
console.log(valor); // #8e44ad
// Quitar la que pusimos desde JavaScript
raiz.style.removeProperty('--principal');
Un ejemplo útil: seguir la posición del ratón para un efecto de luz en una tarjeta. JavaScript solo pasa los números; el dibujo lo hace el CSS:
const tarjeta = document.querySelector('.tarjeta');
tarjeta.addEventListener('pointermove', (e) => {
const caja = tarjeta.getBoundingClientRect();
tarjeta.style.setProperty('--x', `${e.clientX - caja.left}px`);
tarjeta.style.setProperty('--y', `${e.clientY - caja.top}px`);
});
.tarjeta {
background: radial-gradient(circle at var(--x, 50%) var(--y, 50%), #ffffff40, transparent 40%), #2d6cdf;
}
Así el reparto de tareas queda limpio: JavaScript calcula, CSS pinta. Para saber más de eventos, mira eventos en JavaScript.
Variables y calc()
Las variables se combinan con calc() para derivar unos valores de otros:
:root {
--espacio: 8px;
}
.tarjeta {
padding: calc(var(--espacio) * 2); /* 16px */
margin-bottom: calc(var(--espacio) * 3); /* 24px */
gap: var(--espacio); /* 8px */
}
Si cambias --espacio a 10px, todos los espaciados crecen en proporción. Es la base de un sistema de diseño coherente.
Números sin unidad
Una variable puede guardar un número puro y convertirlo en medida al usarlo. Se multiplica por 1px, 1rem, 1deg…:
.barra {
--progreso: 65; /* un número de 0 a 100 */
width: calc(var(--progreso) * 1%); /* 65% */
}
.icono {
--giro: 45;
transform: rotate(calc(var(--giro) * 1deg)); /* 45deg */
}
También puedes guardar partes de un color. Con hsl es muy cómodo crear toda una paleta a partir de un tono:
:root {
--tono: 220;
--principal: hsl(var(--tono) 70% 50%);
--principal-claro: hsl(var(--tono) 70% 92%);
--principal-oscuro: hsl(var(--tono) 70% 30%);
}
Cambiando solo --tono a 150, toda la paleta pasa de azul a verde. Tienes más sobre hsl en colores.
Cuidado:
var()no pega texto.--tamanio: 20;seguido dewidth: var(--tamanio)px;no da20px: el navegador ve20 pxseparados y la declaración falla. Usacalc(var(--tamanio) * 1px).
Variables CSS frente a variables de Sass
Si has visto Sass (un preprocesador que añade funciones al CSS), conocerás sus variables con $:
$principal: #2d6cdf;
.boton { background: $principal; }
Se parecen, pero funcionan de forma muy distinta:
Variables de Sass ($var) | Variables CSS (--var) | |
|---|---|---|
| Cuándo existen | Solo al compilar: en el CSS final ya no están | En el navegador, mientras la página funciona |
| Se heredan por el HTML | No | Sí, como cualquier propiedad heredable |
| Cambiar por zonas de la página | No | Sí, redefiniéndolas en un selector |
| Cambian con media queries | No (se sustituyen antes) | Sí |
| Cambiar con JavaScript | No | Sí, con setProperty |
| Necesitan herramientas | Sí, un compilador | No, CSS puro |
Hoy, para colores, espaciados y temas, las variables CSS son la opción natural. Sass sigue siendo útil para otras cosas (bucles, mixins), y los dos tipos pueden convivir.
@property: variables con tipo
Por defecto, para el navegador una variable es solo un trozo de texto: no sabe si es un color o una medida. Por eso no puede animarla; si pones una transición sobre --angulo, salta de golpe de un valor a otro.
@property registra la variable con un tipo, un valor inicial y si se hereda:
@property --angulo {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.borde-animado {
background: conic-gradient(from var(--angulo), #2d6cdf, #e76f51, #2d6cdf);
animation: girar 3s linear infinite;
}
@keyframes girar {
to { --angulo: 360deg; }
}
Como el navegador ya sabe que --angulo es un ángulo, puede calcular los valores intermedios y el degradado gira suavemente. Los tipos más usados en syntax son <length>, <color>, <number>, <percentage> y <angle>. Las animaciones las verás en transiciones y animaciones.
Errores frecuentes
- Olvidar los dos guiones.
var(principal)ovar(-principal)no funcionan. Siempre--principal. - Olvidar
var().color: --principal;no es válido. Hay que escribircolor: var(--principal);. - Pegar unidades detrás.
var(--n)pxno funciona. Usacalc(var(--n) * 1px). - Esperar que una variable inválida use la regla anterior. Si
--colorvale20pxy escribescolor: var(--color), la declaración no vuelve al valor de otra regla: el color pasa a ser el heredado (o el inicial). Por eso conviene dar buenos valores por defecto. - Declarar la variable en un elemento hermano. Una variable solo la ven el elemento y sus descendientes. Si la declaras en
.menu, el<main>de al lado no la tiene. - Confundir mayúsculas.
--Principaly--principalson distintas. - Crear variables para todo. Una variable que se usa una sola vez no aporta nada. Úsalas para valores que se repiten o que cambian (temas, variantes).
Resumen
| Concepto | Para qué sirve | Ejemplo |
|---|---|---|
--nombre: valor | Declarar una variable | --principal: #2d6cdf; |
var(--nombre) | Usarla | color: var(--principal); |
var(--a, valor) | Valor por defecto | var(--color, blue) |
:root | Variables globales | :root { --radio: 8px; } |
| Herencia | Redefinir por zonas | .ofertas { --principal: orange; } |
prefers-color-scheme | Tema según el sistema | @media (prefers-color-scheme: dark) |
Clase en <html> | Tema elegido por el usuario | :root.oscuro { ... } |
setProperty() | Cambiarla con JavaScript | raiz.style.setProperty('--x', '10px') |
calc() | Derivar valores | calc(var(--espacio) * 2) |
@property | Variable con tipo (animable) | syntax: "<angle>" |
Con las variables tu CSS se vuelve mucho más fácil de mantener. Ahora ya puedes darle movimiento con transiciones y animaciones.
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.