Finer grained control over CSS transforms with individual transform properties

Transform elements with the translate, rotate, and scale properties

The CSS transform property

To apply transforms to an element, use the CSS transform Property. The property accepts one or more <transform-function>s which get applied one after the other.

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

The targeted element is translated by 50% on the X-axis, rotated by 30 degrees, and finally scaled up to 120%.

While the transform property does its work just fine, it becomes a bit tedious when you want to alter any of those values individually.

To change the scale on hover, you must duplicate all functions in the transform property, even though their values remain unchanged.

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

The individual transform properties

Shipping with Chrome 104 are individual properties for CSS transforms. The properties are scale, rotate, and translate, which you can use to individually define those parts of a transformation.

By doing so, Chrome joins Firefox and Safari which already support these properties.

Browser Support

  • 104
  • 104
  • 72
  • 14.1

Source

Rewriting the preceding transform example with the individual properties, your snippet becomes this:

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

Order matters

One key difference between the original CSS transform property and the new properties is the order in which the declared transformations get applied.

With transform, the transformation functions get applied in the order they’re written–from left (outside) to right (inside).

With the individual transformation properties, the order is not the order in which they are declared. The order is always the same: first translate (outside), then rotate, and then scale (inside).

That means both of the following code snippets give the same result:

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

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

In both cases the targeted elements will first be translated by 50% on the X-axis, then rotated by 30deg, and finally scaled by 1.2.

If one of the individual transform properties are declared along with a transform property, then the individual transforms get applied first (translate, rotate, and then scale) with the transform last (inside). More details are in the spec that defines how the transformation matrix should be calculated.

Animations

The main reason these properties were added is to make animations easier. Say you want to animate an element as follows:

Keyframes graph.

Using transform

To implement this animation using transform, you’d have to calculate all in-between values for all defined transformations, and include those in each keyframe. For example, to do a rotation at the 10% mark, the values for the other transformations must be calculated as well, because the transform property needs all of them.

Keyframes graph with intermediate values calculated.

The resulting CSS code becomes this:

@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;
}

Using individual transform properties

With individual transform properties this becomes much easier to write. Instead of dragging all transformations from keyframe to keyframe, you can target each transform individually. You also no longer need to calculate all those in-between values.

@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;
}

Using individual transform properties and several keyframes

To make your code modular you can split up each sub-animation into its own set of keyframes.

@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;
}

Thanks to this split you can apply each separate set of keyframes as you like because the transform properties–which now have become individual properties–no longer overwrite each other. Above that you can give each transformation a different timing without needing to rewrite the whole lot.

Performance

Animations using these new properties are just as efficient as animations of the existing transform property.

Animations of translate, rotate, and scale run on the compositor the same way that animations of transform do, so they’re good for animation performance in the same way that transform is.

These new properties also work with the will-change property. In general, it’s best to avoid overusing will-change by using it on the minimum number of elements needed, and for as short an amount of time as reasonably possible. But it’s also good to be as specific as possible. For example, if you’re using will-change to optimize an animation with the rotate and filter properties, you should declare this using will-change: rotate, filter. This is slightly better than using will-change: transform, filter in a case where you’re animating rotate and filter, because some of the data structures that Chrome creates in advance when you use will-change are different for transform versus rotate.

Part of the Newly interoperable series