CSS transform lets you move, rotate, resize, and distort elements visually — without affecting the document layout around them. It is one of the most important CSS properties for building modern UIs, and because it is GPU-accelerated, it is also one of the most performant ways to animate elements. This guide covers every transform function, how to combine them, and how to avoid the common mistakes.

How CSS transform works

The transform property applies a visual transformation to an element using one or more transform functions. The key thing to understand is that transform does not affect layout — the element still occupies its original space in the document. Neighbouring elements do not move or reflow when you transform something.

/* Basic syntax */
.element {
  transform: function(value);
}

/* Example */
.box {
  transform: translateX(50px);
}

Transforms are applied relative to the element's transform origin, which defaults to the centre of the element (50% 50%). You can change this with the transform-origin property.

translate — Move an Element

translate moves an element along the X and Y axes. Unlike changing top, left, or margin, translating an element does not trigger a layout recalculation — making it the correct way to move elements in animations.

/* Move right 50px */
transform: translateX(50px);

/* Move down 20px */
transform: translateY(20px);

/* Move right 50px and down 20px */
transform: translate(50px, 20px);

/* Move using percentages (relative to the element's own size) */
transform: translate(50%, -50%);

/* 3D translate */
transform: translateZ(100px);
transform: translate3d(50px, 20px, 100px);

Percentage values in translate are relative to the element itself, not the parent. This makes translate(-50%, -50%) the standard technique for centering an absolutely positioned element:

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

rotate — Spin an Element

rotate turns an element clockwise (positive values) or counter-clockwise (negative values) around its transform origin. The angle can be specified in degrees (deg), radians (rad), gradians (grad), or turns (turn).

/* Rotate 45 degrees clockwise */
transform: rotate(45deg);

/* Rotate 90 degrees counter-clockwise */
transform: rotate(-90deg);

/* One full rotation */
transform: rotate(1turn);

/* 3D rotations */
transform: rotateX(45deg); /* tilts toward/away from viewer */
transform: rotateY(45deg); /* spins around vertical axis */
transform: rotateZ(45deg); /* same as rotate() in 2D */

The turn unit is the most readable for full or partial rotations: 0.25turn is 90 degrees, 0.5turn is 180 degrees. This makes animation keyframes much easier to reason about than degree values.

/* Spinning loader animation */
@keyframes spin {
  from { transform: rotate(0turn); }
  to   { transform: rotate(1turn); }
}

.spinner {
  animation: spin 1s linear infinite;
}

scale — Resize an Element

scale enlarges or shrinks an element around its transform origin. A value of 1 is the original size, above 1 is larger, and below 1 (but above 0) is smaller. Negative values flip the element.

/* Scale up to 150% */
transform: scale(1.5);

/* Scale down to 80% */
transform: scale(0.8);

/* Scale X and Y independently */
transform: scaleX(1.5); /* wider only */
transform: scaleY(0.5); /* shorter only */

/* Different X and Y values */
transform: scale(1.5, 0.8);

/* Flip horizontally (mirror) */
transform: scaleX(-1);

/* Flip vertically */
transform: scaleY(-1);

Unlike changing width and height, scale does not affect layout or trigger reflow — it is purely a visual scaling. This makes it ideal for hover effects and entrance animations.

/* Common hover scale effect */
.card {
  transition: transform 0.2s ease;
}

.card:hover {
  transform: scale(1.03);
}

skew — Distort an Element

skew tilts an element along the X or Y axis, creating a slanted or parallelogram-like distortion. It is less commonly used than the other transforms but is handy for decorative shapes and diagonal section dividers.

/* Skew along X axis (horizontal slant) */
transform: skewX(20deg);

/* Skew along Y axis (vertical slant) */
transform: skewY(10deg);

/* Skew both axes */
transform: skew(20deg, 10deg);

A common pattern is to skew a container for a diagonal effect and then counter-skew its child to keep the content straight:

.diagonal-section {
  transform: skewY(-4deg);
}

.diagonal-section .content {
  transform: skewY(4deg); /* undo the skew for inner content */
}

Combining Multiple Transforms

You can apply multiple transform functions in a single transform declaration by separating them with spaces. The order matters — transforms are applied from right to left, so the last function listed is applied first.

/* Rotate AND move AND scale — applied right to left */
transform: translateX(100px) rotate(45deg) scale(1.2);

/* Common hover: lift and scale */
.card:hover {
  transform: translateY(-4px) scale(1.02);
}

