Transforms
Learn to move, scale, rotate, and skew elements with the CSS transform property, control the pivot point with transform-origin, and get an introduction to 3D transforms.
The transform property lets you visually reposition, resize, rotate, or distort an element without affecting the normal document flow — other elements on the page behave as if the transformed element were still in its original position and size. This makes transforms ideal for hover effects, animations, and micro-interactions, and they're one of the two CSS properties (along with opacity) that browsers can animate especially efficiently, which you'll learn more about in the transitions and animations lessons.
translate(): moving an element
translate(x, y) shifts an element visually by the given amounts along the x (horizontal) and y (vertical) axes, without changing layout — the element still occupies its original space for the purposes of other elements' positioning. translateX() and translateY() move along a single axis. Positive x moves right, positive y moves down.
This is different from adjusting margin or top/left: those actually change layout and can push or pull neighboring elements, while translate is a purely visual offset layered on top of the normal layout.
scale(): resizing an element
scale(x, y) resizes an element by a multiplier: scale(1) is the original size, scale(1.5) is 150% size, scale(0.5) is half size. scale(2) with one argument scales both axes equally. Like translate, this is purely visual — it doesn't reflow surrounding content, and can cause the element to visually overlap its neighbors if scaled up significantly.
rotate() and skew()
rotate(45deg) rotates an element clockwise by the given angle around its transform-origin point (negative values rotate counter-clockwise). skew(x-angle, y-angle) slants an element along the x and/or y axis — skewX(20deg) alone slants it diagonally, useful for stylized banner shapes or parallelogram-style buttons.
Combining transforms
Multiple transform functions can be combined in a single transform declaration, space-separated: transform: translateX(20px) rotate(10deg) scale(1.1);. Order matters — transforms apply in the order written, each one operating in the coordinate space left by the previous one, so translate then rotate can produce a visually different result than rotate then translate.
transform-origin: choosing the pivot point
By default, rotate() and scale() pivot around the center of the element (50% 50%). transform-origin lets you move that pivot point — for example transform-origin: top left; makes an element rotate around its top-left corner instead of its center, which is useful for effects like a page-turn or a flag-style animation anchored at one edge.
transform-origin accepts keywords (top, bottom, left, right, center) or explicit lengths/percentages, and can take up to three values for the x, y, and z axes.
A brief look at 3D transforms
Beyond 2D, CSS supports 3D transforms: rotateX() and rotateY() rotate an element around a horizontal or vertical axis, creating a sense of depth (imagine a card flipping over, or a door swinging open). For 3D rotation to look convincing, a perspective value must be set — either as the perspective property on a parent element, or as a perspective() function inside the transform itself — which controls how dramatic the sense of depth appears (a smaller value creates a more extreme, close-up perspective effect; a larger value is more subtle).
3D transforms are a deep topic on their own (often paired with transform-style: preserve-3d for nested 3D scenes like flip cards), and this course returns to them in more depth later — for now, know that rotateX/rotateY plus perspective exist as the building blocks.
Common transform functions
Transforms don't affect the box model or trigger layout reflow of surrounding elements. Order of chained functions matters — it changes the visual result.
.el {
transform: translate(20px, 10px);
transform: translateX(20px);
transform: scale(1.2);
transform: scale(1.2, 0.8);
transform: rotate(15deg);
transform: skewX(-10deg);
/* combined, applied left-to-right */
transform: translateX(10px) rotate(5deg) scale(1.1);
transform-origin: top left;
/* 3D */
transform: rotateY(35deg);
perspective: 800px; /* usually set on the parent */
}Code Examples
On hover, the card moves up 8 pixels and grows slightly larger — both changes are purely visual, so the surrounding grid layout never shifts or reflows. (The transition property, covered next lesson, is what makes this change animate smoothly instead of snapping instantly.) transform: translateY(-8px) scale(1.03);
Because the origin is set to the top-left corner, the element rotates as if pinned there, rather than spinning around its own middle — useful for effects like a flag waving from a fixed pole point.
.card {
transform: translateY(0) scale(1);
transition: transform 0.25s ease;
}
.card:hover {
transform: translateY(-8px) scale(1.03);
}.flag {
transform-origin: top left;
transform: rotate(3deg);
}transform-origin changes the pivot point
The ● marks the pivot point that rotate() and scale() use.
default (center) transform-origin: top left
┌────────┐ ┌────────┐
│ │ rotates around │ ● │ rotates around
│ ● │ the middle │ │ the corner
│ │ │ │
└────────┘ └────────┘Important Rules
translate() moves an element visually without affecting document flow or neighboring elements.
scale() resizes visually; values below 1 shrink, above 1 grow; can overlap neighbors visually since layout space is unchanged.
rotate() and scale() pivot around transform-origin, which defaults to the element's center (50% 50%).
Multiple transform functions in one declaration apply in the order written — order affects the result.
3D rotation (rotateX/rotateY) needs a perspective value (on the element or its parent) to look three-dimensional.
Where you'll see transforms in real interfaces
Nearly every modern interactive UI element — hoverable cards, modal dialogs that scale in, dropdown menus that slide down, mobile menu icons that morph into an X, image galleries with a zoom-on-hover effect — is built with transforms, usually paired with transitions or animations. Because transforms don't trigger layout, they're also the performance-friendly choice for anything animated frequently, a topic the animations lesson covers in detail..box { transform: translate(20px 10px); }.box { transform: translate(20px, 10px); }
Using top/left with position: absolute to build every hover-move effect
Use transform: translate() for purely visual movement that shouldn't affect layout top/left changes on a positioned element do affect stacking and can force layout recalculation; translate() is a compositor-friendly visual-only offset, generally cheaper and simpler for interactive effects.
[Identify the Error] A developer wants an element to shrink to 80% size and rotate 20 degrees when clicked, but this CSS has a bug. What's wrong? .btn:active { transform: scale(0.8); transform: rotate(20deg); } .btn:active { transform: scale(0.8); transform: rotate(20deg); } A. scale() and rotate() cannot both be used on the same element under any circumstances B. The second transform declaration overwrites the first entirely, so only the rotation is applied — they should be combined into one declaration: transform: scale(0.8) rotate(20deg); C. transform: rotate(20deg) needs a unit other than deg D. transform cannot be used with the :active pseudo-class
B. The second transform declaration overwrites the first entirely, so only the rotation is applied — they should be combined into one declaration: transform: scale(0.8) rotate(20deg); — Each transform declaration fully replaces the previous one for that element — CSS does not merge them. To apply both effects, they must appear as space-separated functions within a single transform declaration.
Write CSS for a .icon-button that rotates 90 degrees clockwise and grows to 110% size when hovered, pivoting around its center (the default)..icon-button:hover { transform: rotate(90deg) scale(1.1); }
Combine rotate() and scale() in one transform declaration inside a :hover selector; no need to set transform-origin since center is the default.