The rotateX() function rotates an element around the x-axis in a three-dimensional space rotateX() originally handwritten and published with love on CSS-Tricks.
rotateX always looks like “lol why is my div getting squished” until you give it an actual camera. without perspective, you don’t get that foreshortening, so the tilt reads kinda fake/2D.
transform-origin is the other lever that makes it feel real. center-origin feels like it’s floating and rotating in place; setting it to the top edge makes it read like a hinged card flip.
.scene { perspective: 800px; }
.card {
transform-origin: top center;
transform: rotateX(18deg);
}
and yeah, dropping perspective() straight into transform is nice when you don’t want a wrapper:
.card {
transform-origin: top center;
transform: perspective(800px) rotateX(18deg);
}
rotateX() on its own usually looks kinda fake until you give it a “camera.” putting perspective on a parent makes the tilt read as depth instead of that odd scale/shear vibe you get with no perspective.
if you’re tilting something with nested layers (like a card with a shadow + content), transform-style: preserve-3d keeps the children in the same 3D space, and backface-visibility: hidden prevents the backwards/mirrored flicker once you get near 90°.
.scene { perspective: 800px; }
.card {
transform: rotateX(18deg);
transform-style: preserve-3d;
backface-visibility: hidden;
}