Custom Properties & Fallbacks
Cascading variables that inherit, theme, and animate β plus the two-argument var() fallback and @property.
Custom properties (CSS variables) are not like Sass variables. They're live: they cascade, they inherit, you can read and change them at runtime, and unlike preprocessor variables they exist in the browser.
Define, use, fall back
:root {
--brand: #2965f1;
--space: 1rem;
}
.btn {
background: var(--brand);
/* second argument is used if --gap is undefined */
gap: var(--gap, 0.5rem);
}They cascade — that's the whole point
Set --brand on :root for a global default, then override it on a single component or a [data-theme="dark"] block. Everything downstream that reads var(--brand) updates for free. This is why theming and dark mode became a five-line job.
Registered properties with @property
A plain custom property is always treated as a string, so you can't animate it. Register it with @property to give it a type, and suddenly gradients and angles become animatable:
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}Example
When to use it
- A developer defines brand colors as --color-primary and --color-secondary in :root so changing one variable updates the entire site's theme.
- A designer toggles a [data-theme='dark'] attribute on the body element, which overrides :root custom properties to instantly switch every component's colors.
- A developer uses var(--spacing-md, 16px) with a fallback value so components remain styled even in older environments that partially support custom properties.
More examples
Design token system with :root
Centralises design tokens as custom properties in :root so a single change propagates through every component.
:root {
--color-primary: #2965f1;
--color-danger: #c0392b;
--color-bg: #fff;
--color-text: #1a1a2e;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 32px;
--radius-card: 8px;
}Dark theme via property override
Overrides custom properties on the data-theme attribute so toggling the attribute switches the entire theme instantly.
:root {
--bg: #fff;
--text: #1a1a2e;
}
[data-theme='dark'] {
--bg: #1a1a2e;
--text: #f0f0f0;
}
body {
background-color: var(--bg);
color: var(--text);
}var() with fallback value
Uses the two-argument var() form to supply fallback values, including a chained custom property as the fallback.
.card {
/* Uses --card-padding if set, falls back to 20px */
padding: var(--card-padding, 20px);
border-radius: var(--radius-card, 8px);
background: var(--card-bg, var(--color-bg, #fff));
}
Discussion