The power of CSS Transforms

With a single line of CSS we can completely reorient any element on our page – we can move it, rotate it, resize it, or send it into another dimension (to an extent). This all comes to us via the transform property. While it is a single property, a lot of functionality is packed into it, and it can be confusing how all its values combine to create its end result. Let’s discuss some of the basics and then dive into a handful of use cases.

What can transform do?

The value for the property consists of one or more transformation functions that build on one another.

The translate Function

The most straightforward one to jump into is the translate() function since it is related to positioning and uses the length units you find in more common properties like margin and top.

.my-element {
transform: translate(5px, 10%);
}

Here our CSS will take the element as it is and shift it to the right 5px and down 10% of its size. The important piece to note is that before the transformation is applied the box is rendered according to the normal layout flow. Visually the element is moved to its new position, but it does not affect the flow of elements that appear later.

See the Pen Transforms and Flow by Dan Wilson (@danwilson) on CodePen.

Elements that appear inside will move with its parent, and you can have nested transforms too.

<div style="transform: translate(5px, 0)">
<div style="transform: translate(10px, 0)">Hello</div>
</div>

With this HTML, the inner div will appear 15px to the right of where its default position was.

You can use the functions translateX() and translateY() as well if you want to separate them out or only use one direction.

The scale Function

You can also make your elements bigger or smaller with the scale() function. It accepts a single number, where scale(1) represents no change. A number between 0 and 1 will be a fraction of the original size, and a larger number will be bigger than the default size.

transform: scale(1); //original size
transform: scale(2); //twice as big
transform: scale(0.5); //half size

You can also scale horizontally to stretch your element with scaleX() or vertically with scaleY(). Extra fun comes when you start throwing in negative numbers to get flips.

See the Pen Scale Options by Dan Wilson (@danwilson) on CodePen.

The rotate Function

Then there are rotations using angle units such as deg, rad, or turn. These all move in the clockwise direction for positive values and counterclockwise for negative values.

See the Pen Rotation Options by Dan Wilson (@danwilson) on CodePen.

The skew Function

There are even ways to skew an element. This uses a similar angle unit to the rotations inside the functions skewX() and skewY().

See the Pen SkewX/Y Options by Dan Wilson (@danwilson) on CodePen.

Once you have a handle on these key functions you can explore the 3D equivalents (such as translate3d())and perspective to get a level of depth. But there is plenty to dig into with just the 2D world.

Building out the full transform

The transform property does a lot already, but we have only focused on how to apply a single transformation. The property accepts multiple functions at a time, so you can apply both a scale and a rotation with transform: scale(2) rotate(20deg). You can even use the same functions multiple times, such as with transform: translateX(20px) rotate(.25turn) translateX(30px).

This structure grants us a fair amount of flexibility, but one of the first thoughts people often have is why do we cram everything in one property instead of having separate properties. The primary answer is that all of these transform functions boil down to matrix multiplication (a key piece of Linear Algebra and common in computer graphics). Order can affect the end visual result, so it makes sense to keep them inside the same property. If you check the browser’s current value of your transform through the JavaScript getComputedStyle(yourElement) it will be converted to a matrix() function (which is another function you can use if you are comfortable with matrices and how to work with them).

See the Pen Order of Transforms by Dan Wilson (@danwilson) on CodePen.

All of this being said, individual properties are in the pipeline and will provide a convenience when you only need to set one. They will follow a predefined order, though, so you lose some flexibility. When this spec is finalised and lands in browsers we will be able to break out simple transforms like this:

.element {
  scale: 2;
  rotate: 20deg;
}

Why use transform?

When we can already position and size items many other ways in CSS, why do we even need transforms as web developers? There are a few reasons, but the biggest is the ability to combine them effectively with transitions and animations.

The transforms spec first appeared in the age of the smartphone as users became used to views sliding in and other small transitions. Moving an element from one position to another had long been possible by using JavaScript and modifying the margin property or left and top repeatedly. It even became abstracted out and popularised in jQuery with the animate() method. But it was never guaranteed to be smooth and caused repainting and reflow as the styles and layout for the page need to be recalculated.

With transforms the original position stays the same, so the browser can optimise the animation as it doesn’t have to do the full recalculation. The transformed element moves in its own compositor layer that the browser can effectively treat separately. Combined with a CSS transition that specifies a duration for a transform (e.g. transition: transform 400ms) we can animate the element when we have a hover or we toggle a class via JavaScript:

a {
  display: inline-block;
  transition: transform 250ms;
}
a:hover {
  transform: translateY(10px);
}
a.important {
  transform: scale(1.2);
}

Now anytime the user hovers over a link, it will move down 10px over the course of 250 milliseconds. When we modify the class in JavaScript to include “important” we will see the link scale to 120% of its original size, also over the course of 250 milliseconds.

What happens when we hover over a link that has the “important” class? You might expect (or at least hope) that it will take the scaled up link and also move it down the 10px. But in fact there is no translation. The values inside the transform are not being appended to previous values, they are simply overriding them. Since the a.important rule comes last (and it has the same specificity as the a:hover rule) the “important” transform is the value used in this case.

