Menu

CSS Transform: translate, scale, skew and transform-origin

The transform property moves, scales, rotates and skews an element without changing the layout around it. Learn every transform function, why the order of functions matters, transform-origin, the separate translate, scale and rotate properties, and the translate(-50%, -50%) centering trick.

This page includes runnable editors - edit, run, and see output instantly.

What transform does

transform changes how an element is drawn: moved, scaled, rotated or slanted. The dashed outlines below show where each box would be without its transform:

translate(20px, 10px) moves the blue box 20px along and 10px down. scale(1.3) makes the green one 30% bigger around its center. rotate(20deg) turns the amber one clockwise, and skewX(-20deg) slants the dark one. In every case the dashed slot, which is the space the element takes in the layout, stays exactly where it was.

Transform functions

FunctionDoesExample
translate(x, y)movestranslate(20px, -10px), translate(-50%, -50%)
translateX(x) / translateY(y)moves on one axistranslateY(-4px)
scale(s) / scale(x, y)resizes (1 = original, 0.5 = half, negative mirrors)scale(1.1), scale(-1, 1)
scaleX(s) / scaleY(s)resizes on one axisscaleX(0.5)
rotate(angle)turns clockwise (negative = counterclockwise)rotate(45deg), rotate(0.25turn)
skew(x, y) / skewX(a) / skewY(a)slantsskewX(-15deg)
matrix(a, b, c, d, e, f)all of the above in one matrix, mostly what the browser reports back
noneno transform

Percentages in translate() are relative to the element's own size, not its parent's. Rotation has its own page with degrees, turns and spinning: CSS rotate.

Transform playground

Move the sliders. The first line is the transform value you built; the second is the matrix the browser computed from it, which is what getComputedStyle returns for any transform:

Transforms do not move the layout

A transformed element is drawn somewhere else, but the page is laid out as if it were not transformed. Its neighbors keep their positions. This is what makes transforms good for hover effects and animation: nothing around them reflows.

Both middle boxes look 1.5 times bigger. The scaled one overlaps its neighbors and C stays put; the one made bigger with width and height pushes C 40px further along.

Multiple transforms and their order

Put several functions in one transform, separated by spaces. The order changes the result, because, read from left to right, each function works in the coordinate system left behind by the functions before it:

The blue box moved 120px straight along and was then turned in place. The green box was turned first, so its "x axis" points diagonally, and translateX(120px) moves it down the diagonal. Same functions, different place.

The other trap: a second transform declaration replaces the first. If a card has transform: rotate(-2deg) and its hover rule says transform: scale(1.05), the rotation disappears on hover. Repeat both (rotate(-2deg) scale(1.05)), or use the individual properties below.

transform-origin

Scaling, rotating and skewing happen around a fixed point, the transform origin, which is the center by default. Change it with keywords, lengths or percentages:

All three boxes turn 25 degrees. The first pivots on its middle, the second keeps its top-left corner pinned to the slot's corner, the third its bottom-right corner. A dropdown menu that grows out of its button uses transform-origin: top with scale().

The translate, scale and rotate properties

Modern browsers also have three separate properties that do the same thing as the functions, but can be set and animated independently:

.card {
  rotate: -2deg;
  transition: scale 0.2s ease-out;
}
.card:hover {
  scale: 1.05;   /* the rotation stays, because it is a different property */
}

They always combine as if written transform: translate(...) rotate(...) scale(...) in front of any transform value, whatever order you declare them in. Note the syntax: translate: 10px 20px and scale: 1.2 take values without the function parentheses.

Centering with translate(-50%, -50%)

Because translate() percentages refer to the element's own size, top: 50%; left: 50%; transform: translate(-50%, -50%) centers an absolutely positioned element of any size in its parent. The center a div page compares it with flexbox and grid.

Common mistakes

  • Two transform declarations. The later one wins entirely. Combine the functions, or use the rotate, scale and translate properties.
  • Transforming an inline element. A plain <span> or <a> ignores transform. Make it inline-block.
  • Expecting neighbors to move. Transforms never change the layout. To make space, change width, margin or gap.
  • Wrong function order. rotate() before translate() moves along the rotated axis.
  • Surprises with position: fixed children. A transformed element becomes the containing block for fixed-position descendants, so a fixed header inside it scrolls with it. It also creates a new stacking context for z-index.

Frequently Asked Questions

What does transform do in CSS?

It changes how an element is drawn: moved (translate), resized (scale), turned (rotate) or slanted (skew). The element keeps its original place in the layout, so the elements around it do not move.

How do I apply multiple transforms in CSS?

List them in one transform value separated by spaces: transform: translateX(50px) rotate(20deg) scale(1.2);. Writing a second transform declaration replaces the first one instead of adding to it.

Does the order of transform functions matter?

Yes. Read from left to right, each function works in the axes the previous ones left behind, so rotate(45deg) translateX(100px) moves the element along a rotated axis, while translateX(100px) rotate(45deg) moves it straight and then turns it.

What is transform-origin?

The fixed point that scaling, rotating and skewing happen around. The default is the center (50% 50%). transform-origin: top left makes an element grow from, or turn around, its top-left corner.

Why does transform not work on my span?

Transforms do not apply to non-replaced inline elements such as a plain <span> or <a>. Give the element display: inline-block or block and the transform works.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED