CSS custom properties
Variables that live in the cascade.
Custom properties aren't preprocessor variables β they're real values in the DOM that inherit, cascade, and update live. Define once with --name, read with var().
Declare with --name, read with var()
A custom property is any property whose name starts with two dashes: --accent: #e9b454. You read it back with the var() function: color: var(--accent). Names are case-sensitive, so --Accent and --accent are different properties.
The convention is to declare your shared values on :root (the <html> element) so they're available everywhere. Because custom properties inherit, a value set on :root flows down to every descendant unless something closer overrides it.
Fallbacks and the cascade
var() takes an optional second argument: a fallback used when the property is missing or invalid. var(--gap, 8px) resolves to 8px if --gap was never set. The fallback can itself be a var(), so you can chain defaults.
Custom properties obey the normal cascade. Whatever wins specificity wins for the property too β set --accent on :root, then re-set it on .theme-warm, and any element inside .theme-warm reading var(--accent) gets the warm value. This is what makes theming a matter of swapping a class rather than rewriting rules.
Compute with them
Because the resolved value is a real value, you can feed it into calc(): padding: calc(var(--space) * 2). Custom properties are resolved at the element where var() appears, using the value inherited there β so a single --space token can drive a whole spacing scale by multiplying it inline.
Try it 5 examples
Define on :root, use everywhere
HTML+CSS+JSintroThe card's panel background, ink text, amber border, title colour, and CTA all resolve from the four tokens declared once on :root. Because custom properties inherit, every descendant reads the same var() values without any of them being redeclared.