Changing how an element is drawn
The transform property changes the way an element is drawn without changing the layout around it. You can move it, resize it, turn it, tilt it. The element's original space remains reserved, so nothing else on the page shifts, which makes transforms ideal for effects and animation.
<style>
.row { display: flex; gap: 40px; padding: 30px; }
.box { width: 70px; height: 70px; background: #6366f1; color: white; display: grid; place-items: center; border-radius: 8px; }
.t1 { transform: translate(20px, 14px); }
.t2 { transform: scale(1.4); }
.t3 { transform: rotate(20deg); }
.t4 { transform: skewX(-15deg); }
</style>
<div class="row">
<div class="box t1">move</div>
<div class="box t2">scale</div>
<div class="box t3">rotate</div>
<div class="box t4">skew</div>
</div>The 2D functions
translate(x, y): move.translateX()andtranslateY()do one direction. Percentages refer to the element's own size, which makestranslate(-50%, -50%)a famous centring trick.scale(n): resize;1is normal,0.5is half,1.2is 20 percent bigger.scaleX()andscaleY()stretch in one direction.rotate(angle): turn clockwise by degrees (45deg), turns (0.25turn) or radians. Negative values turn the other way.skew(x, y): slant the element.
Because transforms leave neighbours alone, a scaled-up card overlaps what is next to it rather than pushing it.
Combining transforms
List several functions in one declaration, separated by spaces:
.card:hover { transform: translateY(-4px) rotate(2deg) scale(1.03); }Order matters: they are applied right to left, in terms of the element's coordinate system, so rotate(45deg) translateX(100px) and translateX(100px) rotate(45deg) end up in different places. When the result is surprising, reorder.
Note also that setting transform replaces any earlier transform on that element rather than adding to it. If a hover rule only says scale(1.1), a rotate(5deg) from the base rule is dropped. Repeat the whole list, or use the individual properties below.
Individual properties
Modern browsers also support separate translate, rotate and scale properties, which combine without overwriting each other:
.icon { rotate: 15deg; scale: 1.1; translate: 0 -2px; }transform-origin
Scaling and rotation happen around a point, by default the centre (50% 50%). Change it with transform-origin:
<style>
.bar { width: 120px; height: 14px; background: #ef4444; margin: 40px; transition: transform .6s; }
.bar.left { transform-origin: left center; }
.bar:hover { transform: scaleX(0.4); }
</style>
<div class="bar left">hover</div>
<div class="bar">hover</div>The first bar shrinks toward its left edge; the second shrinks toward its centre. This is how progress bars and "swing open" menus are built: transform-origin: top center for a dropdown, top left for a door-like hinge.
Centring with transforms
Before Flexbox and Grid, this was the usual way to centre an absolutely positioned element:
.modal { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }top and left move the element's top-left corner to the centre of the parent; the translate pulls it back by half its own width and height. It still works, but place-items: center on a grid parent is simpler.
A taste of 3D
Add perspective to the parent, then rotate on the X or Y axis:
<style>
.scene { perspective: 400px; padding: 40px; }
.card { width: 120px; height: 80px; background: #f59e0b; color: white; display: grid; place-items: center;
border-radius: 8px; transition: transform .5s; }
.card:hover { transform: rotateY(50deg); }
</style>
<div class="scene"><div class="card">flip</div></div>Smaller perspective values exaggerate depth. Fully flipping a card needs transform-style: preserve-3d and backface-visibility: hidden on its faces. Use 3D sparingly; it is easy to make text blurry or unreadable.
Things to know
- A transform creates a stacking context and a containing block for
position: fixedchildren, which can break fixed headers inside transformed parents. - Text and thin lines can look blurry at fractional pixel translations or with 3D. Round values where you can.
- Inline elements (
span) cannot be transformed until they aredisplay: inline-blockor block. - Transforms are not announced to assistive technology; they are purely visual. Do not use them to convey meaning.
- Combine with
transitionfor hover effects, or@keyframesfor continuous ones (next lessons).
Common mistakes
- Overwriting an earlier transform with a new
transformdeclaration. - Wrong order of combined functions.
- Expecting the layout to adjust to a scaled element.
- Transforming an inline element that ignores it.
- Forgetting
transform-originwhen rotating or scaling from an edge.
Exercise
Make a card that tilts 3 degrees and rises 6px on hover, scaled up by 3 percent, with a smooth 0.2s transition. Then set a different origin for a "door" that opens from the left.
Show one possible answer
<style>
.card { width: 200px; padding: 20px; background: #e0f2fe; border-radius: 12px; transition: transform .2s ease-out; }
.card:hover { transform: translateY(-6px) rotate(-3deg) scale(1.03); }
.door { width: 120px; height: 160px; background: #92400e; transform-origin: left center; transition: transform .5s; }
.door:hover { transform: perspective(500px) rotateY(-60deg); }
</style>
<div class="card">Tilt card</div>
<div class="door"></div>Key points
- transform changes an element's appearance - translate moves it, scale resizes it, rotate turns it and skew slants it.
- Transforms do not affect layout, so other elements stay where they were.
- Several transforms in one declaration apply right to left, so the order changes the result.
- transform-origin sets the point that scaling and rotation happen around, and it defaults to the centre.
- Transforms are cheap for the browser, which makes them the right choice for animation.