How to Use CSS @property for Typed Custom Properties
The CSS @property rule, part of the CSS Houdini family
of APIs, allows developers to explicitly define CSS custom properties
with strict data types, inheritance behavior, and initial default
values. This article explains how @property works, breaks
down its core descriptors, and demonstrates how it enhances type safety,
fallback handling, and animations in modern web development.
The Limitation of Standard CSS Variables
Standard CSS custom properties behave essentially as untyped tokens
or strings. A variable such as --primary-size: 16px; can be
reassigned to any value, including an invalid one like
--primary-size: red;. In these cases, the browser cannot
infer the intended data type. This limitation causes two major
issues:
- Lack of Fallback Protection: If an invalid value is supplied, the browser falls back to standard inheritance rather than a designated default value, potentially breaking layouts.
- Animation Limitations: The browser cannot interpolate between values if it does not know the underlying type. For example, standard CSS variables cannot smoothly transition colors within gradients or custom numeric values.
Anatomy of the
@property Rule
The @property at-rule registers a custom property
directly in the stylesheet. It requires three core descriptors:
syntax: A string defining the expected data type using standard CSS syntax types (such as<color>,<length>,<percentage>,<integer>, or<angle>).inherits: A boolean (trueorfalse) that dictates whether the property cascades down to child elements.initial-value: The default fallback value. This descriptor is mandatory ifsyntaxis anything other than universal (*).
Basic Syntax Example
@property --brand-color {
syntax: '<color>';
inherits: true;
initial-value: #0066cc;
}
@property --gradient-angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}How Typed Properties Function
1. Enforcing Types and Applying Defaults
When a custom property is registered with @property, the
browser validates any assigned value against the declared
syntax. If an author or dynamic script assigns an invalid
value, the browser discards it and applies the
initial-value:
.card {
/* Invalid assignment for a <color> syntax */
--brand-color: 24px;
/* Resolves to the initial-value: #0066cc */
background-color: var(--brand-color);
}This prevents style failures caused by typos or malformed values passed via JavaScript.
2. Controlling Style
Leakage with inherits
Standard CSS variables always inherit down the DOM tree. By setting
inherits: false, developers can scope custom properties
strictly to the elements where they are declared. This stops parent
variables from unintentionally cascading into nested components.
3. Enabling Complex Transitions and Animations
Because the browser knows the exact mathematical type of the property, it can interpolate between values across keyframes or transitions. This makes it possible to animate properties that were previously impossible to transition directly, such as CSS gradients:
@property --stop-position {
syntax: '<percentage>';
inherits: false;
initial-value: 0%;
}
.gradient-box {
background: linear-gradient(to right, #0066cc var(--stop-position), #ff3366);
transition: --stop-position 0.5s ease-in-out;
}
.gradient-box:hover {
--stop-position: 100%;
}In this scenario, the rendering engine smoothly steps through intermediate percentage values, producing a continuous gradient animation.
Conclusion
The @property rule transforms CSS variables from simple
string replacements into robust, predictable styling primitives. By
enforcing data types, providing dependable fallbacks, managing
inheritance, and enabling complex transitions, it provides greater
control over dynamic styling in modern web applications.