In order to have the scaled up link also go down 10px on hover, your styles would need to be updated with:

a {
  display: inline-block;
  transition: transform 250ms;
}
a:hover {
  transform: translateY(10px);
}
a.important {
  transform: scale(1.2);
}
a.important:hover {
  transform: scale(1.2) translateY(10px);
}

This can get unwieldy pretty quickly if you have several variations and you want there to be any combination of them. This is where the upcoming addition to CSS transforms that includes the option to use individual properties (mentioned earlier) will simplify our code. However, this is a future thing, so we currently need to be explicit for every combination of transforms we expect.

Though, to be fair… that’s not entirely true if you want to venture into another newer feature of CSS with a lot of exciting possibilities and now in Firefox, Chrome, Safari, and Edge – CSS Variables! With the help of CSS Variables you can get the best of both worlds by specifying the transform order you want as well as break out properties more individually. This is beyond the scope of our current discussion, but it shows the evolving nature of transforms in the larger context of CSS.

In addition to using CSS transitions, we can also get smooth motion with transforms when used with CSS animations. Where transitions give you a straightforward mechanism to animate from a start state to an end state, animations allow you to get more complex. It takes two parts to do a CSS animation: an animation property on the element you want to animate and a @keyframes definition to specify what happens.

div.mover {
  animation: my-fancy-custom-animation 5000ms;
}

@keyframes my-fancy-custom-animation {
  0% {
    transform: translate(0px);
  }
  20% {
    transform: scale(.5);
  }
  50% {
    transform: rotate(120deg);
  }
  80% {
    transform: translateX(80px) rotate(120deg)
  }
  100% {
    transform: translateX(0px);
  }
}

There are many more options for the animation (related to delays, easing, iteration count, and more), but this shows a minimum amount to get an animation going where we specify the element to have an animation named my-fancy-custom-animation and a duration of 5000ms. We then define what this animation does by setting up a @keyframes ruleset. Here we state what should happen at the start, 20% mark, 50% mark, 80% mark, and end. The browser determines what happens in between, so from 0% to 20% it will animate from translate(0) (which would be no transform visually applied) to a scale of .5. Between 20% and 50% it transitions from that half scale to a 120deg rotation, and so forth.

It is important to note the same overriding behaviour applies in these @keyframes. When we go from just a scale to just a rotate we are effectively telling the browser to go from transform: scale(.5) rotate(0deg) to transform: scale(1) rotate(120deg) (since 0deg and 1 are the default values for rotate and scale, respectively).

See the Pen Transform animation example by Dan Wilson (@danwilson) on CodePen.

Keyframe animations and transitions are not just for transforms, but transforms benefit greatly from the ability to animate between different states more efficiently by the browser.

The best friend of transforms

We can do a lot with transforms, but we can expand our options further when we introduce a complementary property. The transform-origin allows us to change the origin of our transformation. By default everything happens from the centre of the element. However, we can change the origin to any other point, in the same way we might change a background’s position. This changes how scales, rotations, and skews occur. For a rotation it changes the default from rotating around the centre of the element to rotating around the new point.

See the Pen Animating transform-origin by Dan Wilson (@danwilson) on CodePen.

You can similarly affect the visual result for scaling by using a different origin point.

See the Pen Animating transform-origin (scale) by Dan Wilson (@danwilson) on CodePen.

But it’s not just about animation, right?

Animation can be a lot of fun, but transforms are certainly not limited to that realm. They can add design flourishes and lead to creative solutions, especially when combined with other CSS pieces like blend modes, clip paths, and more.

However, really anything that requires a scaling or a rotation or needs a new position without affecting other elements’ positions can get value from using transform.

Some specific examples for inspiration: What if we want to have a circular menu where each item is laid out along the edge of circle? Rotate each item and translate them out to get each into position.

li {
  transform: rotate(30deg) translateX(30vmin);
}
li:nth-child(2) {
  transform: rotate(60deg) translateX(30vmin);
}
/* etc. */

Without transforms, this would require specific calculations for each item to determine the x and y values for its position (which could then be combined with absolute positioning). If the circle’s dimensions or the number of items changed, the calculations would need to be reworked. With transforms we can take a more straightforward approach by specifying only the angle we want and the radius of the circle and let the browser do the calculation.

See the Pen Clock Setup by Dan Wilson (@danwilson) on CodePen.

Maybe you need something more basic like a frame around a photo?

See the Pen Picture Frame by Dan Wilson (@danwilson) on CodePen.

Or when you want to take transforms to the next level, take a look at some options we did not even get to touch on with 3D and the Z axis:

See the Pen Visual Reference: 3D Transform Functions by Dan Wilson (@danwilson) on CodePen.

Whether you want to simply position something visually or you want to dig in and create performant animations, the transform property can help get you there. This property encompasses a lot of options, so start exploring it, start playing with it, and see how it might help you.

Additional reading: