Skip to content

Variables CSS ​

Imagina que el cliente dice: "Me gustaría probar el azul un poco más oscuro". Si escribiste #2563eb en 47 lugares, tienes un problema. Con variables CSS (custom properties) lo cambias en una sola línea. En esta lección aprenderás a crearlas, su alcance, cómo construir un sistema de diseño (design tokens) y un tema claro/oscuro completo.

Concepto ​

Una variable CSS es un valor con nombre que defines una vez y reutilizas donde quieras.

Analogía

Es como el contacto "Mamá" en tu teléfono: si cambia de número, lo actualizas en un solo lugar y todas tus llamadas siguen funcionando. No necesitas memorizar el número.

Sintaxis ​

css
/* 1. Definir: empieza con dos guiones */
:root {
  --color-primario: #2563eb;
  --espacio: 1rem;
  --radio: 12px;
  --fuente-base: "Inter", system-ui, sans-serif;
}

/* 2. Usar con var() */
.boton {
  background: var(--color-primario);
  padding: var(--espacio) calc(var(--espacio) * 2);
  border-radius: var(--radio);
  font-family: var(--fuente-base);
}
  • :root es el elemento raíz (html): las variables definidas ahí están disponibles en todo el documento.
  • Los nombres distinguen mayúsculas: --Color y --color son distintas.
  • Pueden guardar cualquier valor: colores, medidas, fuentes, sombras completas, incluso fragmentos (--sombra: 0 4px 12px rgb(0 0 0 / .1)).

Valor de respaldo (fallback) ​

css
.alerta {
  color: var(--color-alerta, #dc2626);   /* si --color-alerta no existe, usa rojo */
  padding: var(--alerta-padding, var(--espacio, 1rem));  /* respaldos anidados */
}

Alcance (scope) y herencia ​

Las variables se heredan y siguen la cascada. Puedes redefinirlas en cualquier selector y afectarán sólo a ese elemento y sus descendientes:

css
:root {
  --color-boton: #2563eb;
}

.boton {
  background: var(--color-boton);
}

/* Variante: redefine la variable, no la propiedad */
.boton--peligro {
  --color-boton: #dc2626;
}

/* Todos los botones dentro del footer serán grises */
.footer {
  --color-boton: #475569;
}

Este patrón es muy poderoso para variantes de componentes: el componente define qué propiedad usa la variable y las variantes sólo cambian el valor.

Variables como "API" de un componente

css
.tarjeta {
  --tarjeta-padding: 1.5rem;
  --tarjeta-fondo: #fff;
  padding: var(--tarjeta-padding);
  background: var(--tarjeta-fondo);
}

.tarjeta--compacta { --tarjeta-padding: 0.75rem; }
.tarjeta--destacada { --tarjeta-fondo: #fef3c7; }

Incluso puedes personalizar desde el HTML: style="--tarjeta-fondo: #e0f2fe".

Variables + calc() ​

css
:root {
  --espacio: 8px;
}

.seccion  { padding: calc(var(--espacio) * 8); }   /* 64px */
.tarjeta  { gap: calc(var(--espacio) * 2); }       /* 16px */

/* Con números sin unidad */
:root { --columnas: 3; }
.grid {
  grid-template-columns: repeat(var(--columnas), 1fr);
}
@media (max-width: 600px) {
  :root { --columnas: 1; }   /* cambiar la variable cambia el layout */
}

Error común

--tamano: 20; y luego width: var(--tamano)px; no funciona: las variables no se concatenan con texto. Usa calc(var(--tamano) * 1px) o guarda la unidad en la variable: --tamano: 20px.

Design tokens: un sistema completo ​

Los equipos profesionales definen todos los valores del diseño como variables ("tokens"). Así el sitio es consistente y los cambios son triviales:

css
:root {
  /* Colores de marca */
  --color-primario: hsl(262 83% 58%);
  --color-primario-hover: hsl(262 83% 48%);
  --color-secundario: hsl(174 72% 40%);

  /* Colores semánticos (por función, no por apariencia) */
  --color-fondo: #ffffff;
  --color-superficie: #f8fafc;
  --color-texto: #0f172a;
  --color-texto-suave: #64748b;
  --color-borde: #e2e8f0;
  --color-exito: #16a34a;
  --color-error: #dc2626;

  /* Tipografía */
  --fuente-texto: "Inter", system-ui, sans-serif;
  --fuente-titulos: "Playfair Display", Georgia, serif;
  --texto-sm: 0.875rem;
  --texto-base: 1rem;
  --texto-lg: 1.25rem;
  --texto-xl: clamp(1.5rem, 1rem + 2vw, 2rem);
  --texto-2xl: clamp(2rem, 1.2rem + 4vw, 3.5rem);

  /* Espaciado (escala de 4/8px) */
  --espacio-1: 0.25rem;
  --espacio-2: 0.5rem;
  --espacio-3: 1rem;
  --espacio-4: 1.5rem;
  --espacio-5: 2rem;
  --espacio-6: 3rem;
  --espacio-7: 5rem;

  /* Bordes y sombras */
  --radio-sm: 6px;
  --radio: 12px;
  --radio-full: 999px;
  --sombra-sm: 0 1px 2px rgb(0 0 0 / 0.06);
  --sombra: 0 4px 16px rgb(0 0 0 / 0.08);
  --sombra-lg: 0 16px 40px rgb(0 0 0 / 0.12);

  /* Movimiento */
  --transicion: 200ms ease;

  /* Layout */
  --ancho-max: 1200px;
}

Buena práctica: nombres semánticos

Nombra por función (--color-texto, --color-error) y no por apariencia (--gris-oscuro, --rojo). Si mañana el tema oscuro usa texto claro, --color-texto sigue teniendo sentido; --gris-oscuro no.

Tema claro y oscuro ​

Con tokens semánticos, un tema oscuro consiste en redefinir los valores, sin tocar ningún componente:

Tema claro/oscuro con variables

1. Respetar la preferencia del sistema ​

css
:root {
  --color-fondo: #ffffff;
  --color-texto: #0f172a;
  --color-superficie: #f8fafc;
  --color-borde: #e2e8f0;
  color-scheme: light dark;      /* formularios y scrollbars nativos se adaptan */
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-fondo: #0f172a;
    --color-texto: #e2e8f0;
    --color-superficie: #1e293b;
    --color-borde: #334155;
  }
}

body {
  background: var(--color-fondo);
  color: var(--color-texto);
}

2. Con un botón para elegir (y recordarlo) ​

html
<button id="tema" aria-label="Cambiar tema">🌙</button>
css
/* Tema oscuro activado manualmente */
[data-theme="oscuro"] {
  --color-fondo: #0f172a;
  --color-texto: #e2e8f0;
  --color-superficie: #1e293b;
  --color-borde: #334155;
}
js
const html = document.documentElement;
const boton = document.getElementById('tema');

// 1. Al cargar: preferencia guardada o, si no hay, la del sistema
const guardado = localStorage.getItem('tema');
const sistemaOscuro = window.matchMedia('(prefers-color-scheme: dark)').matches;
html.dataset.theme = guardado ?? (sistemaOscuro ? 'oscuro' : 'claro');

// 2. Al hacer clic: alterna y guarda
boton.addEventListener('click', () => {
  const nuevo = html.dataset.theme === 'oscuro' ? 'claro' : 'oscuro';
  html.dataset.theme = nuevo;
  localStorage.setItem('tema', nuevo);
});

Parpadeo al cargar

Si el script se ejecuta al final de la página, verás un destello del tema claro antes de cambiar al oscuro. Coloca la parte que lee localStorage en un script pequeño dentro del head, antes del CSS, para aplicar el tema antes de pintar.

light-dark() ​

Los navegadores modernos permiten definir ambos valores en una línea (requiere color-scheme):

css
:root {
  color-scheme: light dark;
  --color-fondo: light-dark(#ffffff, #0f172a);
  --color-texto: light-dark(#0f172a, #e2e8f0);
}

Variables y JavaScript ​

js
const raiz = document.documentElement;

// Leer
const primario = getComputedStyle(raiz).getPropertyValue('--color-primario').trim();

// Escribir
raiz.style.setProperty('--color-primario', '#16a34a');

// Ejemplo: seguir el mouse con un brillo
document.addEventListener('pointermove', (e) => {
  raiz.style.setProperty('--mouse-x', e.clientX + 'px');
  raiz.style.setProperty('--mouse-y', e.clientY + 'px');
});
css
.brillo {
  background: radial-gradient(circle at var(--mouse-x) var(--mouse-y), rgb(124 58 237 / 0.25), transparent 40%);
}

Es la forma limpia de conectar JS y CSS: JavaScript sólo cambia números y el CSS decide cómo se ven.

Casos de uso reales ​

  • Marca del cliente: todo el sitio se basa en --color-primario. Si cambian el logo, actualizas un valor.
  • Multi-marca o franquicias: el mismo sitio con distintos colores según la sucursal, cambiando unas variables en una clase en el body.
  • Personalización por el usuario: tamaño de fuente o tema elegidos en un panel de configuración.
  • Componentes configurables en un sistema de diseño.

Ejercicios ​

  1. Define en :root un color primario, un radio y un espaciado, y úsalos en un botón.
  2. Crea .boton--secundario y .boton--peligro cambiando sólo una variable.
  3. Haz que una grilla tenga 1 columna en móvil y 3 en escritorio cambiando una variable --columnas en una media query.
  4. Implementa un tema oscuro automático con prefers-color-scheme para fondo, texto y bordes.
Ver solución
css
/* 1 y 2 */
:root {
  --color-primario: #2563eb;
  --radio: 10px;
  --espacio: 1rem;
}

.boton {
  --boton-color: var(--color-primario);
  background: var(--boton-color);
  color: #fff;
  border: 0;
  border-radius: var(--radio);
  padding: calc(var(--espacio) * 0.75) calc(var(--espacio) * 1.5);
}
.boton--secundario { --boton-color: #475569; }
.boton--peligro    { --boton-color: #dc2626; }

/* 3 */
.grid {
  --columnas: 1;
  display: grid;
  grid-template-columns: repeat(var(--columnas), 1fr);
  gap: var(--espacio);
}
@media (min-width: 900px) {
  .grid { --columnas: 3; }
}

/* 4 */
:root {
  --fondo: #ffffff;
  --texto: #111827;
  --borde: #e5e7eb;
  color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
  :root {
    --fondo: #111827;
    --texto: #f3f4f6;
    --borde: #374151;
  }
}
body { background: var(--fondo); color: var(--texto); }
.tarjeta { border: 1px solid var(--borde); }

Resumen ​

  • Define con --nombre: valor y usa con var(--nombre, respaldo).
  • Se heredan y respetan la cascada: redefínelas en un selector para crear variantes.
  • No se concatenan con unidades: usa calc(var(--n) * 1px).
  • Los design tokens con nombres semánticos hacen el sitio consistente y fácil de cambiar.
  • Tema oscuro = redefinir tokens con prefers-color-scheme o un atributo data-theme.
  • JavaScript puede leer y escribir variables con setProperty.

Siguiente: Pseudo-clases y pseudo-elementos.

Academia Web — aprende, copia y construye.