The order-dependency is a common source of bugs. translate(50px) rotate(45deg) produces a different result than rotate(45deg) translate(50px) — because rotating first changes the direction of the subsequent translation.

/* Different results: */

/* 1. Move right 50px, then rotate 45deg around new origin */
transform: rotate(45deg) translate(50px, 0);

/* 2. Rotate 45deg first, then move 50px along rotated X axis */
transform: translate(50px, 0) rotate(45deg);

One critical mistake to avoid: Do not use multiple separate transform declarations — the second one overrides the first entirely.

/* WRONG — second transform overwrites the first */
.element {
  transform: translateY(-4px);
  transform: scale(1.02); /* this is all that applies */
}

/* CORRECT — combine in one declaration */
.element {
  transform: translateY(-4px) scale(1.02);
}

transform-origin — Changing the Pivot Point

By default, all transforms happen around the element's centre (50% 50%). transform-origin lets you move that pivot point anywhere — which changes how rotations and scales behave visually.

/* Default — centre */
transform-origin: 50% 50%;
transform-origin: center;

/* Top-left corner */
transform-origin: 0 0;
transform-origin: top left;

/* Bottom-right corner */
transform-origin: 100% 100%;
transform-origin: bottom right;

/* Custom point */
transform-origin: 20px 80px;

/* 3D origin */
transform-origin: 50% 50% 100px;

A practical example — a folding card that rotates from its top edge:

.flap {
  transform-origin: top center;
  transition: transform 0.4s ease;
}

.flap.open {
  transform: rotateX(-90deg);
}

Performance: Why transform Is the Right Choice for Animation

Browsers render pages in layers. Animating properties like top, left, width, or margin forces the browser to recalculate the layout of the entire page on every animation frame — this is called reflow, and it is expensive.

transform and opacity are the two CSS properties that bypass reflow entirely. The browser hands them off to the GPU, which composites the element's layer independently. The result is smooth 60fps animation even on lower-end devices.

/* Slow — causes reflow on every frame */
@keyframes move-bad {
  from { left: 0; }
  to   { left: 200px; }
}

/* Fast — GPU composited, no reflow */
@keyframes move-good {
  from { transform: translateX(0); }
  to   { transform: translateX(200px); }
}

If you notice jank during a CSS animation, the first thing to check is whether you are animating a layout property instead of transform.

Practical Examples

Card Hover Lift

.card {
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.card:hover {
  transform: translateY(-6px);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15);
}

Button Press Effect

.btn {
  transition: transform 0.1s ease;
}

.btn:active {
  transform: scale(0.96);
}

Icon Rotation on Accordion Toggle

.icon {
  transition: transform 0.3s ease;
}

.accordion.open .icon {
  transform: rotate(180deg);
}

Entrance Animation

@keyframes slide-up {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.hero-text {
  animation: slide-up 0.5s ease-out both;
}

Frequently Asked Questions

Does CSS transform affect layout?

No. transform is purely visual — the element still occupies its original space in the document flow. Neighbouring elements do not shift when you translate, rotate, or scale something. If you need the layout to respond to a size change, you must change actual size properties like width or margin.

Why does my second transform override the first?

Because transform is a single CSS property. Writing two separate transform declarations means the second one completely replaces the first. Always combine multiple transform functions in one declaration: transform: translateY(-4px) scale(1.02);

Does the order of transform functions matter?

Yes, significantly. Transforms are applied from right to left (last to first). rotate(45deg) translateX(100px) and translateX(100px) rotate(45deg) produce different visual results because rotation changes the axis direction of the subsequent translation.

What is the difference between translateX and left/margin-left?

translateX moves the element visually without affecting layout or triggering reflow. left and margin-left change the actual position in the document, which forces the browser to recalculate the layout of surrounding elements. Always use translateX for animations.

Can I use CSS transform in 3D?

Yes. CSS supports 3D transforms using translateZ, rotateX, rotateY, rotateZ, perspective, and translate3d. To enable 3D perspective, set perspective on the parent container. Without a perspective value, 3D transforms appear flat.

Why is my rotated element getting clipped?

If a rotated element is clipped, the parent likely has overflow: hidden. Because transform does not affect layout, the browser considers the element's bounding box to still be its original untransformed size, and overflow clipping is applied based on that. Remove overflow: hidden from the parent or add enough padding to accommodate the rotation.