CSS Custom Properties Advanced
Build theming systems, component tokens, and JavaScript-driven animations using cascading custom properties, fallbacks, and @property.
Overview
Key Points
- •var(--color, fallback) uses the fallback if --color is not defined or is invalid
- •Custom properties inherit by default a child gets the parent's variable value
- •Redefine a variable on a more specific selector to "scope" it to that component
- •JavaScript reads: getComputedStyle(el).getPropertyValue("--my-var")
- •JavaScript writes: el.style.setProperty("--my-var", "value")
- •@property allows defining a type, syntax, and initial value for a custom property
- •@property makes custom properties animatable (without it, animation snaps instantly)
Examples
Component-level theme overrides via cascade
:root {
--card-bg: white;
--card-border: #e5e7eb;
--btn-color: #6366f1;
}
.card {
background: var(--card-bg);
border: 1px solid var(--card-border);
}
/* Dark variant override tokens locally */
.card-dark {
--card-bg: #0f172a;
--card-border: #1e293b;
--btn-color: #818cf8;
color: white;
}
.card-dark .btn {
background: var(--btn-color);
/* automatically uses the dark-variant value */
}No selector specificity tricks needed just override the variable in the right scope.
Animatable property with @property
@property --gradient-angle {
syntax: '<angle>'; /* typed as an angle */
initial-value: 0deg;
inherits: false;
}
@keyframes rotateGradient {
to { --gradient-angle: 360deg; }
}
.animated-card {
background: conic-gradient(
from var(--gradient-angle),
#6366f1, #8b5cf6, #ec4899, #6366f1
);
animation: rotateGradient 4s linear infinite;
}Without @property, animating custom properties in keyframes snaps between values (no interpolation). @property adds type information that enables smooth animation.
JavaScript-driven interactive styles
/* CSS receives the variable */
.interactive {
background: hsl(var(--hue, 240) 60% 60%);
transition: background 0.2s;
}
/* JavaScript writes the variable */
// document.addEventListener('mousemove', (e) => {
// const hue = Math.round(e.clientX / window.innerWidth * 360);
// document.documentElement.style.setProperty('--hue', hue);
// });This pattern powers cursor-following gradients, scroll-based animations, and real-time theme editors.
Common Mistakes
- Using var(--undefined-var) without a fallback the declaration becomes invalid, not just empty
- Expecting @property to animate in browsers that do not support it use @supports to check
- Overusing global :root variables for everything some tokens belong in component scope for better encapsulation
Note
Use a naming convention like --color-brand, --color-text-muted, --space-md, --radius-sm for your token names. This self-documents your design system and makes it clear what each variable controls.
Exercises
Write CSS to style the HTML scaffold. The preview updates live — when it looks right, mark as done.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4