Transforms
Translate, scale, rotate and skew — plus the 3D transform utilities and perspective added in Tailwind v4.
Transforms move, resize and rotate an element without affecting document layout, which makes them the cheapest way to animate anything.
2D transforms#
| Utility | Effect |
|---|---|
translate-x-4 / translate-y-4 | Move along an axis |
translate-4 | Move on both axes |
-translate-y-1 | Negative — move up |
translate-x-1/2 | Percentage of the element's own size |
scale-110 | Scale both axes to 110% |
scale-x-75 / scale-y-150 | Per-axis |
rotate-45 / -rotate-12 | Rotate in degrees |
skew-x-6 / skew-y-3 | Skew |
transform-none | Remove all transforms |
<div class="grid grid-cols-2 gap-6 p-8 text-xs sm:grid-cols-4">
<div class="grid h-16 place-items-center rounded bg-indigo-500 text-white">none</div>
<div class="grid h-16 -translate-y-2 place-items-center rounded bg-indigo-500 text-white">-translate-y-2</div>
<div class="grid h-16 rotate-6 place-items-center rounded bg-indigo-500 text-white">rotate-6</div>
<div class="grid h-16 scale-90 place-items-center rounded bg-indigo-500 text-white">scale-90</div>
</div>Transforms compose — translate-x-2 rotate-3 scale-105 applies all three.
Transform origin#
By default transforms pivot around the centre. origin-* changes that.
| Utility | Origin |
|---|---|
origin-center | Default |
origin-top origin-bottom origin-left origin-right | Edges |
origin-top-left … origin-bottom-right | Corners |
origin-[30%_70%] | Arbitrary |
<div class="flex items-end gap-10 p-8 text-xs">
<div class="grid size-16 rotate-45 place-items-center rounded bg-emerald-500 text-white">center</div>
<div class="grid size-16 origin-bottom-left rotate-45 place-items-center rounded bg-emerald-500 text-white">bottom-left</div>
</div>Origin matters most for menus and tooltips — a dropdown scaling from origin-top looks like it is unfolding from its trigger, while origin-center looks like it is inflating.
3D transforms#
v4 v4 added a full set of 3D transform utilities.
| Utility | Effect |
|---|---|
perspective-near … perspective-distant | Depth of field on a parent |
perspective-origin-* | Vanishing point |
transform-3d | Enable transform-style: preserve-3d |
rotate-x-45 rotate-y-30 rotate-z-12 | Axis rotation |
translate-z-12 | Depth |
scale-z-125 | Depth scale |
backface-visible / backface-hidden | Show or hide the reverse face |
<div class="perspective-normal grid place-items-center p-10">
<div class="grid size-24 rotate-x-40 rotate-z-30 transform-3d place-items-center rounded-lg bg-gradient-to-br from-sky-400 to-indigo-600 text-xs font-semibold text-white shadow-xl">
3D
</div>
</div>The parent needs perspective-* for any 3D rotation to look like depth rather than a flat squash.
Flip card#
<div class="perspective-normal p-6">
<div class="group mx-auto h-32 w-48 cursor-pointer transform-3d">
<div class="relative size-full transition-transform duration-500 transform-3d group-hover:rotate-y-180">
<div class="absolute inset-0 grid place-items-center rounded-xl bg-indigo-600 text-sm font-medium text-white backface-hidden">
Front — hover me
</div>
<div class="absolute inset-0 grid rotate-y-180 place-items-center rounded-xl bg-emerald-600 text-sm font-medium text-white backface-hidden">
Back
</div>
</div>
</div>
</div>zoom-*#
v4.3 The zoom property scales an element and its layout box, unlike scale-* which only scales the rendering.
| Utility | Effect |
|---|---|
zoom-75 zoom-100 zoom-125 | Preset zoom levels |
zoom-[1.1] | Arbitrary |
zoom-(--preview-zoom) | From a variable |
<div class="zoom-75">Everything inside is 75% size, and takes 75% of the space.</div>scale-75 | zoom-75 | |
|---|---|---|
| Affects layout | No — leaves a gap | Yes — reflows |
| Animatable cheaply | Yes | No |
| Right for | Hover effects, animation | Preview panes, density controls |
Common patterns#
Centring absolutely#
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">Centred</div>translate-x-1/2 is relative to the element's own width, which is exactly what this pattern needs.
Press feedback#
<button class="rounded-md bg-indigo-600 px-4 py-2 text-white transition active:scale-95">Click</button>Image zoom inside a fixed frame#
<div class="p-4">
<a href="#" class="group block w-52 overflow-hidden rounded-xl">
<div class="h-32 bg-gradient-to-br from-amber-400 to-rose-500 transition-transform duration-500 group-hover:scale-110"></div>
</a>
</div>overflow-hidden on the wrapper is what keeps the zoom inside the frame.
Chevron that rotates when open#
<svg class="size-4 transition-transform group-open:rotate-180">…</svg>Dropdown that unfolds from its trigger#
<div class="origin-top scale-95 opacity-0 transition duration-150 data-[open=true]:scale-100 data-[open=true]:opacity-100">
…
</div>Common mistakes#
- Expecting a transform to move neighbours. Transforms do not affect layout; siblings stay put. Use margin or
zoom-*if you need reflow. - 3D rotation with no
perspective-*on the parent. It looks like a flat squash. transformon a parent of afixedchild. The child becomes fixed to the transformed ancestor, not the viewport — see Position.scale-*on text. It scales the rendered pixels, so text goes blurry. Changefont-sizeinstead.- Forgetting
backface-hiddenon a flip card. You see a mirrored copy of the front through the back. - Transform without
transition. The change is instant. hover:scale-110on a card containing a hover-activated dropdown. The transform creates a containing block that traps the menu.
Related topics#
- Transitions — making transforms animate
- Animation — keyframes built from transforms
- Position — the centring pattern
- Overflow & Layering — stacking contexts created by transforms