🚀AdvancedAdvanced CSS Architecture

CSS @property: Typed Custom Properties

Register CSS custom properties with a type, initial value, and inheritance behavior to enable animation and type safety.

Overview

`@property` lets you define a custom property with a specific type, initial value, and inheritance rule. Without this, custom properties are just untyped strings that cannot be animated. ```css @property --progress { syntax: '<number>'; initial-value: 0; inherits: false; } /* Now --progress can be transitioned */ .bar { width: calc(var(--progress) * 100%); transition: --progress 0.5s ease; } .bar:hover { --progress: 0.8; } ``` **Syntax types you can use:** - `'<number>'` any number - `'<length>'` CSS length like 16px or 2rem - `'<color>'` any CSS color - `'<percentage>'` a percentage - `'<angle>'` degrees, turns - `'<integer>'` whole number - `'<url>'` a URL - `'<image>'` any image value - `'*'` any value (no type checking) **inherits:** whether the property cascades to children **initial-value:** required when inherits is false

Key Points

  • •@property must be at the top level, not inside a selector
  • •syntax, inherits, and initial-value are all required (unless syntax is *)
  • •Typed custom properties can be animated and transitioned natively
  • •The browser validates values against the declared syntax at assignment time
  • •inherits: false creates a fresh value for every element that does not explicitly set the property
  • •Use @property for gradient angles, hue rotations, counter values, and progress indicators
  • •Fallback: unregistered custom properties still work, they just cannot be animated
  • •Supported in Chrome 85+, Safari 16.4+, Firefox 128+

Examples

Animating a gradient angle

/* Without @property this would not animate smoothly */
@property --angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

.spinning-gradient {
  background: conic-gradient(from var(--angle), #6366f1, #8b5cf6, #ec4899, #6366f1);
  animation: spin-hue 4s linear infinite;
}

@keyframes spin-hue {
  to { --angle: 360deg; }
}

Without @property, CSS cannot interpolate between "0deg" and "360deg" because it treats the custom property as a string. With @property, it knows --angle is an angle and can animate it.

Animating custom property for progress

@property --fill {
  syntax: '<number>';
  initial-value: 0;
  inherits: false;
}

.progress-bar {
  background: linear-gradient(
    to right,
    #6366f1 calc(var(--fill) * 100%),
    #e5e7eb calc(var(--fill) * 100%)
  );
  height: 8px;
  border-radius: 4px;
  transition: --fill 0.6s ease;
}

.progress-bar.loaded {
  --fill: 0.75; /* 75% */
}

Because --fill is typed as a number, the browser can interpolate between 0 and 0.75, animating the gradient fill.

Type-safe color token with @property

@property --theme-primary {
  syntax: '<color>';
  initial-value: oklch(0.55 0.2 264);
  inherits: true;
}

/* Change the theme by setting the property */
.theme-emerald {
  --theme-primary: oklch(0.55 0.18 155);
}

/* All children inherit the updated color */
.btn {
  background: var(--theme-primary);
  transition: --theme-primary 0.4s ease; /* can animate between colors */
}

With syntax: color, reassigning --theme-primary and transitioning it creates a smooth color shift across the entire component tree.

Hue rotation animation

@property --hue {
  syntax: '<number>';
  initial-value: 0;
  inherits: false;
}

.hue-cycle {
  filter: hue-rotate(calc(var(--hue) * 1deg));
  animation: cycle-hue 6s linear infinite;
}

@keyframes cycle-hue {
  to { --hue: 360; }
}

/* Also works for oklch-based themes */
.dynamic-bg {
  background: oklch(0.55 0.2 calc(var(--hue) * 1deg));
  animation: cycle-hue 8s linear infinite;
}

Common Mistakes

  • Putting @property inside a selector (it must be at the top level of the stylesheet)
  • Omitting initial-value when inherits: false is set (required by spec)
  • Expecting @property to work as a JavaScript-accessible Typed OM variable (it is CSS-only)
  • Using --property: value inside @property (the descriptor names are syntax, inherits, initial-value without -- prefix)

Note

Use @property whenever you want to animate a custom property. If the animation looks like a discrete jump instead of a smooth interpolation, the property likely lacks a @property registration.

Exercises

Write CSS to style the HTML scaffold. The preview updates live — when it looks right, mark as done.

Loading editor…

Completed: 0 / 2 exercises

Knowledge Check

Question 1 of 3

Why can unregistered CSS custom properties not be animated?