CSS Transforms: Rotate, Scale, Translate and Skew

Move, turn, resize or slant an element with the transform property: transform: translate(20px, 0) rotate(15deg) scale(1.1) skewX(5deg). Functions apply right to left, so their order changes the result. Transforms never shift surrounding layout, and animating them is cheap because the browser can hand the work to the compositor.

The four 2D transform functions
FunctionExampleTailwind
translate()translate(1rem, -50%)translate-x-4 -translate-y-1/2
rotate()rotate(45deg)rotate-45
scale()scale(1.1)scale-110
skew()skewX(6deg)skew-x-6

Updated

Use the sliders to build a transform and watch a card react, then switch between the single transform property and the separate translate, rotate and scale properties. Further down are the origin demo, a 3D flip you can hover, transition recipes and a note on why transforms stay smooth while top and left stutter.

Every transform function

2D and 3D transform functions with their Tailwind utilities
FunctionWhat it doesTailwind (v3 and v4 unless noted)
translate(x, y)Moves along X and Ytranslate-x-4, -translate-y-2, translate-x-[13px]
translateX() / translateY()Moves on one axistranslate-x-1/2, translate-y-full
translateZ(z)Moves toward or away from the viewer (needs perspective)translate-z-4 (v4)
rotate(a)Turns clockwise by an angle (deg, turn, rad)rotate-45, -rotate-12, rotate-[17deg]
rotateX(a) / rotateY(a)Tilts around the horizontal or vertical axis in 3Drotate-x-45, rotate-y-180 (v4)
scale(s) / scale(x, y)Grows or shrinks; negative mirrorsscale-110, scale-x-75, -scale-x-100
skew(x, y) / skewX() / skewY()Slants the elementskew-x-6, -skew-y-3
matrix(a, b, c, d, e, f)All 2D transforms in one matrixarbitrary: [transform:matrix(...)]
perspective(d)Depth for this element onlyperspective-[800px] on the parent (v4)

Order of operations

The same two functions in a different order put the box in a different place. Both blue boxes start at the same spot.

translate then rotate, versus rotate then translate

In the first box the slide happens along the screen's X axis. In the second the slide follows the rotated axis, so the box ends up lower.

