How to Define CSS Custom Property Types with @property

The CSS @property rule, part of the CSS Houdini suite, allows developers to register custom properties with explicit types, inheritance behaviors, and default values. By providing strict type definitions such as <color> or <length>, browsers can properly validate values and interpolate them during transitions and animations. This article explains the syntax of the @property rule, how the syntax descriptor enforces types, and how to implement common types like colors and lengths in your stylesheets.

The @property Rule Structure

Standard CSS custom properties (variables) are parsed as arbitrary tokens, meaning the browser treats them as plain strings without understanding what they represent. The @property at-rule registers a custom property with a concrete data type using three primary descriptors:

@property --my-property {
  syntax: '<type>';
  inherits: true | false;
  initial-value: value;
}
  • syntax: A string defining the permitted data types and structures.
  • inherits: A boolean defining whether child elements inherit this property from parent elements.
  • initial-value: The default fallback value for the property. This is required for almost all syntax definitions and must be valid according to the defined syntax.

Defining <color> Properties

To restrict a custom property to color values, set the syntax descriptor to '<color>'. When a property is registered as a <color>, the browser parses inputs as actual color data. This enables smooth color transitions and gradient animations that are otherwise impossible with unregistered custom properties.

@property --theme-accent {
  syntax: '<color>';
  inherits: false;
  initial-value: #3b82f6;
}

.box {
  background: linear-gradient(var(--theme-accent), #000);
  transition: --theme-accent 0.3s ease;
}

.box:hover {
  --theme-accent: #ef4444;
}

In this example, the browser interpolates --theme-accent mathematically between the initial blue and the hover red across the linear gradient.

Defining <length> and <length-percentage> Properties

The <length> type represents distance values, accepting units such as px, em, rem, vh, and vw. If a value should accept either absolute lengths or relative percentages, you can use the combined <length-percentage> type.

@property --card-padding {
  syntax: '<length>';
  inherits: true;
  initial-value: 16px;
}

@property --element-width {
  syntax: '<length-percentage>';
  inherits: false;
  initial-value: 100%;
}

By defining --card-padding as a <length>, invalid values (such as strings or unitless numbers other than zero) are rejected, and the browser falls back to 16px.

Supported Data Types and Combinators

The syntax string supports standard CSS types and combinations using specific operators:

  • Common types: <integer>, <number>, <percentage>, <angle>, <time>, <image>, <url>, and <transform-function>.
  • Alternatives (|): Allows multiple acceptable types. For example, '<color> | auto' allows either a valid color or the literal keyword auto.
  • Space-separated lists (+): Accepts one or more values separated by spaces. For example, '<length>+' allows values like 10px 20px.
  • Comma-separated lists (#): Accepts one or more values separated by commas. For example, '<color>#' allows a comma-separated sequence of colors.
  • Universal syntax ('*'): Accepts any valid token stream, matching the behavior of standard untyped custom properties.

JavaScript Equivalent: CSS.registerProperty

The exact same functionality can also be implemented through JavaScript using the CSS Houdini API:

window.CSS.registerProperty({
  name: '--dynamic-size',
  syntax: '<length>',
  inherits: false,
  initialValue: '0px',
});

Using either CSS @property or CSS.registerProperty ensures type safety, enables advanced keyframe animations, and guarantees predictable fallbacks throughout your stylesheets.