Variables
Lección 22 de 37 · 3 min
Las variables de CSS le ponen nombre a un valor para usarlo en muchos lugares. Se declaran una vez, se heredan hacia abajo y se pueden pisar en cualquier parte del árbol.
Declarar y leer
Su nombre técnico es propiedad personalizada (custom property). Se declara como cualquier propiedad, pero el nombre empieza con dos guiones: --accent: oklch(0.52 0.2 290). Para leerla se usa la función var(): background: var(--accent).
- Distinguen mayúsculas
--accent ≠ --Accent--accenty--Accentson dos variables distintas. - Se heredan
inheritsSi la declarás en un elemento, todos sus descendientes la pueden leer. - Se leen tarde
var()CSS no revisa el valor al declararla. Recién lo interpreta cuando una propiedad la lee convar().
Mové los controles y mirá cómo el botón y la tarjeta cambian juntos: los dos leen las mismas variables de .tema. Después pisá --accent en la tarjeta y quitá --radius. Las marcas naranjas muestran el valor de cada variable donde se lee: el relleno de la tarjeta, su borde y la esquina del botón.
Club de lectura
Jueves 14 de noviembre, 19 h · Biblioteca Moreno
↖ radius 12px
padding: var(--space) = 16px
border-color: var(--accent) = turquesa
.tema {--accent: oklch(0.52 0.1 190);--radius: 12px;--space: 16px;}.boton {background: var(--accent);color: white;border-radius: var(--radius, 8px);padding: calc(var(--space) / 2) var(--space);}.tarjeta {border: 2px solid var(--accent);border-radius: var(--radius, 8px);padding: var(--space);}
<div class="[--accent:oklch(0.52_0.1_190)] [--radius:12px] [--space:16px]"><button class="bg-(--accent) text-white rounded-(--radius,8px) px-(--space) py-[calc(var(--space)/2)]">Anotarme</button><div class="border-2 border-(--accent) rounded-(--radius,8px) p-(--space)">Este mes: Los años, de Annie Ernaux</div></div>
Alcance y cascada
Una variable vive en el elemento que la declara y en sus descendientes. Sigue la cascada normal: especificidad, orden y herencia, igual que color. Por eso declararla en .tarjeta cambia el acento solo dentro de la tarjeta, y el botón sigue con el de .tema.
El segundo argumento de var() es el valor de respaldo (fallback). Con var(--radius, 8px), si --radius no está definida en ningún ancestro, se usan 8px. Es lo que pasa en la demo al quitarla.
.tema {--space: red; /* válido: no se revisa al declarar */}.tarjeta {padding: 24px;padding: var(--space); /* inválido al calcularse: queda en 0 */}
Ojo con ese caso: si la variable existe pero su valor no sirve para la propiedad, la declaración es inválida al calcular el valor (invalid at computed-value time). No se usa el respaldo ni el padding: 24px anterior; la propiedad se comporta como unset, y como padding no se hereda, vale 0.
Usos prácticos
- Tokens de diseño
:rootColores, radios y espacios con nombre en:root, para cambiarlos en un solo lugar. - Temas
prefers-color-schemeRedefinís los mismos tokens dentro de@media (prefers-color-scheme: dark)y todo lo que los lee cambia. Este sitio lo hace así. - Cálculos
calc(var(--space) * 2)Con--space: 16pxda 32px. Para sumar una unidad a un número usácalc(var(--n) * 1px);var(--n)pxno funciona.
:root {--bg: oklch(0.99 0 0);--text: oklch(0.2 0 0);}@media (prefers-color-scheme: dark) {:root {--bg: oklch(0.2 0 0);--text: oklch(0.95 0 0);}}body {background: var(--bg);color: var(--text);}
Una variable común no tiene tipo, así que el navegador no sabe interpolarla y una transición sobre ella salta de golpe. Con @property la registrás con un tipo (syntax), si se hereda (inherits) y un valor inicial (initial-value). Así se puede animar:
@property --angle {syntax: "<angle>";inherits: false;initial-value: 0deg;}.flecha {transform: rotate(var(--angle));transition: --angle 300ms;}.flecha:hover {--angle: 90deg;}
En Tailwind
En Tailwind v4 los valores del tema ya son variables: --spacing o --color-white viven en :root. Para declarar una propia en un elemento se usa una propiedad arbitraria entre corchetes, y para leerla, la clase con el nombre entre paréntesis.
| CSS | Tailwind |
|---|---|
| --space: 16px | [--space:16px] |
| background-color: var(--accent) | bg-(--accent) |
| border-color: var(--accent) | border-(--accent) |
| padding: var(--space) | p-(--space) |
| border-radius: var(--radius, 8px) | rounded-(--radius,8px) |
| --color-accent en @theme | bg-accent · border-accent |
<div class="[--accent:oklch(0.52_0.2_290)] [--space:16px]"><button class="bg-(--accent) text-white rounded-(--radius,8px)px-(--space) py-[calc(var(--space)/2)]">Guardar</button><div class="[--accent:oklch(0.52_0.2_10)]border-2 border-(--accent) p-(--space)">Tarjeta</div></div>
Variables
Lesson 22 of 37 · 3 min
CSS variables give a value a name so you can use it in many places. You declare them once, they inherit downward, and you can override them anywhere in the tree.
Declare and read
Their technical name is custom property. You declare one like any other property, but the name starts with two dashes: --accent: oklch(0.52 0.2 290). To read it, use the var() function: background: var(--accent).
- Case-sensitive
--accent ≠ --Accent--accentand--Accentare two different variables. - They inherit
inheritsDeclare one on an element and all its descendants can read it. - Read late
var()CSS doesn't check the value when you declare it. It only interprets it when a property reads it withvar().
Move the controls and watch the button and the card change together: both read the same variables from .theme. Then override --accent in the card and remove --radius. The orange marks print each variable's value where it is read: the card's padding, its border and the button's corner.
Book club
Thursday, November 14, 7 pm · Moreno Library
↖ radius 12px
padding: var(--space) = 16px
border-color: var(--accent) = teal
.theme {--accent: oklch(0.52 0.1 190);--radius: 12px;--space: 16px;}.button {background: var(--accent);color: white;border-radius: var(--radius, 8px);padding: calc(var(--space) / 2) var(--space);}.card {border: 2px solid var(--accent);border-radius: var(--radius, 8px);padding: var(--space);}
<div class="[--accent:oklch(0.52_0.1_190)] [--radius:12px] [--space:16px]"><button class="bg-(--accent) text-white rounded-(--radius,8px) px-(--space) py-[calc(var(--space)/2)]">Sign me up</button><div class="border-2 border-(--accent) rounded-(--radius,8px) p-(--space)">This month: The Years, by Annie Ernaux</div></div>
Scope and cascade
A variable lives on the element that declares it and on its descendants. It follows the normal cascade: specificity, order and inheritance, same as color. That's why declaring it on .card changes the accent only inside the card, while the button keeps the one from .theme.
The second argument of var() is the fallback value. With var(--radius, 8px), if --radius isn't defined on any ancestor, 8px is used. That's what happens in the demo when you remove it.
.theme {--space: red; /* valid: not checked when declared */}.card {padding: 24px;padding: var(--space); /* invalid at computed-value time: ends up 0 */}
Watch out for that case: if the variable exists but its value doesn't work for the property, the declaration is invalid at computed-value time. Neither the fallback nor the earlier padding: 24px is used; the property behaves as unset, and since padding doesn't inherit, it's 0.
Practical uses
- Design tokens
:rootNamed colors, radii and spacing on:root, so you change them in one place. - Themes
prefers-color-schemeRedefine the same tokens inside@media (prefers-color-scheme: dark)and everything that reads them changes. This site works that way. - Calculations
calc(var(--space) * 2)With--space: 16pxit gives 32px. To add a unit to a number usecalc(var(--n) * 1px);var(--n)pxdoesn't work.
:root {--bg: oklch(0.99 0 0);--text: oklch(0.2 0 0);}@media (prefers-color-scheme: dark) {:root {--bg: oklch(0.2 0 0);--text: oklch(0.95 0 0);}}body {background: var(--bg);color: var(--text);}
A plain variable has no type, so the browser can't interpolate it and a transition on it jumps at once. With @property you register it with a type (syntax), whether it inherits (inherits) and an initial value (initial-value). Then it can animate:
@property --angle {syntax: "<angle>";inherits: false;initial-value: 0deg;}.arrow {transform: rotate(var(--angle));transition: --angle 300ms;}.arrow:hover {--angle: 90deg;}
In Tailwind
In Tailwind v4 the theme values are already variables: --spacing or --color-white live on :root. To declare your own on an element, use an arbitrary property in brackets, and to read it, the class with the name in parentheses.
| CSS | Tailwind |
|---|---|
| --space: 16px | [--space:16px] |
| background-color: var(--accent) | bg-(--accent) |
| border-color: var(--accent) | border-(--accent) |
| padding: var(--space) | p-(--space) |
| border-radius: var(--radius, 8px) | rounded-(--radius,8px) |
| --color-accent in @theme | bg-accent · border-accent |
<div class="[--accent:oklch(0.52_0.2_290)] [--space:16px]"><button class="bg-(--accent) text-white rounded-(--radius,8px)px-(--space) py-[calc(var(--space)/2)]">Save</button><div class="[--accent:oklch(0.52_0.2_10)]border-2 border-(--accent) p-(--space)">Card</div></div>