Skip to main content
  • Created:
  • Updated:
  • Author:
    Takeshi Takatsudo

@property

The Problem

Standard CSS custom properties are untyped — the browser treats them as arbitrary strings. This means you cannot animate or transition custom properties (e.g., smoothly transitioning a gradient), the browser cannot validate their values, and inheritance behavior cannot be controlled. AI agents rarely use @property and instead resort to JavaScript animations or complex workarounds for effects that @property makes trivial.

The Solution

The @property at-rule lets you formally register a CSS custom property with a type (syntax), an initial value, and inheritance control. Once a property is typed, the browser can interpolate it — enabling smooth transitions and animations on values that were previously impossible to animate, like gradients, colors within gradients, and individual numeric values.

Code Examples

Basic @property Declaration

@property --primary-color {
syntax: "<color>";
inherits: true;
initial-value: #2563eb;
}

@property --card-radius {
syntax: "<length>";
inherits: false;
initial-value: 8px;
}

@property --opacity-level {
syntax: "<number>";
inherits: false;
initial-value: 1;
}

Animating Gradients

Without @property, gradient transitions snap between states. With typed properties, smooth interpolation becomes possible.

@property --gradient-start {
syntax: "<color>";
inherits: false;
initial-value: #3b82f6;
}

@property --gradient-end {
syntax: "<color>";
inherits: false;
initial-value: #8b5cf6;
}

.hero-banner {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
transition: --gradient-start 0.6s, --gradient-end 0.6s;
}

.hero-banner:hover {
--gradient-start: #ec4899;
--gradient-end: #f59e0b;
}

Animated Gradient Angle

@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}

.rotating-gradient {
background: linear-gradient(var(--angle), #3b82f6, #8b5cf6);
animation: rotate-gradient 3s linear infinite;
}

@keyframes rotate-gradient {
to {
--angle: 360deg;
}
}

Progress Indicator with Animated Percentage

@property --progress {
syntax: "<percentage>";
inherits: false;
initial-value: 0%;
}

.progress-ring {
background: conic-gradient(
#2563eb var(--progress),
#e5e7eb var(--progress)
);
border-radius: 50%;
transition: --progress 1s ease-out;
}

.progress-ring[data-value="75"] {
--progress: 75%;
}

Type-Safe Design Tokens

@property --spacing-unit {
syntax: "<length>";
inherits: true;
initial-value: 0.25rem;
}

@property --brand-hue {
syntax: "<number>";
inherits: true;
initial-value: 220;
}

.design-system {
--spacing-unit: 0.25rem;
--brand-hue: 220;
}

.card {
padding: calc(var(--spacing-unit) * 4);
background: hsl(var(--brand-hue) 90% 95%);
border: 1px solid hsl(var(--brand-hue) 60% 70%);
}

Controlling Inheritance

@property --section-bg {
syntax: "<color>";
inherits: false; /* Does NOT cascade to children */
initial-value: transparent;
}

.section {
--section-bg: #f0f9ff;
background: var(--section-bg);
}

/* Nested sections get transparent (initial-value), not the parent's blue */
.section .section {
/* --section-bg is transparent here because inherits: false */
background: var(--section-bg);
}

Supported Syntax Types

/* All supported syntax descriptors */
@property --a { syntax: "<color>"; inherits: false; initial-value: black; }
@property --b { syntax: "<length>"; inherits: false; initial-value: 0px; }
@property --c { syntax: "<percentage>"; inherits: false; initial-value: 0%; }
@property --d { syntax: "<number>"; inherits: false; initial-value: 0; }
@property --e { syntax: "<integer>"; inherits: false; initial-value: 0; }
@property --f { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
@property --g { syntax: "<time>"; inherits: false; initial-value: 0s; }
@property --h { syntax: "<resolution>"; inherits: false; initial-value: 1dppx; }
@property --i { syntax: "<length-percentage>"; inherits: false; initial-value: 0px; }
@property --j { syntax: "<image>"; inherits: false; initial-value: url(); }
@property --k { syntax: "<transform-function>"; inherits: false; initial-value: scale(1); }
@property --l { syntax: "<custom-ident>"; inherits: false; initial-value: none; }

/* Union types */
@property --m { syntax: "<color> | <length>"; inherits: false; initial-value: black; }

/* Universal (any value, like regular custom properties) */
@property --n { syntax: "*"; inherits: true; }

Browser Support

  • Chrome 85+
  • Edge 85+
  • Firefox 128+
  • Safari 15.4+

Global support exceeds 93%. Firefox added support in mid-2024, making @property available in all major browsers. The feature is Baseline Newly available as of July 2024.

Common AI Mistakes

  • Not using @property when trying to animate gradients or custom property values — resulting in abrupt snapping instead of smooth transitions
  • Using JavaScript-based animation (requestAnimationFrame) for effects that typed custom properties handle natively
  • Forgetting that all three descriptors (syntax, inherits, initial-value) are required (except initial-value when syntax is *)
  • Setting inherits: true when the property should be component-scoped, causing unintended cascade leakage
  • Not knowing that @property enables gradient animations — this is the most impactful and most overlooked use case
  • Confusing @property registration with the JavaScript CSS.registerProperty() API — the CSS at-rule is preferred for static definitions

When to Use

  • Smooth gradient transitions and animations (the primary killer use case)
  • Animated progress indicators, loading spinners, and visual effects using conic/radial gradients
  • Type-safe design tokens where the browser should validate property values
  • Controlling inheritance to prevent custom properties from cascading into nested components
  • Any animation that requires interpolating a custom property value

Live Previews

Animated Gradient with @property — Hover to see
Rotating Gradient Angle with @property

References