3D Transform & Perspective Playground
rotateX, rotateY and the three parent properties that have to agree before any of it looks right
Perspective is the distance from the viewer to the z=0 plane. Small numbers are a wide-angle lens — dramatic, distorted; large numbers flatten towards an isometric projection.
/* On the PARENT — this is the part that catches everyone out.
perspective on the transformed element itself does nothing useful. */
.scene {
perspective: 800px;
transform-style: preserve-3d;
}
.card {
transform: rotateX(-12deg) rotateY(24deg);
backface-visibility: hidden;
}<div class="scene">
<div class="card">…</div>
</div>Now use it on something
Every effect, block, page and template in the catalog is styled against tokens shaped exactly like these — so they take your values rather than fighting them.
npx shadcn@latest add https://hoverlab.dev/r/hoverlab.json
How far the viewer is from the z=0 plane. This is the property that has to live on the parent — set it on the element you are rotating and you get a flat squash instead of depth. Under about 400px it reads as a fisheye; over about 1500px it barely reads at all.
Where the viewer is standing, horizontally. Moving it off centre is how you get a scene that looks lit and composed rather than photographed head-on.
The same, vertically. Below 50% you are looking down at the scene; above, up at it.
Keeps children in the same 3D space as their parent. Off — the default, `flat` — every child is projected onto the parent's plane first, which is exactly why a card flip built from two faces shows both of them at once.
Tips the element towards or away from you, around the horizontal axis. Small negative values — the top leaning back — are the standard product-shot angle.
Turns it around the vertical axis. At exactly 180 you are looking at the back, which is where backface-visibility starts to matter.
A flat spin in the plane of the screen — the same thing plain `rotate` does. No perspective is involved, so this one looks identical whatever the scene is set to.
Moves the element towards the viewer or away. This is the one that needs perspective to do anything visible at all — with no perspective on the parent it is a no-op, which is the fastest way to confirm the scene is set up correctly.
Horizontal move, applied before the rotations so the element turns around where it ended up.
Vertical move.
Uniform resize. Applied last in the list, so it scales the already-rotated result rather than the original box.
Shears horizontally. Genuinely 2D — no amount of perspective makes a skew look three-dimensional, which is worth knowing before you spend an afternoon trying.
Shears vertically.
The pivot, horizontally. Moving it to an edge is the difference between a card spinning in place and a door swinging open.
The pivot, vertically. Bottom-centre is the hinge a dropdown wants; top-centre is the one a tooltip wants.
Hides the element once it has turned more than 90° away. Set rotateY past 90 with this off to see the mirrored back — and then understand why every card flip on the web sets it.
Swaps the single face for the two-sided card everyone is actually trying to build when they find rotateY. The copied CSS becomes the whole recipe — both faces, the transition, and the reduced-motion guard.
Keep this transform. This browser remembers what you last had open. A free account remembers the ones you name, on every machine you use.
- Meta & OG BuilderThe tags that get you indexed and the card your link turns into, with length counters and a search-result preview. Emits Next.js Metadata, not stray meta tags.
- Typography PlaygroundPick from 11 curated font pairings — each with the reasoning written down — tune weight / line-height / letter-spacing / paragraph-spacing, and generate a modular type scale with one of 5 ratio presets. Emits next/font, which self-hosts.
- Spacing ScaleGenerate a whole spacing ladder from two decisions: a base unit and a progression — linear multiples or one of the same five modular ratios the typography tool uses. Every step is drawn at its actual size, so you see whether neighbours are distinguishable before you commit. Export as CSS variables, Tailwind spacing config, or JSON.