<style>
  .stage { position: relative; height: 200px }
  .ghost, .box { position: absolute; top: 20px; left: 20px; width: 70px; height: 70px; border-radius: 8px }
  .ghost { border: 2px dashed #94a3b8 }
  .a { background: #4F98FB; transform: translateX(120px) rotate(45deg) }
  .b { background: #f59e0b; transform: rotate(45deg) translateX(120px) }
</style>
<div class="stage">
  <div class="ghost"></div>
  <div class="box a"></div>
  <div class="box b"></div>
</div>

Individual properties do not overwrite each other

The hover rule only touches scale, so the rotate set on the base rule survives. With the transform shorthand you would have to repeat rotate(-3deg) inside :hover.

.sticker {
  rotate: -3deg;
  transition: scale 200ms ease;
}
.sticker:hover {
  scale: 1.08;
}
/* Applied order: translate, rotate, scale, then transform */

transform-origin

Same rotation, three origins

The origin is the point that stays fixed. center is the default; top left swings the box like a door hinge in the corner.

<style>
  .row { display: flex; gap: 48px; padding: 30px }
  .card { width: 70px; height: 70px; background: #4F98FB; border-radius: 6px; transform: rotate(25deg); outline: 2px dashed #94a3b8; outline-offset: -2px }
  .tl { transform-origin: top left }
  .br { transform-origin: bottom right }
</style>
<div class="row">
  <div class="card"></div>
  <div class="card tl"></div>
  <div class="card br"></div>
</div>

3D card flip

Hover or focus to flip (CSS only)

perspective on the wrapper gives depth, preserve-3d keeps both faces in one 3D space and backface-visibility hides whichever face points away. Reduced-motion users get an instant swap.

<style>
  .scene { perspective: 800px; width: 200px; height: 180px; margin: 10px auto }
  .card { position: relative; width: 100%; height: 100%; transform-style: preserve-3d; transition: transform 600ms ease }
  .scene:hover .card, .scene:focus-within .card { transform: rotateY(180deg) }
  .face { position: absolute; inset: 0; display: grid; place-items: center; border-radius: 12px; backface-visibility: hidden; font: 600 18px system-ui; color: #fff }
  .front { background: #4F98FB }
  .back { background: #0f172a; transform: rotateY(180deg) }
  @media (prefers-reduced-motion: reduce) { .card { transition: none } }
</style>
<div class="scene" tabindex="0">
  <div class="card">
    <div class="face front">Front</div>
    <div class="face back">Back</div>
  </div>
</div>

Combining transforms with transitions

Hover lift for cards and buttons

Only transform and box-shadow change, and the movement is turned off for readers who ask for less motion.

<style>
  .tile { display: inline-block; padding: 18px 26px; margin: 20px; border-radius: 12px; background: #fff; border: 1px solid #e5e5e5; box-shadow: 0 1px 2px rgb(0 0 0 / .08); transition: transform 180ms ease-out, box-shadow 180ms ease-out }
  .tile:hover { transform: translateY(-4px); box-shadow: 0 10px 24px rgb(0 0 0 / .12) }
  @media (prefers-reduced-motion: reduce) { .tile { transition: none } .tile:hover { transform: none } }
</style>
<a class="tile" href="#">Hover this card</a>

The same idea in Tailwind

motion-safe: applies the hover movement only when the reader has not requested reduced motion.

<a href="/plans"
   class="inline-block rounded-xl border p-5 transition-transform duration-200
          motion-safe:hover:-translate-y-1 motion-safe:hover:scale-[1.02]">
  See plans
</a>

What is cheap to animate

Rendering work triggered when a property changes (typical browser behaviour)
PropertyLayoutPaintComposite only
transform, translate, rotate, scaleNoNo (usually)Yes
opacityNoNo (usually)Yes
background-color, color, box-shadowNoYesNo
width, height, top, left, marginYesYesNo

Reading a transform list

A helpful mental model: start from the last function and work back to the first, each time moving, turning or stretching the element in its current, already altered frame. If a result looks wrong, reversing the list is often the fix.

Percentages inside translate() measure the element's own border box. That is why translate(-50%, -50%) centres a box whose size you do not know in advance.

Why transforms animate smoothly

Browsers render a page in stages: style, layout, paint and composite. Changing top or width reruns layout for the element and often for its neighbours. Changing transform or opacity can usually skip straight to compositing, where an already painted layer is moved on the graphics processor.

That is the reason motion guidelines keep pointing at these two properties. If an animation drops frames, open the browser performance panel and look for layout and paint work inside each frame.

3D in one paragraph

Give the parent perspective, for example 800px, so children rotated on X or Y appear to recede. Smaller numbers exaggerate the depth. transform-style: preserve-3d lets nested children share that 3D space, and backface-visibility: hidden hides a face once it turns away, which is the core of every card flip.

How it works

  1. Drag the translate, rotate, scale and skew sliders. The card moves and the CSS rewrites itself.
  2. Pick a transform-origin to see which point stays pinned while the card turns or grows.
  3. Toggle between the combined transform shorthand and the individual properties.
  4. Copy the CSS or the Tailwind classes and paste them into your stylesheet or markup.

Tips

Frequently asked questions

Why does the order of transform functions matter?

Each function changes the coordinate system for the ones written before it, so the list is effectively applied from right to left. translate(100px) rotate(45deg) turns the box and then slides it straight right, while rotate(45deg) translate(100px) slides it along the rotated, diagonal axis.

What is the difference between transform: rotate() and the rotate property?

The rotate, scale and translate properties are standalone versions of the same functions. They can be set and animated separately, so a hover scale does not wipe out a rotation set elsewhere. The browser applies translate first, then rotate, then scale, and then anything in transform.

Does transform affect layout or other elements?

No. The element keeps its original box in the layout and neighbours do not move. Only its painted image changes. A transformed element does create a new stacking context and becomes the containing block for fixed-position descendants, which can surprise you with modals.

How do I flip an element horizontally?

Use transform: scaleX(-1) or the scale property with scale: -1 1. It mirrors the element around its origin, which is the centre by default. For a 3D flip that shows a back face, rotate around the Y axis inside a parent with perspective.

Why is my transformed text blurry?

Fractional translations and some scale values place glyphs between pixels, which softens them, especially while an animation is running. Rounding translate values to whole pixels and scaling to the final size rather than up from a smaller one usually sharpens the result.

Related: CSS transitions, CSS animation generator, CSS masks and shapes, CSS position, Scroll animations.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap