CSS course menu
CSS Transforms: translate, rotate, scale, skew and transform-origin
Learn CSS transforms with live examples: translate, rotate, scale and skew, combining transforms and why order matters, transform-origin, the individual translate, rotate and scale properties, and 3D transforms with perspective.
On this page
The transform property moves, rotates, scales and skews an element visually, without affecting the layout around it: neighbouring elements stay where they are, as if the element had not changed.
The four basic transforms#
.stage { display: flex; gap: 30px; padding: 30px; }
.b { width: 70px; height: 70px; display: grid; place-items: center; background: #2f5fe0; color: white; font: 12px monospace; border-radius: 6px; }
.ghost { outline: 2px dashed #bbb; }<div class="stage">
<div class="ghost"><div class="b" style="transform: translate(20px, 20px)">translate</div></div>
<div class="ghost"><div class="b" style="transform: rotate(25deg)">rotate</div></div>
<div class="ghost"><div class="b" style="transform: scale(1.3)">scale</div></div>
<div class="ghost"><div class="b" style="transform: skewX(-15deg)">skew</div></div>
</div>The dashed outlines show where each box would normally be.
Combining transforms#
List several functions separated by spaces. They are applied from right to left, which means the order changes the result:
.stage { position: relative; height: 140px; }
.b { position: absolute; top: 50px; left: 20px; width: 60px; height: 40px; background: #e8590c; opacity: 0.9; }
.one { transform: translateX(120px) rotate(45deg); }
.two { transform: rotate(45deg) translateX(120px); background: #2f9e44; }<div class="stage">
<div class="b one"></div>
<div class="b two"></div>
</div>The orange box rotates and then moves right. The green box moves along its rotated axis, so it travels diagonally.
transform-origin#
Rotation and scaling happen around the element's centre by default. transform-origin moves that point:
.stage { display: flex; gap: 70px; padding: 30px 40px; }
.b { width: 80px; height: 50px; background: #9b5de5; transform: rotate(30deg); }<div class="stage">
<div class="b" style="transform-origin: center"></div>
<div class="b" style="transform-origin: top left"></div>
<div class="b" style="transform-origin: bottom right"></div>
</div>Individual properties: translate, rotate, scale#
You can also use separate properties, which are easier to change independently (for example, in different states or animations):
.icon {
translate: 0 -2px;
rotate: 10deg;
scale: 1.1;
}They are applied in a fixed order (translate, then rotate, then scale) and combine with any transform value.
Transforms are visual only#
Because transforms do not change layout:
- scaling up a card on hover does not push its neighbours away (it overlaps them instead);
translateis ideal for animation, since the browser does not need to recalculate layout (Transitions);- a transformed element creates a new stacking context and becomes the containing block for
position: fixedchildren (z-index).
3D transforms#
Add perspective to the parent to give children depth, then rotate them around the X or Y axis:
.scene { perspective: 600px; display: flex; gap: 30px; padding: 30px; }
.card { width: 110px; height: 120px; background: linear-gradient(135deg, #2f5fe0, #9b5de5); color: white; display: grid; place-items: center; border-radius: 10px; font: 13px monospace; }<div class="scene">
<div class="card" style="transform: rotateY(40deg)">rotateY(40deg)</div>
<div class="card" style="transform: rotateX(40deg)">rotateX(40deg)</div>
<div class="card" style="transform: rotateY(-40deg)">rotateY(-40deg)</div>
</div>Smaller perspective values give a stronger effect. backface-visibility: hidden and transform-style: preserve-3d are used to build flip cards.
Practice#
Practice exercise
Create a "notification bell" icon (use the 🔔 emoji in a span) that rotates back and forth slightly around its top centre when its button is hovered: rotate it to 15deg on hover with a 0.2s transition and transform-origin: top center.
Show solution
.bell-btn { font-size: 28px; background: none; border: 1px solid #ddd; border-radius: 10px; padding: 6px 12px; }
.bell { display: inline-block; transform-origin: top center; transition: transform 0.2s ease-out; }
.bell-btn:hover .bell, .bell-btn:focus-visible .bell { transform: rotate(15deg); }<button class="bell-btn" type="button" aria-label="Notifications"><span class="bell" aria-hidden="true">🔔</span></button>transform does not work on non-replaced inline elements, so the span is made inline-block. A continuous swing would need a keyframe animation (next lesson).
Key takeaway#
Finished this lesson?