CSS @property
In this page:
@property --property-name {
syntax: "<type>";
inherits: true | false;
initial-value: value;
}
The Problem With Plain Custom Properties
A plain --custom-property declared with var() has no type checking at all -- the browser treats its value as an opaque string substituted at use-time, which means it can't be smoothly animated (the browser doesn't know how to interpolate between two arbitrary strings) and typos or invalid values fail silently.
- Accepted values:
- name: <value> — accepts any token stream
- var(--name) — substituted as-is at use time
- var(--name, fallback) — fallback if not defined
- transition: --name — an unregistered property switches abruptly instead of animating
Example: The Problem With Plain Custom Properties
<style>
.box { padding: 20px; color: white; background: var(--my-color); }
.box:hover { --my-color: blue; }
:root {
--my-color: red;
}
.box {
transition: --my-color 0.3s;
}
</style>
<div class="box">Hover: the color snaps, it does not animate</div>
Registering a Typed Property With @property
@property registers a custom property name with an explicit syntax (its type, like <length> or <color>), whether it inherits, and an initial-value -- turning what was an opaque string into a real, browser-understood typed value.
- Accepted values:
- syntax: '<color>' — required type descriptor
- inherits: true | false — required inheritance flag
- initial-value: <value> — required for non-universal types
- @property --name — the rule name must start with two dashes
Example: Registering a Typed Property With @property
<style>
@property --my-color {
syntax: '<color>';
inherits: false;
initial-value: red;
}
.box { padding: 20px; color: white; background: var(--my-color); transition: --my-color 0.5s; }
.box:hover { --my-color: blue; }
@property --my-color {
syntax: '<color>';
inherits: false;
initial-value: red;
}
</style>
<div class="box">Registered typed property</div>
The syntax Descriptor
The syntax descriptor declares the accepted value type using predefined syntax component names like <color>, <length>, <percentage>, <number>, or <angle>.
An invalid value assigned to the property is rejected and the initial-value is used instead, unlike plain custom properties which accept anything.
- Accepted values:
- '<color>' — any CSS color
- '<length>' — px, rem, em etc.
- '<percentage>' — e.g. 50%
- '<number>' | '<integer>' — unitless numbers, whole numbers only
- '<angle>' — deg, rad, turn
- '<length-percentage>' — a length or a percentage
- '<color> | none' — alternatives separated by |
- '<length>+' | '<length>#' — space or comma separated lists
- '*' — any value (universal)
Example: The syntax Descriptor
<style>
@property --gap-size {
syntax: '<length>';
inherits: false;
initial-value: 0px;
}
.box { display: flex; gap: var(--gap-size); transition: --gap-size 0.4s; }
.box:hover { --gap-size: 30px; }
.box { background: #cfe8ff; border: 2px solid #1e6fd9; padding: 10px; }
@property --gap-size {
syntax: '<length>';
inherits: false;
initial-value: 0px;
}
</style>
<div class="box">Length-typed custom property</div>
Now It Can Be Smoothly Animated
Because a registered property has a real, interpolatable type, the browser can now animate it smoothly with transition or @keyframes -- this is the primary practical payoff of @property, unlocking animations that were impossible with a plain untyped custom property.
- Accepted values:
- transition: --my-color 0.3s — animates a registered property
- animation on a registered property — interpolates by type
- <color> — interpolates color values
- <length> | <angle> | <number> — interpolate numerically
- syntax: '*' — cannot be interpolated
Example: Now It Can Be Smoothly Animated
<style>
@property --angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
.box {
background: conic-gradient(red var(--angle), blue);
transition: --angle 0.3s;
}
.box:hover {
--angle: 180deg;
}
</style>
<div class="box">Now smoothly animatable</div>
The inherits and initial-value Descriptors
inherits (true/false) explicitly controls whether the property flows down to child elements, and initial-value sets the fallback used whenever the property is unset or assigned an invalid value.
Both descriptors are required for a valid @property rule, unlike the optional nature of plain custom property fallbacks in var().
- Accepted values:
- inherits: true — children inherit the value
- inherits: false — children get the initial value
- initial-value: <value> — the value used when the property is not set
- syntax: '*' — initial-value may be omitted
Example: The inherits and initial-value Descriptors
<style>
.parent { --my-size: 24px; }
.child { font-size: var(--my-size); background: #cfe8ff; border: 2px solid #1e6fd9; padding: 10px; }
@property --my-size {
syntax: '<length>';
inherits: true;
initial-value: 16px;
}
</style>
<div class="parent"><div class="child">Child inherits --my-size</div></div>
- Forgetting the
syntaxdescriptor, so the property is not registered. - Missing the
initial-valuefor a property withinherits, so the rule is invalid. - Expecting a custom property to animate without registering it with
@property.
@property is a newer feature: supported in Chrome 85+, Safari 16.4+ and Firefox 128+.
Chapter Quiz — Complete all 25 topics to unlock
0/25 topics done
Complete these topics first:
- CSS Container Queries
- CSS Subgrid
- CSS Logical Properties
- CSS Logical Sizing
- CSS Writing Modes
- CSS Aspect Ratio
- CSS Object Fit
- CSS object-position
- CSS Masking
- CSS Math Functions
- CSS Motion Path
- CSS @supports
- CSS @property
- CSS At-Rules
- CSS Cursor
- CSS Will Change
- CSS User Interface
- CSS Print Styles
- CSS Pagination (Print)
- CSS Dark Mode
- CSS Accessibility
- CSS Performance
- CSS Preprocessors
- CSS Frameworks Overview
- CSS Interview Prep