---
title: "Transition a custom property with @property"
description: "Use it when one state change should drive several values together, or when a script feeds a number such as pointer position or progress into CSS and the result should ease."
canonical: https://good-css.com/transition-a-custom-property-with-property.md
last-updated: 2026-11-05
---

# Transition a custom property with `@property`

Use it when one state change should drive several values together, or when a script feeds a number such as pointer position or progress into CSS and the result should ease.

```css
@property --progress {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

.card:focus-within { --progress: 1; }

@media (hover: hover) and (pointer: fine) {
  .card:hover { --progress: 1; }
}

.card .icon {
  display: inline-block;
  rotate: calc(var(--progress) * 90deg);
}

.card .bar {
  transform-origin: left;
  scale: calc(0.25 + var(--progress) * 0.75) 1;
}

.card .more { opacity: var(--progress); }

@media (prefers-reduced-motion: no-preference) {
  .card { transition: --progress 0.3s var(--ease-out, ease-out); }
}
```

Why it works:

- An unregistered custom property is a string, and a string cannot be interpolated. Registering it with a `syntax` gives it a type, and the browser then transitions it like any number.
- One transition with one easing drives every dependent value. The children derive theirs with `calc()`, so the parts stay in step.
- `inherits: true` passes the number down to the children that use it.

Rules:

- `initial-value` is required for every syntax except `"*"`.
- `rotate`, `scale` and `translate` do nothing on an inline element. Give the element `display: inline-block` or another box type first.
- A registration is global. Register each name once and keep the names specific.
- A script only writes the variable, as in `el.style.setProperty("--x", "40%")`. The easing and duration stay in CSS.
- Other useful types are `<length-percentage>` for a gradient position and `<angle>` for a conic gradient.

Support: Chrome 85, Firefox 128, Safari 16.4.

- Borrowed from: Stripe, on the stripe.com home page and its sales contact page
- Docs: [MDN: @property](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@property)
