CSS Advanced
CSS @property
Plain CSS variables are untyped text, so the browser cannot animate them. The @property at-rule registers a custom property with a type, an inheritance flag and an initial value, which unlocks animation, better validation and gradient interpolation. This guide explains it with live examples.
Why @property Exists
A normal custom property is just a string. Because the browser does not know whether --angle holds a number, a colour or a length, it cannot smoothly interpolate it during a transition or animation. @property tells the browser the property's type, so it can animate the value and reject invalid input.
Syntax
@property takes a property name and three descriptors: syntax (the type), inherits (whether it cascades to children), and initial-value (a required default).
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}| Descriptor | Meaning | Example |
|---|---|---|
| syntax | The allowed value type | "<color>", "<length>", "<number>" |
| inherits | Whether it inherits to descendants | true / false |
| initial-value | Required default value | 0deg, #000, 0px |
| syntax value | Accepts |
|---|---|
| "<length>" | Lengths like 10px, 2rem |
| "<color>" | Colours like #f00, red |
| "<number>" | Plain numbers |
| "<percentage>" | Percentages |
| "<angle>" | Angles like 45deg, 0.5turn |
| "*" | Any value (untyped, not animatable) |
Animating a Registered Angle
Registering --angle as an <angle> lets a conic gradient spin. Without @property the same keyframes would jump instead of rotating smoothly, because the variable would be treated as text.
<!DOCTYPE html>
<html>
<head>
<style>
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.spinner {
width: 140px; height: 140px; border-radius: 50%;
background: conic-gradient(from var(--angle), #6366f1, #ec4899, #6366f1);
animation: spin 3s linear infinite;
}
@keyframes spin {
to { --angle: 360deg; }
}
</style>
</head>
<body>
<div class="spinner"></div>
</body>
</html>Animating a Typed Number
<!DOCTYPE html>
<html>
<head>
<style>
@property --p {
syntax: "<percentage>";
inherits: false;
initial-value: 0%;
}
.bar {
height: 22px; border-radius: 999px;
background: linear-gradient(#22c55e, #22c55e) left / var(--p) 100% no-repeat, #e5e7eb;
transition: --p 1.2s ease;
}
.bar:hover { --p: 100%; }
</style>
</head>
<body>
<div class="bar"></div>
<p style="font:13px sans-serif">Hover the bar to fill it.</p>
</body>
</html>@property can also be registered from JavaScript with CSS.registerProperty({ name, syntax, inherits, initialValue }), which is handy when tokens are generated dynamically.
initial-value is mandatory for any syntax except "*". Omitting it makes the whole @property rule invalid and it is ignored.
Key points
- @property registers a custom property with a type so the browser can animate it.
- It needs three descriptors: syntax, inherits and initial-value.
- Typed properties interpolate smoothly in transitions and keyframes.
- syntax: "*" is untyped and, like plain variables, cannot be animated.
- initial-value is required unless syntax is "*".
