Prompt and context
A component library uses custom properties such as --accent, --progress, and --card-size. Design requirements call for smooth transitions, but the variables are untyped strings, invalid values propagate silently, and child components accidentally inherit theme values. Explain when to register them with CSS @property, including defaults, invalid values, inheritance, and browser compatibility.
This fits frontend, design-system, and Web-performance interviews. The test is understanding the boundary of the CSS Properties and Values API, not registering every variable. Cover syntax, initial values, inheritance, interpolation, scope, progressive enhancement, and tests.
What the interviewer is testing
A strong answer says ordinary custom properties are computed as strings and inherit by default; registered properties declare a type, initial value, and inheritance behavior, with invalid values checked at computed-value time. Explain that syntax and inherits are required descriptors, that non-universal syntax normally needs a computationally independent initial-value, and when animation, type constraints, and non-inherited component state justify registration. Do not present @property as JavaScript state or an automatic performance guarantee.
Questions to clarify first
- What type does the variable carry: color, length, angle, number, or an open token string?
- Does it need smooth interpolation, or only a discrete theme swap?
- Should it inherit into children, or should the component boundary isolate it?
- On an invalid value, should the result be the initial value, an inherited value, or no declaration?
- What are the browser targets, server-rendering path, fallback, and design-token release process?
A 30-second answer
“Ordinary custom properties are inheriting strings and work well for open tokens. I would register a property when it needs type checking, a non-inherited default, or smooth interpolation. The registration must specify syntax, inherits, and a computationally independent initial-value, making invalid assignments predictable. Older browsers keep the ordinary variable as a fallback, and component tests cover inheritance, invalid values, animation, and theme changes; registration itself is not assumed to be faster.”
Step-by-step answer
Step 1: Separate ordinary variables from registered properties
An ordinary --name participates in the cascade and inherits by default; the browser treats its value as a token sequence. That is useful for colors, spacing tokens, and open compositions, but the browser does not know whether a value should be a color or a length. @property adds type, inheritance, and default-value metadata to the same variable.
Step 2: Choose an explicit syntax
Register only values that need constraints or interpolation. Use the CSS color, number, and angle syntax types for the matching values. Do not force a multi-token experimental value into one type just to appear strict; valid theme values would then be discarded.
Step 3: Set inherits and initial-value
Make inherits explicit. A theme color usually inherits; an internal component size or animation progress may not. Non-universal syntax requires a computationally independent initial-value, such as a fixed unit rather than a percentage depending on a parent. Missing required descriptors make the registration invalid.
Step 4: Understand invalid values and fallback
Registered properties validate at computed-value time. A value with the wrong type does not apply and the property resolves through its registered initial value, inheritance, or another valid cascade result, depending on inherits and declaration location. Global keywords such as initial, inherit, unset, and revert still have special semantics and need separate tests.
Step 5: Use interpolable animation
Unregistered custom properties generally change discretely because the browser cannot interpolate arbitrary strings. Once registered as a color, length, or number, the browser knows how to interpolate gradients, progress, and rotation. Still test duration, compositing, reduced-motion behavior, and the real cost of updating many elements.
Step 6: Control component boundaries
Name global theme tokens separately from internal state. Inheriting values are useful for themes; a non-inherited property lets a component use its own initial value and prevents a parent’s same-named variable from contaminating internal animation. If a property crosses shadow DOM or container boundaries, define the namespace and public API before deciding to register it.
Step 7: Provide progressive enhancement
Ship an ordinary custom property and a static final state first, so browsers without @property still render usable UI. Supported browsers can add type constraints and interpolation. Do not make critical layout depend only on registration; use @supports (property: --x) or capability detection when selecting the enhancement path.
Step 8: Verify errors and browser behavior
Test valid values, wrong types, missing initial values, inheritance toggles, theme overrides, mid-animation updates, and the server-rendered first paint. Check older browsers, computed values in developer tools, and visual regressions. MDN notes that registration is validated at computed-value time and a malformed rule may be ignored entirely, so build checks and runtime examples both matter.
Trade-offs and boundaries
Registration adds declaration and design-token maintenance cost, and contributors unfamiliar with the API may assume every value remains freely replaceable. Register values with a clear type, animation need, default, or inheritance boundary; keep open-ended content tokens as ordinary custom properties.
Type checking is not a security boundary or a cross-browser consistency guarantee. Support, parsing timing, and animation compositing still need the target matrix tested. Critical accessibility states need static styles and a reduced-motion path.
Rollout plan and evidence
Pilot one progress or color component. Record each variable’s purpose, type, inheritance policy, and fallback. Use valid and invalid examples to verify computed results, then compare animation smoothness, style recalculation, and older-browser behavior before and after registration. Once the pilot passes, include registrations, token documentation, and visual regression in the component-library release checks.
Treat every syntax or inherits change as a behavior change and inspect child overrides, theme switching, and persisted snapshots. If users report a color jump or size fallback, inspect computed values and capability paths before changing animation duration.
Common mistakes and follow-ups
Registering every CSS variable
Open tokens, composite values, and experimental strings may not have a stable syntax. Register only values that need type, default, inheritance control, or interpolation.
Forgetting computational independence of initial-value
For non-universal syntax, the initial value cannot depend on context. A percentage or em can invalidate the registration; use a computationally independent unit or accept weaker constraints with universal syntax.
Assuming invalid values keep the previous valid declaration
Registered properties validate at computed-value time. An invalid assignment can resolve to an initial or inherited result rather than the neighboring old value. Test computed styles, not just source order.
Treating inherits as complete cascade isolation
inherits: false controls default inheritance; authors can still write inherit, and another token can carry a parent value into the component. Naming, documentation, and tests must enforce the boundary together.
How do you support browsers without @property?
Keep an ordinary variable with a static final state and sensible default, then add registration and animation as enhancement. Use @supports or capability detection for critical paths and test first paint, themes, and reduced motion across the target browser matrix.