Skip to content
ColorKit

Free CSS transform builder

CSS transform generator that shows what the order costs

This CSS transform generator is free and needs no signup: stack any of the 21 functions the specification defines, reorder them with the arrow buttons, and copy the declaration. Two previews run side by side, your chain and the same chain reversed, because transform functions are matrix multiplications and multiplication does not commute — rotate(45deg) translateX(100px) and translateX(100px) rotate(45deg) leave the element 77px apart. The output switches between the function chain, the individual translate / rotate / scale properties, and the single matrix the browser reduces all of it to.

  • 100% free
  • No signup
  • 21 functions
  • 2-D and 3-D
  • Reorderable chain
Start from
Your order

rotate(45deg) translateX(60px)

Reversed

translateX(60px) rotate(45deg)

Reversing the order moves the element 46px. Same functions, same values, different answer — that is matrix multiplication, and it does not commute.

The function chain

  1. 1rotate()2-D

    Turns clockwise around the transform origin. Identical to rotateZ() in every browser — the 2-D name is simply the older one.

  2. 2translateX()2-D

    The single-axis form. Leaves layout untouched: neighbors stay where they were and the element can overlap them, unlike a change of margin or left.

Press Ctrl+V anywhere on the page to drop in a transform declaration copied out of DevTools — no need to click the field first.

transform-origin

The orange dot marks the point everything pivots and scales around. Move it to a corner and the same rotate() sweeps a completely different arc.

3-D context on the parent

Off — every 3-D rotation collapses to a squash, because an orthographic projection has no vanishing point.

The panel carries its own rotateY(-62deg). Under the default flat it is drawn into the parent's plane and looks like a stripe; under preserve-3d it stands up in the same 3-D space and hinges away.

The arrow is drawn in #ffffff, picked for the higher contrast against #4f46e5 so the rotation stays readable at every angle.

.subject {
  transform: rotate(45deg) translateX(60px);
  transform-origin: 50% 50%;
}

How to build a CSS transform

Stack the functions, move one, and take whichever of the three spellings your stylesheet wants.

  1. Stack the functions you need

    The Add a function menu holds all 21 functions the specification defines, split into 2-D and 3-D groups so translateZ and rotateY are not hiding among the flat ones. Every step you add gets its own row with a number input, a unit selector and a slider — ±300px for translation, ±500px on the Z axis, ±360° for rotations and skews, −2 to 3 for scale factors. Already have a value? Paste it into the Read an existing transform box and it becomes an editable chain, or comes back with the reason it will not parse, naming the token that broke it.

  2. Move a step and watch the other stage disagree

    The arrow buttons on each row move that function up or down the chain, and the second preview always shows the same functions in the opposite order. Under the two stages the tool multiplies both chains out and reports how far apart they land — reversing rotate(45deg) translateX(100px) moves the element 77px, which is the whole argument about order in one number. Use this order adopts the reversed chain if it turns out to be the one you wanted. The orange dot is the transform origin; drag it to a corner with the 3×3 picker and every rotation and scale in the chain pivots somewhere new while the translations ignore it completely.

  3. Take the form your codebase actually uses

    Three output tabs produce three legitimate spellings of the same result: the function chain, the individual translate / rotate / scale properties, and the resolved matrix() or matrix3d(). The individual-properties tab refuses when the rewrite would not be equivalent and tells you which step is out of position, because those three properties always apply in the order translate, rotate, scale, then transform, whatever order you wrote them in. Copy the CSS takes the whole block, and once you switch a perspective on it comes out as a separate rule for the parent, because that is where the property belongs rather than on the transformed element. Download .css writes the same block to a file.

Technical specifications

FunctionsAll 21 in the CSS Transforms specification: 5 translate, 5 rotate, 5 scale, 3 skew, matrix, matrix3d and perspective
Chain lengthUp to 16 functions; the import box reads 1,000 characters
Units acceptedLengths in px, %, em, rem, vw, vh, ch, pt, pc, cm, mm, in; angles in deg, rad, grad and turn
Control ranges±300px translation, ±500px on Z, ±360° rotation and skew, −2 to 3 scale, 100–2000px perspective
transform-origin9 preset positions plus free x/y percentages, and a z length once a 3-D function is in the chain
Output forms3 — function chain, individual translate/rotate/scale properties, or the resolved matrix()/matrix3d()
Matrix precisionDouble-precision 4×4 product, printed to 4 decimals for matrix() and 5 for matrix3d(); suppressed when a length is in %, em or rem
Where it runsEntirely in this tab — the parser, the matrix multiplication and both previews, with no request to a server

Frequently asked questions

Does the order of CSS transform functions matter?

Yes, and it changes where the element ends up. The browser reads the list left to right and applies each function to the coordinate system the previous one left behind, which is the same as multiplying their matrices in that order — and matrix multiplication does not commute. rotate(45deg) translateX(100px) turns the element first and then slides it 100px along its own now-diagonal x-axis, landing at (70.7, 70.7); translateX(100px) rotate(45deg) slides it 100px right and spins it in place at (100, 0). The two results are 77px apart, which is why the second preview on this page exists.

How do the individual translate, rotate and scale properties differ from transform?

They apply in a fixed order — translate, then rotate, then scale, then whatever is in transform — no matter what order you write them in the rule. That is the trade for their real advantage: each one is a separate animatable property, so a hover can scale an element while a keyframe animation independently translates it, something a single transform declaration cannot do because the second declaration wins outright. They reached all three engines in 2022 (Firefox 72, Chrome 104, Safari 14.1), and this generator will only offer the rewrite when your chain already runs in that fixed order.

Why does rotateX or rotateY just squash the element instead of turning it?

Because nothing has set a perspective, so you are looking at an orthographic projection with no vanishing point. Give the parent a perspective of 400 to 1200px — one vanishing point shared by all its children, which is what you want for a row of cards — or put perspective(800px) first in the element's own chain to give it a private one centered on itself. Position matters: perspective() only affects the functions written to its right, so a chain that ends with it does nothing at all.

Does transform-origin move a translation too?

No — translation is the one family the origin cannot touch. transform-origin sets the fixed point that rotate, scale and skew work around, and its default of 50% 50% is why an unqualified rotate() spins about the element's center. Move the origin to 0 0 and the same rotate(30deg) swings the element down and to the right instead of turning it on the spot, while translateX(40px) still moves exactly 40px. A third value is allowed as well, but only as a length: transform-origin: 50% 50% 60px is valid and 50% 50% 20% is not.

Why does transform do nothing to my <span> or <a>?

Because transform does not apply to non-replaced inline boxes, and a span or a link is one until you change its display. The fix is display: inline-block, or letting it become a flex or grid item, at which point the property starts working with no other change. The same rule catches transform-origin, perspective and transform-style, so an inline element sitting in a 3-D scene is inert in all four.

Why does text go blurry after I scale or translate an element?

Because the element was rasterized once and then stretched, or it landed on a half-pixel boundary. A composited layer is painted at its natural size and the GPU resamples that texture, so scale(1.4) is enlarging a bitmap rather than re-rendering the glyphs — render at the final size and scale down instead, or drop will-change once the movement is over so the layer is repainted crisply. The half-pixel version is arithmetic: translateX(50%) on an element 101px wide asks for 50.5px, and rounding it costs the vertical stems their sharpness.

Why does getComputedStyle give me a matrix instead of the functions I wrote?

Because the functions are only a notation — the engine stores one 4×4 matrix and returns that. Your chain is multiplied out at parse time, so matrix(0.7071, 0.7071, -0.7071, 0.7071, 42.4264, 42.4264) is all that survives of rotate(45deg) translateX(60px), and there is no way back to the original functions because infinitely many chains produce the same product. The Resolved matrix tab on this page does the same multiplication so you can compare, and it stays silent when the chain contains a percentage, em or rem, since those need a layout the page cannot see.

About the transform property

A transform value is a list of functions, and the browser composes them by multiplying their matrices left to right — each function operates on the coordinate system the one before it produced. That single sentence explains the behavior people file bug reports about: rotate(45deg) translateX(100px) travels along an axis that has already been turned, while translateX(100px) rotate(45deg) travels along the page's axis and then spins, and the two land 77px apart. Percentages are resolved against the element's own border box rather than the parent's, which is what makes translate(-50%, -50%) a size-independent centering trick. The origin of all this rotating and scaling is transform-origin, defaulting to 50% 50%; it has no effect whatsoever on translation, so a chain that appears to ignore the origin usually contains nothing but translates.

Since 2022 there is a second spelling. translate, rotate and scale exist as standalone properties, and their point is independent animation: a hover can own the scale while a keyframe owns the translate, which one transform declaration can never do because the later declaration replaces the earlier one outright. The catch is that they compose in a fixed sequence — translate, then rotate, then scale, then whatever remains in transform — regardless of the order they appear in your rule. Generators that advertise them as a modern equivalent of any chain are wrong, and this one checks: it offers the rewrite only when your chain already runs in that sequence and otherwise names the step that is out of position. That independence is exactly what makes transform worth animating in the first place, since transform and opacity are the two properties the compositor can move without re-running layout — the reason the CSS animation generator and the keyframes generator reach for transform in almost every effect, and why the shape of the motion is left to the cubic bezier generator instead of to the geometry.

The 3-D half has two traps. The first is that perspective as a property on the parent and perspective() as a function in the chain are not interchangeable: the property gives every child one shared vanishing point, the function gives one element a private vanishing point centered on itself, and because a function only affects what is written to its right, a chain ending in perspective() is a no-op. The second is that transform-style: preserve-3d is fragile — overflow other than visible, a filter, an opacity below 1 or a clip-path on the same element forces the children flat again, which is why a card flip stops working the moment someone adds overflow: hidden to round its corners. Worth remembering throughout: a transform is painted, not laid out. The element keeps reserving its untransformed box, so a scaled card still occupies its original track in a CSS grid and its original width in a flex row, and the ratio a media box is reserving with aspect-ratio is the pre-transform one. Neighbors never move out of the way; overlap is the expected outcome.

Where the matrices are multiplied

Every number on this page is worked out by JavaScript running in the tab you are reading it in. Nothing you type, paste or open is uploaded, logged or kept, which is also why the tools carry on working after you disconnect from the network.

Both previews are ordinary elements with a style attribute written by this page, and the 4×4 products behind the distance readout are worked out in JavaScript here in the tab. A transform you paste in is parsed locally and never leaves the page, and nothing about the chain, the origin or the color you pick is stored between visits.