The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Register a length | @property --gap {
syntax: "<length>";
inherits: false;
initial-value: 0px;
} | View examples |
| Register a color | @property --accent {
syntax: "<color>";
inherits: true;
initial-value: #2563eb;
} | View examples |
| Register a number | @property --progress {
syntax: "<number>";
inherits: false;
initial-value: 0;
} | View examples |
| Accept any token stream | @property --payload { syntax: "*"; inherits: false; } | View examples |
| Allow alternative types | @property --offset {
syntax: "<length> | auto";
inherits: false;
initial-value: 0px;
} | View examples |
| Register a space list | @property --stops {
syntax: "<color>+";
inherits: true;
initial-value: transparent;
} | View examples |
| Register from JavaScript | CSS.registerProperty( {
name:
"--angle", syntax: "<angle>", inherits: false, initialValue: "0deg";
} | View examples |
| Detect JavaScript registration | if ("registerProperty" in CSS) { registerTokens(); } | View examples |
| Handle duplicate registration | try {
CSS.registerProperty(definition);
} catch (error) { console.warn(error.name);;
} | View examples |
| Animate a registered angle | .loader { transition: --angle 600ms linear; } | View examples |
| Use a typed value in a gradient | .loader {
background:
conic-gradient(from var(--angle), #2563eb, transparent);
} | View examples |
| Drive a calculation | .bar {
scale: var(--progress) 1;
transform-origin: left;
} | View examples |
| Fall back after invalid assignment | .card { --gap: red; gap: var(--gap); } | View examples |
| Request inheritance | .child { --gap: inherit; } | View examples |
| Reset to registered initial | .component { --progress: initial; } | View examples |
| Keep an unregistered fallback | .card {
--accent: #2563eb;
color: var(--accent, #2563eb);
} | View examples |
| Stop typed-property motion | @media (prefers-reduced-motion: reduce) {
.loader { transition: none; };
} | View examples |
| Find @property CSSOM rules | [...sheet.cssRules].filter(rule => rule.constructor.name === "CSSPropertyRule") | View examples |
Ordinary custom properties hold token streams and inherit by default. The Properties and Values API can register a custom property with a grammar, inheritance flag, and initial value, enabling computed-value validation and type-aware interpolation. This guide distinguishes CSS and JavaScript registration, explains computational independence and failure modes, and uses typed tokens without turning them into hidden semantic state.
Step by step
Detailed examples
Declare a complete valid registration
A valid @property rule requires a dashed custom-property name plus syntax and inherits descriptors. initial-value is also required unless syntax is the universal * grammar. Unknown descriptors are ignored, but malformed required descriptors invalidate the rule. When multiple valid @property rules register the same name, the last in stylesheet order wins; JavaScript registration has higher registration precedence.
@property --gap {
syntax: "<length>";
inherits: false;
initial-value: 0px;
}
.stack { --gap: 1rem; display:grid; gap:var(--gap); } Use the supported grammar language precisely
The syntax descriptor is a quoted definition using supported component names, literal identifiers, alternation, and list multipliers. It is not arbitrary CSS value-definition grammar. * accepts any token stream and makes the initial value optional, but forfeits useful type interpolation. An initial value for non-universal syntax must parse against the grammar and be computationally independent: 10px is valid, while 1em depends on font metrics and is not.
@property --offset { syntax:"<length> | auto"; inherits:false; initial-value:0px; }
@property --palette { syntax:"<color>+"; inherits:true; initial-value:transparent; } Register once and treat failures as expected
CSS.registerProperty() accepts name, syntax, inherits, and optional initialValue. It can throw SyntaxError for invalid names or syntax and InvalidModificationError when the property is already registered. Registrations belong to a Document and cannot be unregistered, so component mount code should not repeatedly register. Feature-detect, centralize definitions, and handle duplicates caused by bundles or hot reload.
function registerAngle() {
if (!("registerProperty" in CSS)) return;
try {
CSS.registerProperty({ name:"--angle", syntax:"<angle>", inherits:false, initialValue:"0deg" });
} catch (error) {
if (error.name !== "InvalidModificationError") throw error;
}
} Animate types, then consume them cheaply
Registration lets the browser interpolate a custom property according to its type. The custom property still affects rendering only where var() substitutes it into another property. Prefer consumption in transforms, opacity-like effects, or localized paint; animating a typed length used in layout can trigger layout on every frame. Registering a color determines type-aware interpolation but not a chosen color space across every browser.
@property --progress { syntax:"<number>"; inherits:false; initial-value:0; }
.meter { --progress:0; background:conic-gradient(#2563eb calc(var(--progress)*1turn),#e2e8f0 0); transition:--progress 500ms ease; }
.meter.complete { --progress:1; } Understand invalid values at computed-value time
A registered assignment is parsed against its syntax at computed-value time. Invalid values fall back through inheritance or to the registration initial value according to the registered inherit flag; they do not necessarily activate a later declaration the way parse-time invalid syntax would. CSS-wide keywords retain their defined behavior. Registration can therefore change existing unregistered custom-property behavior, so choose globally unique names and test third-party components.
@property --progress { syntax:"<number>"; inherits:false; initial-value:0; }
.parent { --progress:.8; }
.child { opacity:calc(.5 + var(--progress)*.5); } /* child uses 0 */ Progressively enhance without encoding meaning in CSS
Before registration support, declarations still behave as ordinary custom properties, so provide a valid baseline value and var() fallback. @supports cannot directly prove that an @property registration took effect; test the downstream declaration or feature-detect CSS.registerProperty when JavaScript is already required. Custom properties are presentation state, not accessible state: update native attributes, text, ARIA, and DOM behavior separately.
.progress { --progress:1; transform:scaleX(var(--progress,1)); }
@supports (background:paint(something)) { /* unrelated support is not an @property test */ }
@media (prefers-reduced-motion:reduce) { .progress { transition:none; } } Local code tester
Animate a typed design token
Toggle the meter to animate a registered number used by a conic gradient; reduced-motion preferences remove the transition.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
- CSS Houdini Task ForceCSS Properties and Values API Level 1drafts.css-houdini.org
- CSS Working GroupCSS Custom Properties for Cascading Variables Level 1drafts.csswg.org
- CSS Working GroupCSS Values and Units Module Level 4drafts.csswg.org
- CSS Working GroupCSS Transitions Level 2drafts.csswg.org
- MDN Web Docs@propertydeveloper.mozilla.org
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



