CSS Transforms Level 2: shorthand properties

Ever since I first dove into CSS Transforms I have often wished for one seemingly simple change: the ability to specify the transforms as their own properties. As I learned the syntax, I constantly made mistakes by directly using transform functions as CSS properties. Like this:

/*
 * Valid syntax for CSS Transforms Level 1
 */
.classic {
  transform: translate(45px) scale(1.333) rotate(45deg);
}

/*
 * Wrong syntax, but feels right
 */
.shorthand {
  translate: 45px;
  scale: 1.333;
  rotate: 45deg;
}

Shorthand needed

It seems I wasn't the only one subconsciously wanting this shortcut. Sometime after Lea Verou suggested that it's a quirk of the syntax and not the developer mindset, a small addition to the CSS Transforms Level 2 spec was developed: individual transform properties.

The spec is no longer draft

Although the spec is not 100% finalized, many browsers have shipped this to their stable channel. This new syntax can be easily tested today.

Example 1, which uses the same CSS as the first example code block.

Simple but reliable transforms

Using these properties means they are applied in a standard order defined by the spec. Separate properties make authoring easier on us because the order of transforms can affect the outcome. But these new properties are applied in an intuitive order no matter what order we author them. From the spec:

The translate, rotate, and scale properties allow authors to specify simple transforms independently, in a way that maps to typical user interface usage, rather than having to remember the order in transform that keeps the actions of transform(), rotate() and scale() independent and acting in screen coordinates.

You might have noticed this if you've ever applied multiple transforms in an order that doesn't apply them independently:

/*
 * Unintuitive order of transforms, first transform
 * affects later ones.
 */
.classic {
  transform: rotate(45deg) scale(1.333) translate(45px);
}

/*
 * Intuitive order of transforms, each transform
 * unaffected by the previous one.
 */
.classic-independent {
  transform: translate(45px) scale(1.333) rotate(45deg);
}

/*
 * Declarative syntax, so authoring order is ignored.
 */
.shorthand {
  rotate: 45deg;
  translate: 45px;
  scale: 1.333;
}

Example 2, comparing two transform syntaxes to shorthand.

In this case, the .classic transform rotates before translating, meaning the translation moves your element 45px to the right, but at an angle of 45 degrees. And it only becomes more complicated when there are animated 3D transforms involved...

Using with CSS Transitions and Animations

Its strength really shines through when used with CSS Transitions or CSS Animations, when we often want to animate only one aspect of a transformation.

/*
 * Animate using valid CSS Transforms Level 1 syntax.
 *
 * Must repeat the entire transformation instead of overwriting the specific
 * properties we are changing.
 */
.classic .transform {
  transform: translate(0, 0) scale(1.333) rotate3d(0, 1, 0, 45deg);
  animation:  classic 4s alternate infinite;
}

@keyframes classic {
  50% {
    transform: translate(0, 100px) scale(1.333) rotate3d(0, 1, 0, 45deg);
  }
}

Notice in the .classic CSS, we have to specify the entire transform every time we want to alter it. If any of the transform functions are left out, the transform will appear broken. Not so simple to spot the one number that changes between the two transform values, is it?

/*
 * CSS Transforms Level 2 syntax.
 *
 * Individual transform functions can be animated
 * without duplication of code!
 */
.shorthand .transform {
  scale: 1.333;
  transform: rotate3d(0, 1, 0, 45deg);
  animation: shorthand 4s alternate infinite;
}

@keyframes shorthand {
  50% {
    translate: 0 100px;
  }
}

With the shorthand, not only is there less duplication, but starting properties can be completely omitted since they naturally default to 0. That leaves us with a clean, unambiguous definition of our animation.

Example 3, classic, shorthand, and CSS Animations combined.

At the time of writing, the shorthand syntax for 3D rotation is not supported even in Chrome. But I took the opportunity to mix everything together. The third example uses transform: rotate3d() to set the rotation, and yet the shorthand syntax in the animation keyframes still intelligently applies the transforms in the proper order.

Feature detection

Modernizr now detects CSS Transforms Level 2, but you can also detect the feature using CSS Supports:

/*
 * Within your CSS files.
 */
@supports (translate: 45px) {
  translate: 45px;
}

Or if you'd rather use JavaScript, you've got a couple options:

// Use CSS Supports via JS.
window.CSS.supports('(translate: 45px)');

// Explicitly check the CSSStyleDeclaration for the property.
typeof(document.querySelector('body').style.translate) !== 'undefined';

Should I use this?

Maybe. Although many browsers support it in 2022, it won't work in older browsers at all.

But keep it in mind and someday simple transforms will be easier to author and more flexible to manipulate. Until then, just stick with the reliable syntax offered by the transform property.

End of post.