CSS @property Gives Variables a Type
A normal CSS custom property is deliberately loose. The browser stores its value as tokens and only finds out whether those tokens make sense when the variable is used.
:root {
--brand: #3366ff;
--space: banana;
}
That flexibility is useful, but sometimes I want the browser to know that --brand is a colour or --space is a length. The @property rule adds exactly that information.
Register the contract
A registered property needs a syntax, an inheritance choice, and usually an initial value:
@property --brand {
syntax: "<color>";
inherits: true;
initial-value: #3366ff;
}
The browser can now reject values that are not colours. inherits says whether a child receives its parent’s computed value, while initial-value provides the fallback used when no valid value wins.
The property still works with var() like any other custom property:
.button {
background: var(--brand);
}
Animation is where it becomes interesting
An unregistered custom property usually changes as a discrete value because the browser does not know how to interpolate it. Registering the type makes smooth interpolation possible:
@property --progress {
syntax: "<percentage>";
inherits: false;
initial-value: 0%;
}
.bar {
--progress: 0%;
background: linear-gradient(
to right,
blue var(--progress),
#ddd var(--progress)
);
animation: fill 2s linear forwards;
}
@keyframes fill {
to {
--progress: 100%;
}
}
The same idea works with angles:
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.box {
background: linear-gradient(var(--angle), blue, purple);
animation: rotate-gradient 4s linear infinite;
}
@keyframes rotate-gradient {
to {
--angle: 360deg;
}
}
Useful syntax components include <color>, <length>, <percentage>, <number>, <angle>, and <length-percentage>. Alternatives can be combined:
@property --size {
syntax: "<length> | <percentage>";
inherits: false;
initial-value: 10px;
}
Most variables do not need registration
I would not wrap every design token in @property. Plain custom properties remain ideal when I only need reusable text substitution.
Registration earns its place when invalid values should fall back predictably, inheritance must be explicit, or the value needs to animate. It has been broadly available in current browsers since 2024, but a feature-support check is still sensible when an animation is more than decoration.
Think of @property as a contract for the variables that need one. The rest can remain pleasantly unbureaucratic.