CSS 2D and 3D Transforms
The transform property allows you to visually reposition, resize, rotate, and skew elements without affecting page layout. Transformed elements don't push other elements around — they move in their own visual layer, making transforms extremely smooth to animate.
1. Transform Functions Overview
/* All transform functions can be combined in one rule */
.element {
transform: translateX(20px) rotate(15deg) scale(1.1);
}
2. Translate (Move)
Moves the element without affecting other elements' positions.
/* Single axis */
.up { transform: translateX(50px); } /* Right 50px */
.down { transform: translateY(-20px); } /* Up 20px */
/* Both axes */
.shifted { transform: translate(30px, -10px); }
/* Percentage of its OWN size — very useful! */
.centered {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* Classic absolute centering trick */
}
3. Rotate
.clockwise { transform: rotate(45deg); } /* Clockwise 45° */
.counter { transform: rotate(-90deg); } /* Counter-clockwise 90° */
.half-spin { transform: rotate(180deg); } /* Upside down */
/* Individual axes (3D) */
.card-flip { transform: rotateY(180deg); } /* Flip horizontal */
.lean { transform: rotateX(15deg); } /* Tilt toward viewer */
4. Scale
.larger { transform: scale(1.2); } /* 20% larger */
.smaller { transform: scale(0.8); } /* 20% smaller */
.x-stretch { transform: scaleX(2); } /* 2× width only */
.y-stretch { transform: scaleY(0.5); } /* Half height */
/* Common hover effect */
.card:hover { transform: scale(1.03); }
5. Skew
.skewed-x { transform: skewX(20deg); }
.skewed-y { transform: skewY(-10deg); }
.skewed-btn { transform: skew(-15deg, 0); } /* Slanted button/banner */
6. transform-origin
By default, all transforms happen from the element's center. transform-origin changes that point:
/* Syntax: x-position y-position (or keyword) */
.fold-open { transform-origin: left center; transform: rotateY(-90deg); } /* Fold from left */
.spin-corner { transform-origin: top left; transform: rotate(45deg); } /* Rotate from top-left */
.zoom-bottom { transform-origin: bottom center; transform: scale(0, 1); } /* Scale from bottom */
7. 3D Transforms
3D transforms require a perspective to be set on the parent container:
/* The parent sets perspective (distance from viewer to the 3D plane) */
.scene {
perspective: 800px; /* Smaller = more extreme effect */
}
/* Children use 3D transforms */
.card {
transform-style: preserve-3d; /* Children exist in 3D space */
transform: rotateY(20deg);
}
The Card Flip Component
.flip-card {
perspective: 1000px;
width: 300px;
height: 400px;
}
.flip-card__inner {
position: relative;
width: 100%;
height: 100%;
transform-style: preserve-3d;
transition: transform 0.7s cubic-bezier(0.4, 0.2, 0.2, 1);
}
.flip-card:hover .flip-card__inner { transform: rotateY(180deg); }
.flip-card__front,
.flip-card__back {
position: absolute;
inset: 0;
backface-visibility: hidden; /* Hides the back face when flipped away */
border-radius: 16px;
}
.flip-card__back { transform: rotateY(180deg); }
8. Combining Transforms
Order matters — transforms are applied right to left:
/* Different results! */
.a { transform: translateX(100px) rotate(45deg); } /* Translate THEN rotate */
.b { transform: rotate(45deg) translateX(100px); } /* Rotate THEN translate (moves diagonally) */
9. Matrix Transform
The matrix() and matrix3d() functions combine all transforms into a single mathematical expression. You won't write these by hand, but AI may generate them for complex animations:
/* These are equivalent: */
.a { transform: translateX(50px) rotate(30deg) scale(1.2); }
.b { transform: matrix(1.039, 0.6, -0.6, 1.039, 50, 0); }
10. Practical Patterns
Hover Lift Card
.card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-8px);
box-shadow: 0 16px 40px rgba(0,0,0,0.3);
}
Rotating Arrow Icon (Accordion)
.accordion__icon {
transition: transform 0.3s ease;
}
.accordion[open] .accordion__icon {
transform: rotate(180deg);
}
Parallax on Scroll with Transform
.parallax-layer {
transform: translateY(calc(var(--scroll-y, 0) * 0.3)); /* Updated via JS */
}