https://www.joshwcomeau.com/css/transforms/
JoshWComeau
* Latest
* Posts
* Snippets
* Goodies
HomeTutorialsCSS
The World of CSS Transforms
Table of Contents
IntroductionTransform functionsTranslationScaleRotateSkewTransform
originCombining multiple operationsInline elementsThe third dimension
Going deeper
Introduction
Like so many things in CSS, the transform property is surprisingly
remarkable.
At first glance, it may seem like a pretty niche thing. How often do
we need to rotate or skew something, after all? And yet, the more I
learn about transform, the more I find myself taking advantage of it.
In my blog's codebase, I've used the transform property more than 350
times!
In this blog post, we're diving deep into the transform property.
I'll show you some of the cool and unexpected things you can do with
it!
Intended audience
This tutorial is intended to be beginner-friendly, though I do assume
that you're already comfortable with the fundamentals of HTML/CSS.
Unless you're a seasoned CSS expert, I bet you'll learn something
neat!
Link to this heading
Transform functions
The transform property can do a whole bunch of different things,
through the use of transform functions like translate and skew.
Let's look at each in turn.
Link to this heading
Translation
Translation allows us to move an item around:
transform: translate(0px, 0px);
x:0px
[0 ]
y:0px
[0 ]
We can use translate to shift an item along in either axis: x moves
side to side, y moves up and down. Positive values move down and to
the right. Negative values move up and to the left.
Critically, the item's in-flow position doesn't change. As far as our
layout algorithms are concerned, from Flow to Flexbox to Grid, this
property has no effect.
For example: in this visualization, we have 3 children aligned using
Flexbox. When we apply a transform to the middle child, the Flexbox
algorithm doesn't notice, and keeps the other children in the same
place:
transform: translate(0px, 0px);
x:0px
[0 ]
y:0px
[0 ]
This is similar to how top / left / right / bottom work in positioned
layout, with relatively-positioned elements.
When we want to move an element along a single axis, we can use
translateX and translateY:
css
There's one thing that makes translate ridiculously powerful, though.
Something totally unique in the CSS language.
When we use a percentage value in translate, that percentage refers
to the element's own size, not the available space within the parent
container.
For example:
transform: translate(0%, 0%);
x:0%
[0 ]
y:0%
[0 ]
Setting transform: translateY(-100%) moves the box up by its exact
height, no matter what that height is, to the pixel.
This is incredibly handy when we want an element to sit just outside
another one:
Code Playground
Format code using Prettier
Reset code
HTMLCSS
[
Result
Refresh results pane
[ ]
Enable 'tab' key
Why is this so cool?
You might be wondering why this is such a big deal. After all, the
above layout could be built with Flexbox or Grid, right? If we set
.parent and .child to be siblings?
Well, not quite.
Show more
A common usecase for this trick is to add a "close" button just
outside a dialog box:
Code Playground
Format code using Prettier
Reset code
HTMLCSS
[
Result
Refresh results pane
[ ]
Enable 'tab' key
With the magic of calc, we can even mix relative and absolute units:
transform: translateX(calc(0% + 0px));
Percentage:0%
[0 ]
Pixels:0px
[0 ]
This allows us to add a "buffer", so that we can translate something
by its own size plus a few extra pixels.
Link to this heading
Scale
Alright, let's look at another transform function!
scale allows us to grow or shrink an element:
transform: scale(1);
Ratio:1
[1 ]
Scale uses a unitless value that represents a multiple, similar to
line-height. scale(2) means that the element should be 2x as big as
it would normally be.
We can also pass multiple values, to scale the x and y axis
independently:
transform: scale(1, 1);
x:1
[1 ]
y:1
[1 ]
At first glance, this might seem equivalent to setting width and
height, but there's one big difference.
Check out what happens when our element has some text in it:
Hello World
transform: scale(1);
Ratio:1
[1 ]
The text scales up and down with the element. We aren't just
transforming the size and shape of the box, we're transforming the
entire element and all of its descendants.
A different mechanism
This reveals an important truth about transforms: elements are
flattened into a texture. All of these transforms essentially treat
our element like a flat image, warping and contorting it as you might
in Photoshop.
Incidentally, this is what makes transform such a good choice for
animations!
Think about how much work is required when we change something like
width. All of the layout algorithms need to re-run, figuring out
exactly where this element and all of its siblings should be. If the
element has text inside, the line-wrapping algorithm needs to figure
out if this new width affects the line breaks. Then, the paint
algorithms run, figuring out which color every pixel needs to be, and
filling it in.
It's fine to do this once when the page loads, but when we animate
something, we need to do all of those calculations many many times a
second. With transforms, we can skip a bunch of steps. This means
that the calculations run quicker, leading to smoother motion.
It may seem like a bummer that scale will stretch/squash the
element's contents, but we can actually use this effect to our
advantage. For example, check out this old-timey TV power animation:
Old-timey black-and-white video, showing people walking in a city
transform: scale(1, 1);
filter: brightness(100%);
Power status:
(*)On( )Off
For this animation, the squashing effect actually improves the
effect!
And, if we really don't want our text to squash, we can apply an
inverse transform to the child.
This is an advanced technique, far beyond the scope of this blog
post, but know that it's possible to use scale to increase an
element's size without distorting its children. Libraries like Framer
Motion take advantage of this fact to build highly-performant
animations without stretching or squashing.
Link to this heading
Rotate
You guessed it: rotate will rotate our elements:
transform: rotate(0deg);
Rotation:0deg
[0 ]
We typically use the deg unit for rotation, short for degrees. But
there's another handy unit we can use, one which might be easier to
reason about:
transform: rotate(0turn);
Rotation:0turn
[0 ]
The turn unit represents how many turns the element should make. 1
turn is equal to 360 degrees.
It's obscure, but well-supported; the turn unit goes all the way back
to IE 9!
Link to this heading
Skew
Finally, skew is a seldom-used but pretty-neat transformation:
Hello World
transform: skew(0deg);
Skew:0deg
[0 ]
As with translate, we can skew along either axis:
Hello World
transform: skewX(0deg);
Rotation:0deg
[0 ]
Axis:
(*)X( )Y
Skew can be useful for creating diagonal decorative elements (a la
Stripe). With the help of calc and some trigonometry, it can also be
used on elements without distorting the text! This technique is
explored in depth in Nils Binder's awesome blog post, "Create
Diagonal Layouts Like It's 2020".
Link to this heading
Transform origin
Every element has an origin, the anchor that the transform functions
execute from.
Check out how rotation changes when we tweak the transform origin:
transform: rotate(0deg);
transform-origin: center;
Rotation:0deg
[0 ]
Transform Origin:
[center (default)]
center (default)
Show Origin:
(*)true( )false
The transform origin acts as a pivot point!
It isn't exclusive to rotation, either; here's how it affects scale:
transform: scale(1);
transform-origin: center;
Scale:1
[1 ]
Transform Origin:
[center (default)]
center (default)
Show Origin:
(*)true( )false
This is useful for certain kinds of effects (for example, an element
"growing out of" another one).
Link to this heading
Combining multiple operations
We can string together multiple transform functions by
space-separating them:
transform: translateX(0px) rotate(0deg);
x:0px
[0 ]
Rotation:0deg
[0 ]
The order is important: the transform functions will be applied
sequentially. Check out what happens if we reverse the order:
transform: rotate(0deg) translateX(0px);
Rotation:0deg
[0 ]
x:0px
[0 ]
The transform functions are applied from right to left, like
composition in functional programming.
In the first demo, we rotate the element in its natural position, and
then translate it along the X axis.
In this second demo, however, we translate the element first. When we
apply the rotation, it rotates around its origin, which hasn't
changed.
Here's the same demo, but with the origin shown:
transform: rotate(0deg) translateX(0px);
transform-origin: center;
Rotation:0deg
[0 ]
x:0px
[0 ]
Transform Origin:
[center (default)]
center (default)
We can use this to our advantage:
Code Playground
Format code using Prettier
Reset code
HTMLCSS
[
Result
Refresh results pane
[ ]
Enable 'tab' key
In this example, we start by positioning the moon in the dead center
of the planet. Our animation will shift it 80px to the right, and
then cause it to rotate in a circle. Because the moon's origin is
still in the center of the planet, it orbits around at a distance.
Try changing 80px in the from/to blocks to see how it affects the
animation!
Keyframe animations!
Interested in learning more about CSS keyframe animations? It just so
happens I'm working on a blog post about them! Subscribe to my
personal newsletter, and I'll let you know when it's published.
Link to this heading
Inline elements
One common gotcha with transforms is that they don't work with inline
elements in Flow layout.
Code Playground
Format code using Prettier
Reset code
HTMLCSS
[
Why
Hello
there!
Result
Refresh results pane
[ ]
Enable 'tab' key
Inline elements don't enjoy being jostled. Their goal is to wrap
around some content with as little disruption as possible. Transforms
aren't their cup of tea.
The easiest fix is to switch it to use display: inline-block, or to
use a different layout mode (eg. Flexbox or Grid).
Link to this heading
The third dimension
In addition to the 2D transforms we've covered in this tutorial, CSS
can transform elements in a third dimension!
3D transforms have their own quirks and idiosyncracies. In order to
do them justice, I'll be writing a separate post all about 3D
transforms. Stay tuned!
Link to this heading
Going deeper
I have a confession to make: this tutorial wasn't originally written
as a blog post. It's been ported from my upcoming CSS course:
[]
CSS for JavaScript Developers is a comprehensive multi-format course
with the goal of transforming your relationship with CSS.
The course is specifically created for folks who work with a JS
framework like React or Angular or Vue. We cover the fundamentals of
CSS, but within the context of the modern JS ecosystem.
It goes way deeper than my blog posts. There are over 150 videos, in
addition to dozens of exercises and projects.
I've been working on it full-time for over a year now. Almost 5000
people purchased it in an early crowdfunding round, and their
feedback has made the course so much better.
The course will be released on September 27th, and you can learn more
here: css-for-js.dev.
Last Updated
August 9th, 2021
Hits
A front-end web development newsletter that sparks joy
My goal with this blog is to create helpful content for front-end web
devs, and my newsletter is no different! It includes early previews
to upcoming posts and access to special bonus goodies. No spam,
unsubscribe at any time.
First Name
[ ]
Email
[ ]
Subscribe
JoshWComeau
Thanks for reading!
(c) 2020-present Joshua Comeau. All Rights Reserved.
Tutorials
ReactAnimationCSSCareerGatsbyNext.jsPerformance
Links
TwitterRSSContact
(c) 2020-present Joshua Comeau. All Rights Reserved.