The WebKit blog details how to use individual CSS Transform properties in the latest version of Safari Technology Preview. This brings the browser in line with the CSS Transforms Module Level 2 spec, which breaks out the translate()
, rotate()
and scale()
functions from the transform
property into their own individual properties: translate
, scale
, and rotate
.
So, instead of chaining those three functions on the transform
property:
.some-element {
transform: translate(50px 50px) rotate(15deg) scale(1.2);
}
…we can write those out individually as their own properties:
.some-element {
translate: 50px 50px;
rotate: 15deg;
scale: 1.2;
}
If you’re like me, your mind immediately jumps to “why the heck would we want to write MORE lines of code?” I mean, we’re used to seeing individual properties become sub-properties of a shorthand, not the other way around, like we’ve seen with background
, border
, font
, margin
, padding
, place-items
, and so on.
But the WebKit team outlines some solid reasons why we’d want to do this:
- It’s simpler to write a single property when only one function is needed, like
scale: 2;
instead oftransform: scale(2);
. - There’s a lot less worry about accidentally overriding other
transform
properties when they’re chained together. - It’s a heckuva lot simpler to change a keyframe animation on an individual property rather than having to “pre-compute” and “recompute” intermediate values when chaining them with
transform
. - We get more refined control over the timing and keyframes of individual properties.
The post points out some helpful tips as well. Like, the new individual transform properties are applied first — translate
, rotate
, and scale
, in that order — before the functions in the transform
property.
Oh, and we can’t overlook browser support! It’s extremely limited at the time of writing… basically to just Safari Technology Preview 117 and Firefox 72 and above for a whopping 3.9% global support:
The post suggests using @supports
if you want to start using the properties:
@supports (translate: 0) {
/* Individual transform properties are supported */
div {
translate: 100px 100px;
}
}
@supports not (translate: 0) {
/* Individual transform properties are NOT supported */
div {
transform: translate(100px, 100px);
}
}
That’s the code example pulled straight from the post. Modifying this can help us avoid using the not
operator. I’m not sure that’s an improvement to the code or not, but it feels more like progressive enhancement to do something like this:
div {
transform: translate(100px, 100px);
}
@supports (translate: 0) {
/* Individual transform properties are supported */
div {
transform: none;
translate: 100px 100px;
}
}
That way, we clear the shorthand functions and make way for the individual properties, but only if they’re supported.
Having the separate properties is great, it probably will produce less code not more (think overrides that used to reproduce everything they don’t override) and help make code more modular. Why they needed yet another syntax is beyond me. What we gain without colons we lose in cognitive overhead.
My kingdom for a CSS syntax unification. Lists should be either comma separated or not. Slashes are not a list item delimiter. Properties start with a name, are defined with the value after a colon. Functions are functions and have a name and accept a (rule-following delimited) list of parameters. Nested structures have braces. Once all that exists, shorthand/longhand follows a simple rule: shorthand is sugar for longhand.
Chrome also supports individual transform properties behind the Experimental Web Platform features flag, which can be enabled from
chrome://flags
:I’m personally a bit mixed about their utility. I’d go for “really useful in some cases, but most of the times not an option”.
My biggest problem is they can only get applied in a certain order (and, save for some exceptions, transform order matters). Which is not the order I most often apply transform functions in. I normally use transforms to distribute elements in a logical pattern in 2D or 3D. This means starting with one or more rotations and only then applying a translation. And here we also have the issue that, when I distribute items in 3D, I normally first have a
rotatey()
, then arotatex()
and this only allows for a single rotation. And of course there’s the possibility of filling out the matrices, multiplying them, getting the trace and computing everything else that’s needed to combine them into a singlerotate3d()
. But that’s a pain.However, one case where I find them useful is where I only have a single rotation and a uniform scale that I want to animate and I need different timing functions for the two. Like in the case of this cube animation or for these triangular openings.