CSS Transform Generator

Build a transform with live preview against where the element started, and get the same thing as the individual translate, rotate and scale properties.

Enable JavaScript to change the transform; the CSS below is for the defaults.

A percentage translation is measured against the element's own size, not its parent's, which is what makes translate(-50%, -50%) centre it.

A 3D rotation needs a perspective to look like anything; without one it just squashes.

How far the viewer is from the plane. Smaller is a more extreme angle. Only written out when there is a 3D rotation to apply it to.

The point everything turns and scales around. "center" is the default and is left out of the output.

Transforms are one of the two properties browsers animate without touching layout, which is why a hover effect should use one.

Before
After
transform.css
.card:hover {
	transform: translate(12px, -8px) rotate(6deg) scale(1.05);
	transition: transform 180ms ease;
}

/* The same thing as the individual properties, which animate
   independently of each other. Chrome and Edge 104, Safari 14.1,
   Firefox 72. */
.card:hover {
	translate: 12px -8px;
	rotate: 6deg;
	scale: 1.05;
}

A transform is a list of functions, and the list is not commutative. translate(100px) rotate(45deg) moves the element and then turns it. rotate(45deg) translate(100px) turns the coordinate system first, so the move happens along the rotated axis and lands somewhere else. Same two functions, different place.

This tool writes them in one order, documented below, so the preview and the copied CSS cannot disagree. The preview keeps a dashed box where the element started, because a transform is only legible against its original position.

The output also carries the individual properties, translate, rotate and scale, which browsers have supported since 2022. They matter more than they look: each animates independently, so a hover that scales does not have to restate the translation that positioned the element, and two animations can move and turn the same element without fighting over one property.

How to use

  1. Move, turn and scale. The functions are written in the order translate, rotate, scale, skew.
  2. Set transform-origin if the element should turn around something other than its centre.
  3. For a 3D tilt, set a rotation on X or Y and a perspective. Without a perspective, a 3D rotation just squashes.

Example

A card that lifts slightly on hover:

.card:hover {
	transform: translate(12px, -8px) rotate(6deg) scale(1.05);
	transform-origin: top left;
	transition: transform 180ms ease;
}

/* The same thing as the individual properties, which animate
   independently of each other. Chrome and Edge 104, Safari 14.1,
   Firefox 72. */
.card:hover {
	translate: 12px -8px;
	rotate: 6deg;
	scale: 1.05;
}

And a tilt, where the perspective is doing the work:

.tilt {
	transform: perspective(800px) rotateY(24deg);
}

Drop the perspective from that and the element does not tilt, it narrows.

Pitfalls

Order changes the result. Every list of transform functions is applied left to right, each one on the coordinate system the previous one left behind. If you hand-edit the output, the order is part of the value.

A percentage translation is measured against the element. Not its parent, which is why translate(-50%, -50%) is the centring trick: the element moves half of its own width and height.

Transforms do not affect layout. The space the element occupied stays occupied and nothing reflows around it. That is what makes them cheap to animate and what makes a translated element overlap its neighbours.

A transform creates a containing block. Same as a filter: position: fixed inside a transformed element is fixed to that element rather than the viewport.

Perspective belongs first. In the shorthand it has to precede the rotations it applies to. Written after them it applies to nothing.

transform-origin moves before it turns. Changing the origin changes both rotation and scale, which is why a scale that grows from the top left looks like a different animation from one that grows from the centre.

Skew has no individual property. translate, rotate and scale exist on their own; skew does not, so anything using it still needs the shorthand.

Compatibility

transform, transform-origin and the 2D functions work in every browser in use, including old Safari and Edge.

The individual translate, rotate and scale properties arrived in Chrome and Edge 104, Safari 14.1 and Firefox 72, so they are safe now and were not in 2021. Both forms are emitted, and the shorthand comes first, so a browser that understands neither simply ignores the second rule.

The value is built by one function and mirrored in PHP for the first paint, with a case file comparing the two on every build: the neutral case, a hover transform, a 3D tilt, a skew with mismatched scales and non-numeric input. That is why the server-rendered CSS and the CSS after hydration are the same string.

The preview stage marks the transform origin and keeps a ghost of the untransformed box, since a transform shown alone is not interpretable.

Frequently asked questions

Why does my 3D rotation look flat?
No perspective. rotateY(24deg) without one is an orthographic projection, which looks like a horizontal squash. Add perspective(800px) before it, or set perspective on the parent element.
Should I use transform or the individual properties?
The individual properties when different things animate at different times, since they do not overwrite each other. The shorthand when it is one static value, or when you need a skew.
Why is my animation smoother with transform than with left?
Because left changes layout and transform does not. Transform and opacity are the two properties a browser can animate on the compositor, without recalculating where anything is.
What does transform-origin: 0 100% mean?
The bottom left corner: the first value is the horizontal position, the second the vertical one. Keywords work too, so left bottom is the same thing.
Can I animate between two transforms with different functions?
Only if the function lists match in order and type. translate(0) rotate(0) animating to translate(10px) rotate(5deg) interpolates cleanly; a list that gains or loses a function mid-animation falls back to a matrix interpolation, which rarely looks like what you wanted.
Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.