@property Rule
Overview
While CSS Variables are amazing, they have a fatal flaw: the browser treats them as dumb strings. If you have --rotation: 0deg; and you transition to --rotation: 180deg;, the browser just sees two text strings and doesn't know how to animate the math in between. The modern @property rule allows you to strongly 'Type' your CSS variables (just like TypeScript!). By telling the browser a variable is an <angle> or a <color>, the browser unlocks the ability to natively animate it.
Syntax
/* 1. Register the variable and its Type */
@property --gradient-angle {
syntax: '<angle>'; /* Tells the browser this is math! */
inherits: false; /* Performance optimization */
initial-value: 0deg; /* Default starting value */
}
/* 2. Use it in a gradient */
.spinning-border {
background: conic-gradient(
from var(--gradient-angle),
red, blue, red
);
/* We can now animate the variable directly! */
animation: spin 3s linear infinite;
}
/* 3. The Animation Keyframes */
@keyframes spin {
to {
--gradient-angle: 360deg; /* The browser knows how to interpolate this now! */
}
}Common Pitfalls
- Using
@propertywithout aninitial-value. If the syntax is anything other than*(any string), you are strictly required to define aninitial-value. If you omit it, the entire rule is thrown out. - Forgetting that this is a cutting-edge feature. While supported in modern Chromium and Safari, older enterprise environments will ignore the
@propertyrule, meaning the animation will instantly jump from state A to state B without transitioning.
Interview Questions
Because CSS background-image cannot be mathematically interpolated. By using @property to type a variable as an <angle> or <color>, we bypass the image restriction and animate the mathematical variable feeding into the gradient instead.
Real-World Example
Creating a smooth number counter purely in CSS using typed variables.
@property --counter {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
.score-display {
/* Use CSS Counters to print the variable to the screen */
counter-reset: score var(--counter);
animation: countUp 5s ease-out forwards;
}
.score-display::after {
content: counter(score); /* Displays the number */
}
@keyframes countUp {
to { --counter: 1000; } /* Interpolates smoothly from 0 to 1000! */
}Check Your Knowledge
Test your understanding of @property Rule with these quick questions.