Transforms: translate, scale and rotate

Move, scale and rotate elements without affecting layout, use the individual transform properties, and control the origin.

  • Beginner
  • 7 min read
  • Updated 17 Oct 2026

What you will learn

  • Apply 2D transforms
  • Combine transforms in the right order
  • Use the individual translate, scale and rotate properties
  • Animate transforms smoothly

Transforms change how an element is drawn, moving, scaling or rotating it, without changing the layout around it. Neighbouring elements stay exactly where they were. That makes transforms ideal for hover effects and animation.

Transforms do not affect layout

CSS
.badge {
    transform: translateY(-4px);
}

The badge is drawn 4px higher, but the space it occupies in the layout does not move. Compare this with margin-top: -4px, which would pull the following content up too.

Because the layout is unaffected, browsers can usually animate transforms without recalculating the page, which is why they are the preferred way to animate movement.

Transforms apply to block-level and atomic inline elements such as inline-block, flex items and images. They have no effect on plain inline elements like a <span> in a sentence.

translate

translate() moves an element:

CSS
.a { transform: translateX(2rem); }        /* right */
.b { transform: translateY(-1rem); }       /* up */
.c { transform: translate(2rem, -1rem); }  /* both */

Percentages in translate() refer to the element's own size, not its parent's. translateX(100%) moves an element by exactly its own width, which is useful for sliding panels in from off-screen:

CSS
.drawer {
    position: fixed;
    inset-block: 0;
    inset-inline-end: 0;
    width: min(20rem, 90%);
    transform: translateX(100%);
    transition: transform 250ms ease-out;
}

.drawer.is-open {
    transform: translateX(0);
}

The classic centring trick

You will often see this in existing code:

CSS
.modal {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

top and left put the element's top-left corner in the middle; the translate pulls it back by half its own size. It works, but Grid's place-items: center is usually simpler today.

scale

scale() makes an element larger or smaller. 1 is normal size:

CSS
.thumbnail:hover {
    transform: scale(1.05);
}

.button:active {
    transform: scale(0.97);
}

.flip {
    transform: scaleX(-1);   /* mirror horizontally */
}

Scaling scales everything, including text and borders. A small scale on hover is fine; scaling text up a lot can look blurry during the animation, as browsers may render it as an image while it moves.

rotate

CSS
.chevron {
    transition: transform 200ms ease-out;
}

.disclosure[open] .chevron {
    transform: rotate(180deg);
}

Angles can be written in deg, turn (one full rotation is 1turn), or rad. Positive values rotate clockwise.

skew

skew() slants an element along an axis:

CSS
.slanted {
    transform: skewX(-10deg);
}

It is mostly used for decorative shapes. Skewing text reduces readability.

Combining transforms: order matters

You can list several functions in one transform. Read them from left to right: each one works in the coordinate system left behind by the one before it. So the order changes the result.

CSS
.one {
    transform: translateX(10rem) rotate(45deg);   /* moves right, then rotates in place */
}

.two {
    transform: rotate(45deg) translateX(10rem);   /* rotates first, then moves along the rotated axis: diagonally */
}

A second transform declaration replaces the first, it does not add to it:

CSS
.card {
    transform: rotate(-2deg);
}

.card:hover {
    transform: translateY(-4px);   /* the rotation is lost */
}

To keep both, repeat them: transform: rotate(-2deg) translateY(-4px);.

Individual transform properties

Modern CSS has separate translate, rotate and scale properties that solve that problem:

CSS
.card {
    rotate: -2deg;
    transition: translate 200ms ease-out, scale 200ms ease-out;
}

.card:hover {
    translate: 0 -4px;
    scale: 1.02;
}

Each property can be set, overridden and transitioned independently, so the hover rule no longer wipes out the rotation. Note the syntax: values are space-separated (translate: 0 -4px), not functions.

They are always applied in a fixed order, translate then rotate then scale, followed by anything in the transform property. If you need a different order, use transform.

The individual transform properties are supported in all current major browsers. In very old browsers they are ignored, so the element simply does not move, which is usually an acceptable fallback for a hover effect.

transform-origin

Rotation and scaling happen around a point, the origin. By default it is the centre of the element:

CSS
.door {
    transform-origin: left center;
    transition: rotate 300ms ease-out;
}

.door:hover {
    rotate: -8deg;   /* swings around its left edge */
}

.underline {
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 250ms ease-out;
}

a:hover .underline {
    transform: scaleX(1);
}

The underline grows from the left edge instead of from the middle. transform-origin accepts keywords (top, left, center), lengths and percentages.

A taste of 3D

Transforms also work in three dimensions. Add perspective to a parent so depth looks natural:

CSS
.scene {
    perspective: 800px;
}

.card-3d {
    transition: transform 400ms ease-out;
}

.scene:hover .card-3d {
    transform: rotateY(15deg);
}

Use 3D effects sparingly and always under a reduced-motion check. They are among the effects most likely to cause discomfort.

Side effects to know about

  • Any transform other than none creates a stacking context. It can change how z-index works inside the element.
  • A transformed element becomes the containing block for position: fixed descendants, so a "fixed" child scrolls with it.
  • The clickable area moves with the drawn element, not with the layout space it left behind.

These effects explain many puzzling bugs. If adding a hover transform breaks a dropdown or a fixed element, this is why.

Reduced motion

CSS
@media (prefers-reduced-motion: reduce) {
    .card:hover {
        translate: none;
        scale: none;
    }
}

Small hover movements are usually fine for most people, but large slides, zooms, spins and 3D rotations should be removed or replaced with a fade when the user asks for reduced motion.

Debugging transforms

  • Inspect the element: DevTools highlights the transformed box, while the layout space stays where it was.
  • If a transform has no effect, check that the element is not a plain inline element.
  • If one transform cancels another, switch to the individual properties.
  • In the Animations panel, slow the transition down to check the origin and direction.

Summary

  • Transforms change how an element is drawn without affecting layout around it.
  • translate() percentages refer to the element's own size; scale() uses 1 as normal; rotate() takes angles.
  • Functions in transform apply in order, and a later transform replaces an earlier one.
  • The individual translate, rotate and scale properties can be set and animated independently.
  • transform-origin sets the pivot point; transforms create stacking contexts and containing blocks.

Further reading