concepto CSS vigente

Custom properties

Custom properties

Las custom properties (también llamadas variables CSS) son propiedades definidas por el autor con el prefijo --, como --color-principal, que almacenan valores reutilizables en el documento y se consumen con var(). A diferencia de las variables de preprocesadores como Sass, viven en el navegador en tiempo real, participan de la cascada y se heredan hacia los elementos hijos.

Las custom properties son propiedades CSS que el autor define y nombra con doble guion: --color-principal, --espaciado-base, --fuente-titulo. A primera vista parecen “variables” — y así se las llama coloquialmente — pero la diferencia con las variables de preprocesadores como Sass es fundamental: las custom properties no son una abstracción que desaparece al compilar. Existen en el navegador como propiedades CSS reales, en tiempo de ejecución, dentro del árbol del documento. Eso significa que participan de la cascada, se pueden redefinir por selector o por media query, y que JavaScript puede leerlas y modificarlas en vivo.

La segunda característica que las distingue es la herencia. Una custom property declarada en un elemento se hereda hacia todos sus descendientes, exactamente igual que color o font-size. Si declarás --color-acento: teal en :root, ese valor está disponible en cualquier elemento del documento. Si lo redeclarás en .tema-oscuro, todos los hijos de .tema-oscuro verán el nuevo valor. Esto hace posible el patrón de temas (light/dark, múltiples marcas) sin duplicar reglas ni usar JavaScript para cambiar clases: alcanza con redefinir las variables en el selector correcto.

Desde el punto de vista del modelo de CSS, una custom property es una declaración como cualquier otra: --nombre: valor. El valor puede ser cualquier cosa — un color, un número con unidades, una expresión entera, o incluso un valor sin unidad que se multiplica después con calc(). El valor se almacena textualmente y se evalúa al momento en que var() lo inyecta en una propiedad concreta. Esto significa que podés declarar --ratio: 1.5 y usarlo después como font-size: calc(var(--ratio) * 1rem).

:root --color: #0d9488 .boton background: var(--color) .titulo color: var(--color) .borde border-color: var(--color) cambiar --color en un lugar actualiza los tres elementos

Cómo funciona

Declaración: --nombre: valor

Una custom property se declara exactamente como cualquier propiedad CSS, pero su nombre empieza con dos guiones:

:root {
  --color-principal: #0d9488;
  --espaciado-base: 1rem;
  --fuente-display: "Inter", sans-serif;
  --radio-borde: 4px;
}

El doble guion es obligatorio y distingue mayúsculas de minúsculas: --Color y --color son dos propiedades distintas. El nombre puede contener letras, números, guiones y guiones bajos, pero no puede empezar con un tercer guion ni con un número.

El valor puede ser cualquier secuencia válida de tokens CSS. El navegador lo almacena textualmente sin evaluarlo hasta que se use con var(). Esto permite guardar expresiones parciales:

:root {
  --ratio: 1.5;           /* número sin unidad */
  --sombra: 0 2px 8px;    /* parte de un box-shadow */
}

.elemento {
  font-size: calc(var(--ratio) * 1rem);
  box-shadow: var(--sombra) rgba(0,0,0,.2);
}

Ámbito: :root global vs. selector local

El ámbito de una custom property es el elemento donde se declara y todos sus descendientes, gracias a la herencia. No hay un ámbito de “variable global” separado: la convención de usar :root para declarar variables globales funciona porque :root es el selector de mayor especificidad que apunta al elemento raíz del documento (<html>), y todos los elementos del DOM son descendientes suyos.

/* global: disponible en todo el documento */
:root {
  --color-texto: #1e293b;
}

/* local: solo disponible dentro de .tarjeta y sus hijos */
.tarjeta {
  --color-texto: #0f172a;
}

Si un elemento declara la misma custom property que :root, esa declaración local gana para ese elemento y sus descendientes — la cascada y la herencia funcionan igual que con cualquier propiedad. Este es el mecanismo detrás de los temas:

/* tema claro (por defecto) */
:root {
  --fondo: #ffffff;
  --texto: #1e293b;
}

/* tema oscuro: redefine las mismas variables */
.tema-oscuro {
  --fondo: #0f172a;
  --texto: #e2e8f0;
}

/* los componentes no cambian: leen la variable que corresponde */
body {
  background-color: var(--fondo);
  color: var(--texto);
}

Herencia y cascada — lo que las diferencia de las variables de Sass

Las custom properties se heredan (ver Herencia CSS). Esto es fundamentalmente distinto de las variables de Sass o LESS, que son una abreviación de texto en tiempo de compilación: una vez compilado el CSS, $color-principal desaparece y el valor hardcodeado queda en cada lugar donde se usó. Cambiar un tema en Sass requiere recompilar. Cambiar un tema con custom properties es cambiar un valor en el DOM en tiempo real — algo que se puede hacer con JavaScript, con un media query o con un selector de clase.

La cascada aplica a las custom properties de la misma forma que a cualquier otra propiedad: si dos reglas definen --color para el mismo elemento, gana la de mayor especificidad, y si tienen la misma especificidad, gana la que aparece después (ver Cascada).

Casos de uso típicos

Las custom properties brillan cuando el mismo valor aparece en múltiples lugares y necesitás cambiarlo de forma coordinada. Los tres patrones más comunes son:

Paleta de colores. En lugar de repetir #0d9488 en treinta lugares del CSS, lo guardás en --color-acento y lo consumís con var(). Si el color cambia, lo cambiás en un solo lugar.

Espaciado y tipografía basada en escala. Un sistema de diseño puede definir --espacio-1: 0.25rem, --espacio-2: 0.5rem, etc., y los componentes los consumen sin hardcodear valores. Ver Unidades CSS.

Theming dinámico. Redefinir variables en .tema-oscuro o en @media (prefers-color-scheme: dark) cambia toda la interfaz sin duplicar reglas. Ver color y font-family.

/* paleta centralizada */
:root {
  --color-primario: #1d4ed8;
  --color-secundario: #0d9488;
  --color-texto: #1e293b;
  --espacio-sm: 0.5rem;
  --espacio-md: 1rem;
  --espacio-lg: 2rem;
  --fuente-base: "Inter", system-ui, sans-serif;
  --radio: 6px;
}

.boton {
  background-color: var(--color-primario);
  color: #fff;
  padding: var(--espacio-sm) var(--espacio-md);
  border-radius: var(--radio);
  font-family: var(--fuente-base);
}

.tarjeta {
  border: 1px solid var(--color-secundario);
  padding: var(--espacio-md);
  border-radius: var(--radio);
  color: var(--color-texto);
}
<!-- el HTML no cambia: lee las variables del CSS -->
<button class="boton">Guardar cambios</button>
<div class="tarjeta">Todos los valores (color, espaciado, radio) vienen de variables.</div>

Notas, gotchas e interacción

  • --Color y --color son propiedades distintas. Las custom properties distinguen mayúsculas. Si declarás --ColorFondo y lo consumís como var(--colorfondo), no resolverá. Usá siempre la misma capitalización; la convención más común es todo minúsculas con guiones (--color-fondo).
  • El fallback de var() sólo actúa si la propiedad no está definida, no si su valor es inválido. Esto se cubre en detalle en la entrada de var(). El error más frecuente es pensar que el fallback protege contra valores erróneos — no lo hace.
  • Las custom properties se heredan — incluso las que no querés que se hereden. Si declarás --sombra-especial en un componente para uso interno, sus hijos también la verán. No hay forma de marcarla como “no heredar” (a diferencia de las propiedades CSS normales, que pueden tener initial como valor heredado).
  • var() sólo va en el valor de una declaración, no en el nombre de la propiedad. var(--prop): 10px no es CSS válido. La función inyecta un valor, no un nombre de propiedad.
  • Son compatibles con calc(): font-size: calc(var(--ratio) * 1rem) es un patrón muy usado para escalas tipográficas. Ver calc().
  • JavaScript puede leerlas y modificarlas: getComputedStyle(el).getPropertyValue('--color') y el.style.setProperty('--color', '#f00'). Esto las hace ideales para animaciones o temas controlados desde JS.
  • @property (registro tipado): CSS moderno permite registrar custom properties con un tipo, valor inicial y si se heredan o no, usando @property. Es una funcionalidad avanzada fuera del alcance de esta entrada.

Vigente / legacy

Las custom properties están vigentes y con soporte Baseline widely. IE11 no las soporta — si tu proyecto requiere IE11, necesitás un polyfill o hardcodear los valores como fallback en el CSS previo a cualquier var(). En la práctica, IE11 dejó de tener soporte extendido de Microsoft en 2022 y su cuota de uso es marginal. Todo navegador moderno (Chrome, Firefox, Safari, Edge) las soporta desde hace varios años.