CSS 2D Transforms
In this page:
selector {
transform: translate(x, y) rotate(angle) scale(factor) skew(angle);
}
What Counts as a 2D Transform
A 2D transform operates purely within the flat X/Y plane of the screen: it can move, rotate, scale, or skew an element, but never gives it depth. All of these functions are passed as values of the single transform property, and several can be combined in one declaration.
- Accepted values:
- transform: none — default, no transform
- translate(x, y) — moves, with px or %
- rotate(angle) — deg, turn, or rad
- scale(x, y) — number multipliers, 1 is unchanged
- skew(x, y) — angles in deg
- matrix(a, b, c, d, tx, ty) — combined 2D transform
- transform-origin — keywords, lengths, or percentages
- inherit | initial | unset | revert — CSS-wide keywords accepted by every property
Example: What Counts as a 2D Transform
<style>
.box {
background: steelblue;
color: white;
padding: 10px;
display: inline-block;
}
.box:hover {
transform: translateX(20px) rotate(10deg) scale(1.1);
}
</style>
<div class="box">Move, rotate, and scale, all within the flat X/Y plane</div>
transform-origin
transform-origin sets the fixed point a rotation or scale pivots around, defaulting to the element's exact center (50% 50%). Changing it to a corner or edge makes a rotate() spin around that point instead, which matters a lot for hinge-like or corner-anchored animations.
- Accepted values:
- center — default, 50% 50%
- top left | bottom right — keywords for corners
- left | right | top | bottom | center — keyword positions
- <length> <length> — offsets from the top-left, e.g. 10px 20px
- <percentage> — relative to the element's size
- <z-offset> — optional third length value for 3D
- inherit | initial | unset | revert — CSS-wide keywords accepted by every property
Example: transform-origin
<style>
.box {
transform-origin: top left;
background: steelblue;
color: white;
padding: 10px;
display: inline-block;
}
.box:hover {
transform: rotate(30deg);
}
</style>
<div class="box">Pivots around its top-left corner instead of the center</div>
The matrix() Function
matrix(a, b, c, d, tx, ty) is the low-level mathematical form every other 2D transform function compiles down to internally — six numbers encoding scale, skew, and translation in one matrix. It's rarely written by hand, but understanding it explains why transform functions compose the way they do.
- Accepted values:
- matrix(a, b, c, d, tx, ty) — six numbers for a 2D affine transform
- matrix(1, 0, 0, 1, 0, 0) — identity, no change
- a and d — scale on x and y
- b and c — skew (and rotation)
- tx and ty — translation in px
Example: The matrix() Function
<style>
.box {
background: steelblue;
color: white;
padding: 10px;
display: inline-block;
}
.box:hover {
transform: matrix(1, 0, 0, 1, 20, 10);
}
</style>
<div class="box">matrix() equivalent to translate(20px, 10px)</div>
Order of Combined Transforms
When multiple transform functions are listed in one declaration, they apply right-to-left in effect, meaning the order you write them changes the visual result — rotating then translating produces a different outcome than translating then rotating the same amounts.
- Accepted values:
- transform: translate(...) rotate(...) — moves along the normal axes, then rotates in place
- transform: rotate(...) translate(...) — rotates first, so the move follows the rotated axis
- multiple functions — read right to left when reasoning about the result
- inherit | initial | unset | revert — CSS-wide keywords accepted by every property
Example: Order of Combined Transforms
<style>
.a {
background: steelblue;
color: white;
padding: 10px;
display: inline-block;
margin: 30px;
}
.a:hover {
transform: rotate(45deg) translate(50px);
}
.b {
background: seagreen;
color: white;
padding: 10px;
display: inline-block;
margin: 30px;
}
.b:hover {
transform: translate(50px) rotate(45deg);
}
</style>
<div class="a">Rotate then translate</div>
<div class="b">Translate then rotate — a different result</div>
2D Transforms and Layout
Because transforms operate after layout is calculated, a transformed element still reserves its original, untransformed space in the page flow — visually moving an element with translate() does not shift its neighboring elements, unlike changing margin or position would.
- Accepted values:
- transform — does not change layout; the original space is still reserved
- translate — shifts visually without affecting siblings
- scale — grows visually without pushing neighbors
- width / margin — use these when layout should change
- inherit | initial | unset | revert — CSS-wide keywords accepted by every property
Example: 2D Transforms and Layout
<style>
.moved {
background: steelblue;
color: white;
padding: 10px;
display: inline-block;
}
.moved:hover {
transform: translateX(60px);
}
</style>
<div class="moved">Moved visually</div>
<div style="background: #ddd; padding: 10px; display: inline-block;">Neighbor does not shift — original space still reserved</div>
- Rotating with
rotate(45)and no unit, which is invalid; use45deg. - Writing transforms in the wrong order, since
translatethenrotatediffers fromrotatethentranslate. - Expecting a transform to change the layout, when the space the element occupies stays the same.
Supported in all modern browsers: Chrome, Firefox, Safari, Edge and Opera.
Chapter Quiz — Complete all 22 topics to unlock
0/22 topics done
Complete these topics first:
- CSS Gradients
- CSS Shadows
- CSS Filters
- CSS Blend Modes
- CSS Transforms
- CSS 2D Transforms
- CSS 3D Transforms
- CSS Transitions
- CSS Animations
- CSS Image Styling
- CSS Image Effects
- CSS Hover Overlays
- CSS Image Modal
- CSS Image Centering
- CSS Image Shapes
- CSS Buttons
- CSS Columns
- CSS Clip Path
- CSS Shapes
- CSS Scroll Snap
- CSS Architecture: BEM
- CSS Custom Properties