Individual transform properties in modern CSS

For a long time, the CSS transform property was the only way to apply transformations to an element. It accepts one or more <transform-function> values, which are applied sequentially:

.target {
  transform: translateX(50%) rotate(30deg) scale(1.2);
}

In this example, the element is translated by 50% on the X-axis, rotated by 30 degrees, and then scaled to 120%. That works, but it creates friction the moment you need to change only one value. For instance, altering the scale on hover means duplicating the entire transform declaration, even though the translation and rotation values haven't changed:

.target:hover {
  transform: translateX(50%) rotate(30deg) scale(2); /* Only the value of scale() changed */
}

Chrome 104 brings a remedy: the individual transform properties translate, rotate, and scale. Firefox and Safari already support these, and with Chrome's release they are now interoperable across the major engines.

The earlier example can be rewritten using these dedicated properties:

.target {
  translate: 50% 0;
  rotate: 30deg;
  scale: 1.2;
}

Fixed application order

A crucial difference between the transform property and the individual properties is the order of operations. With transform, the functions apply from left to right, exactly as written.

The individual properties, however, always apply in the same sequence regardless of source order: translate first (outermost), then rotate, then scale (innermost). Consequently, the following two snippets produce identical results:

.transform--individual {
  translate: 50% 0;
  rotate: 30deg;
  scale: 1.2;
}

.transform--individual-alt {
  rotate: 30deg;
  translate: 50% 0;
  scale: 1.2;
}

Both code blocks apply a 50% translation on the X-axis, a 30-degree rotation, and a 1.2 scale factor. If you mix the individual properties with a transform declaration, the individual transforms execute first in that fixed order, and the transform functions are layered on top as the final operation. The specification details the transformation matrix calculation.

Easier animation workflows

The primary motivation for these properties is simplifying complex animations.

Animation with transform

Using transform for multi-stage animations forces you to calculate intermediate values for every transform function at each keyframe. To rotate an element only at the 10% mark, you still have to specify the translation and scale values for that keyframe, because the transform property requires the complete list.

@keyframes anim {
  0% { transform: translateX(0%); }
  5% { transform: translateX(5%) rotate(90deg) scale(1.2); }
  10% { transform: translateX(10%) rotate(180deg) scale(1.2); }
  90% { transform: translateX(90%) rotate(180deg) scale(1.2); }
  95% { transform: translateX(95%) rotate(270deg) scale(1.2); }
  100% { transform: translateX(100%) rotate(360deg); }
}

.target {
  animation: anim 2s;
  animation-fill-mode: forwards;
}

Animation with individual properties

The individual properties remove that burden. Each transform can be targeted on its own, eliminating the need to carry forward unrelated values across keyframes or compute in-between values manually:

@keyframes anim {
  0% { translate: 0% 0; }
  100% { translate: 100% 0; }

  0%, 100% { scale: 1; }
  5%, 95% { scale: 1.2; }

  0% { rotate: 0deg; }
  10%, 90% { rotate: 180deg; }
  100% { rotate: 360deg; }
}

.target {
  animation: anim 2s;
  animation-fill-mode: forwards;
}

Splitting into modular keyframes

The gains scale further when you split a compound animation into separate keyframe sets. Because the individual properties don't overwrite one another, you can define a distinct keyframe animation for translate, rotate, and scale:

@keyframes move {
  0% { translate: 0% 0; }
  100% { translate: 100% 0; }
}

@keyframes scale {
  0%, 100% { scale: 1; }
  5%, 95% { scale: 1.2; }
}

@keyframes rotate {
  0% { rotate: 0deg; }
  10%, 90% { rotate: 180deg; }
  100% { rotate: 360deg; }
}

.target {
  animation: move 2s, scale 2s, rotate 2s;
  animation-fill-mode: forwards;
}

This setup lets you apply each sub-animation selectively, assign a different timing function to each transform, and update one without rewriting the others.

Performance parity

Animating translate, rotate, and scale is just as fast as animating transform. Both run on the compositor, giving you the same animation performance benefits.

These properties also work with will-change. When optimizing, it's best to name the individual property you're animating rather than lumping it under transform. For example, if you're animating rotate and filter, using will-change: rotate, filter is preferable to will-change: transform, filter, since Chrome constructs different internal data structures for transform versus rotate ahead of time.