Shots in this scene
Transforms for Motion
In one line
translate, scale and rotate move, resize and turn an element without changing the page layout. The order of the functions changes the result, the individual properties have a fixed order, and a few elements (inline boxes, SVG shapes) behave differently.
Think of it like this
A photo lying on a desk. You can slide it, turn it or move it nearer without changing what is printed on it, and the other things on the desk stay where they are. A CSS transform does this to an element: the page is laid out as if nothing happened, then the element is drawn moved, turned or scaled. (The CSS course's Topic 10.2 introduced the functions. Here we go deeper, because transforms are what animations move.)
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- transform
- A CSS property that moves, turns, resizes or slants an element's picture without changing the page layout.
- transform function
- One step of a transform list, such as
translateX(60px)orrotate(45deg). - transform-origin
- The point that rotating and scaling pivot around. The middle of the element by default.
- layout
- The browser's work of deciding where every element goes and how big it is. Transforms skip it.
- inline box
- An element that flows inside a line of text, like a
<span>or<a>. It has no box of its own to transform. - transform-box
- For SVG shapes: which box the origin refers to.
view-boxis the whole drawing (the default),fill-boxis the shape itself.
Step by step
01Two lists, two results
Press Go in the first example. Box A (translate, then rotate) slides straight right and turns. Box B (rotate, then translate) ends lower. Same two functions, written in the other order.
Pause, slow it to 0.1× and step to 350 ms: A is at x 30, y 0. B is at x 27.7, y 11.5. B leaves the straight line at once, because the turn bends the direction its translation points.
02Separate properties are separate tracks
Box C uses translate and rotate. On the timeline it has two tracks, translate and rotate, where A and B have one transform track each. Each property is animated on its own and ends in the same place as A.
A transition names the property it animates. transition: transform does not cover translate: change translate and the box jumps (the first break-it).
03The origin decides where it pivots
In the second example press Rotate 45°. The "centre" pad turns on the spot. The "corner" pad has transform-origin: 0 0, so it swings around its top-left corner and leans into its neighbour.
04Some things cannot be transformed
The page logs inl moved by 0 px for the inline span and blk moved by 40 px for the same span made inline-block. Same transform, different box type.
In the SVG, the first square (default origin) rotates around the top-left corner of the drawing and is mostly cut off. The second (fill-box) turns in place.
Try it yourself
- 1
Follow the curve
In "Order matters", press Go, pause, set 0.1× and step to about 350 ms. Read A: (30, 0). Read B: (27.7, 11.5). Step on to the end: A is at (60, 0), B at (42.4, 42.4). B's track is the same length and the same easing as A's. Only the order of its functions differs.
- 2
Make A behave like B
In the CSS, change
.far #atotransform: rotate(45deg) translateX(60px);. Press Run, then Go. Box A now takes B's route and ends at (42.4, 42.4). The order of the two words was the whole difference. - 3
Pivot around a corner
In the second example, delete
#o2 { transform-origin: 0 0; }and press Rotate 45° again. Both pads now turn on the spot, and the overlap is gone.
Code & diagrams
Press Go, then pause and step. A and C end in the same place; B ends 42.4 px lower. C is built from the individual properties, so it has two tracks.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet./* the same result: translate, then rotate, whatever order you write them in */
.a { translate: 60px 0; rotate: 45deg; }
.b { rotate: 45deg; translate: 60px 0; }
/* the transform list is applied after them: scale(2) here is the last step */
.c { rotate: 45deg; translate: 60px 0; transform: scale(2); }The log is the same on every run. Press Rotate 45° and compare the two pads and the two squares.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
inl moved by 0 px
blk moved by 40 pxBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Change translate, transition only transform
In "Order matters", change the transition line to transition: transform 700ms ease-in-out; (delete the translate and rotate parts). Press Run, then Go.
Break #2
Transform an inline element
In the second example, delete the rule #blk { display: inline-block; }. Press Run.
Myth vs fact
Myth
translateX(60px) rotate(45deg) and rotate(45deg) translateX(60px) are the same.
Fact
They are not. Each function works in the space the earlier ones made, so rotating first turns the direction of the move. Box B ends 42.4 px lower than box A.
Myth
A transform moves the element, so the layout updates around it.
Fact
The layout box never moves. Neighbours stay where they were, and the old space stays empty. Only the drawing (and where clicks land) moves.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Function lists with matching functions interpolate function by function:
translateX(0) rotate(0)totranslateX(60px) rotate(45deg)moves in a line while turning. If the lists do not match (different functions or order), the browser falls back to interpolating whole matrices, which can look odd. Keep the same functions in the same order in every state you animate between. - ▸
Transforms are hit-tested as drawn, so a button moved with
translateis clickable in its new place, and a button that slides away withscale(0)is not. Pair a hiding transform withvisibilityorinertso a hidden element cannot be focused with the keyboard. - ▸
The layout box not moving also means scrollable overflow still counts transformed content, and a
translateX(100%)panel off the right edge can create a horizontal scrollbar. Useoverflow: clipon the parent, or a transform on a fixed-position panel.
Remember this
- 1
The problem this solves: to move something you could change
left,toporwidth. Each of those changes the layout, so the browser must work out the position of other elements again on every frame. A transform leaves the layout alone and only changes how the finished element is drawn, which is far cheaper (Phase 6 measures it). It is the property most animations should move. - 2
The functions.
translate(x, y)moves (translateX(60px)moves right).scale(1.5)resizes around the origin point.rotate(45deg)turns clockwise.skew()slants. You write them in thetransformproperty, one after another, as a list:transform: translateX(60px) rotate(45deg). - 3
Order matters. Each function works in the coordinate system the functions before it made. In
translateX(60px) rotate(45deg)the element moves 60 px right, then turns on the spot. Inrotate(45deg) translateX(60px)it turns first, which turns its own x-axis, so 60 px "to the right" now points down and to the right. The first box (A) ends 60 px to the right. The second (B) ends 42.4 px right and 42.4 px down. Try both in the first example. - 4
The individual properties.
translate,rotateandscaleare properties of their own, sotranslate: 60px 0; rotate: 45degworks without a list. The browser always applies them in the order translate, rotate, scale, and only then thetransformlist, however you write them. That makes them safe to animate separately: one animation can movetranslatewhile another turnsrotate(Topic 4.5 uses this). They are Baseline widely available (MDN, checked October 2026: since August 2022, about Chrome 104, Firefox 72, Safari 14.1). - 5
The origin. Rotating and scaling happen around a point,
transform-origin, which is the middle of the element by default.transform-origin: 0 0pivots around the top-left corner instead. Percentages intranslateare percentages of the element itself:translateX(100%)moves a 30 px box 30 px and a 200 px box 200 px. That is whytranslateX(-100%)hides a panel just off its own left edge, whatever its width. - 6
Where transforms do not work. An inline box such as a
<span>in a line of text ignores transforms: it has no box of its own to move. Give itdisplay: inline-block. SVG shapes behave differently: their default origin is the top-left of the whole SVG, not the shape's middle, so a rotated shape swings away.transform-box: fill-boxplustransform-origin: centerbrings the origin back to the shape. - 7
What a transform does not change. The layout box stays where it was, so neighbours do not move. Clicks, though, follow what you see: the moved element receives them in its new place. A transformed element also becomes a stacking context and the containing block for
position: fixedchildren.
Explain it without notes
Why is a transform cheaper to animate than left?
What is the difference between translateX(60px) rotate(45deg) and rotate(45deg) translateX(60px)?
Why does a rotated SVG <rect> sometimes swing out of its drawing, and how do you fix it?
Practice
Make a card slide in from the right and tilt back to straight: it starts at translate: 40px 0; rotate: 4deg and ends at none. Use the individual properties and a transition on both.
Make an arrow icon (a <span>) rotate 90° when a menu opens. It currently ignores the transform. Explain why and fix it.
Done when you can
You can say where the box ends for any list of translate and rotate, in either order
You know the individual properties apply in the order translate, rotate, scale, then transform
You can change the origin, and you know it is the middle by default
You know inline boxes ignore transforms and how SVG shapes differ