Using the CSS properties and values API

Baseline 2024

Newly available

Since July 2024, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

The CSS Properties and Values API — part of the CSS Houdini umbrella of APIs — allows the registration of CSS custom properties, allowing for property type checking, default values, and properties that do or do not inherit their value.

Registering a custom property

Registering a custom property allows you to tell the browser how the custom property should behave; what types are allowed, whether the custom property inherits its value, and what the default value of the custom property is. There are two ways to register a property, in JavaScript or in CSS.

CSS.registerProperty

The following will register a custom property named --my-prop using CSS.registerProperty. --my-prop will use the CSS color syntax, it will have a default value of #c0ffee, and it will not inherit its value:

js
window.CSS.registerProperty({
  name: "--my-prop",
  syntax: "<color>",
  inherits: false,
  initialValue: "#c0ffee",
});

@property

The same registration can take place in CSS. The following will register a custom property named --my-prop using the @property at-rule. --my-prop will use the CSS color syntax, it will have a default value of #c0ffee, and it will not inherit its value:

css
@property --my-prop {
  syntax: "<color>";
  inherits: false;
  initial-value: #c0ffee;
}

Using registered custom properties

One of the advantages of registering a property is that the browser now knows how to handle your custom property through things like transitions! When a property isn't registered, the browser doesn't know how to treat it, so it assumes that any value can be used and therefore can't animate it. When a property has a registered syntax, though, the browser can optimize around that syntax, including being able to animate it!

In this example, the custom property --registered has been registered using the syntax <color> and then used in a linear gradient. That property is then transitioned on hover or focus to a different color. Notice that the transition works with the registered property but not the unregistered one!

HTML

html
<button class="registered">Background Registered</button>
<button class="unregistered">Background Not Registered</button>

CSS

css
.registered {
  --registered: #c0ffee;
  background-image: linear-gradient(to right, #fff, var(--registered));
  transition: --registered 1s ease-in-out;
}

.registered:hover,
.registered:focus {
  --registered: #b4d455;
}

.unregistered {
  --unregistered: #c0ffee;
  background-image: linear-gradient(to right, #fff, var(--unregistered));
  transition: --unregistered 1s ease-in-out;
}

.unregistered:hover,
.unregistered:focus {
  --unregistered: #b4d455;
}

button {
  height: 40vh;
  display: block;
  width: 100%;
  font-size: 3vw;
}

JavaScript

js
window.CSS.registerProperty({
  name: "--registered",
  syntax: "<color>",
  inherits: false,
  initialValue: "red",
});

Result

While not functionally accurate, a good way to think about the difference between the unregistered property in the above example and the registered property is the difference between a <custom-ident> and a number when trying to animate height. You cannot transition or animate from auto to a number because the browser doesn't know the value of auto until it's calculated. With an unregistered property, the browser likewise doesn't know what the value may be until it's calculated, and because of that, it can't set up a transition from one value to another. When registered, though, you've told the browser what type of value it should expect, and because it knows that, it can then set up the transitions properly.

Gotchas

There are two gotchas when registering a property. The first is that, once a property is registered, there's no way to update it, and trying to re-register it with JavaScript will throw an error indicating it's already been defined.

Second, unlike standard properties, registered properties aren't validated when they're parsed. Rather, they're validated when they're computed. That means both that invalid values won't appear as invalid when inspecting the element's properties, and that including an invalid property after a valid one won't fall back to the valid property. An invalid property will, however, fall back to its registered default.

Browser compatibility

BCD tables only load in the browser