Articles /
CSS custom properties (variables)
Learn how custom properties unlock dynamic theming, component tokens, and design-system flexibility that preprocessor variables never could.
Declaring and using var()
Custom properties are declared with a double-hyphen prefix and consumed via var(). They cascade and inherit just like any other CSS property.
:root {
--brand: oklch(65% 0.25 275);
--radius: 0.5rem;
}
.card {
background: var(--brand);
border-radius: var(--radius);
}
Fallback values
var() accepts a second argument as a fallback. You can even nest var() calls for multi-level defaults.
.btn {
/* falls back to --accent, then to a literal color */
color: var(--btn-color, var(--accent, oklch(70% 0.18 145)));
}
initial. An invalid value like --x: 42px used as a color will not trigger the fallback.Inheritance and scoping
Custom properties inherit by default. Scope them to a component by declaring on a class instead of :root.
.card {
--card-pad: 1.5rem;
padding: var(--card-pad);
}
.card--compact {
--card-pad: 0.75rem; /* children inherit the override */
}
Typed variables with @property
The @property rule gives a custom property a type, initial value, and inheritance control. This enables transitions and guards against invalid values.
@property --hue {
syntax: "<number>";
inherits: false;
initial-value: 270;
}
.swatch {
background: oklch(65% 0.2 var(--hue));
transition: --hue 0.4s ease;
}
.swatch:hover {
--hue: 145;
}
Component token pattern
Expose a small API of custom properties on each component. Consumers restyle without touching internals.
.badge {
--badge-bg: oklch(92% 0.04 260);
--badge-text: oklch(30% 0.08 260);
background: var(--badge-bg);
color: var(--badge-text);
padding: 0.2em 0.6em;
border-radius: 999px;
}
/* theme override — no specificity war */
.dark .badge {
--badge-bg: oklch(30% 0.08 260);
--badge-text: oklch(92% 0.04 260);
}
Performance considerations
Changing a custom property on :root triggers style recalculation for the entire page. For frequent updates, scope the property to the smallest possible subtree.
- Prefer component-scoped properties over global ones when only part of the UI changes.
- Use
@propertywithinherits: falseto prevent unnecessary propagation. - Combine with
@layerto keep override specificity flat.