The five properties
- perspective — set on the PARENT (600–1100px feels natural; too small = fisheye).
- transform-style: preserve-3d — lets children keep their own depth planes.
- backface-visibility: hidden — makes the flip card work by hiding the back face.
- rotateX / rotateY / translateZ — the moves themselves; rotate around an axis, translate along Z to layer.
- transform-origin — decide where the pivot sits (bottom for a rise, center for a flip).
Move 1 — the flip
flip.css
.scene { perspective: 1100px; }
.flipper {
position: relative; height: 220px;
transform-style: preserve-3d;
transition: transform .7s cubic-bezier(.4,.2,.2,1);
}
.flipper.is-flipped { transform: rotateY(180deg); }
.face {
position: absolute; inset: 0;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
.face--back { transform: rotateY(180deg); }Move 2 — the tilt
tilt.css
.tilt-card {
transform:
perspective(900px)
rotateX(calc((50 - var(--my, 50)) * 0.32deg))
rotateY(calc((var(--mx, 50) - 50) * 0.32deg));
transition: transform 120ms ease-out;
}
/* --mx / --my are set from pointermove as 0–100 percentages */Move 3 — the orbit ring
orbit.css
/* one spinning ring, items counter-rotate so they stay readable */
.ring { animation: spin 14s linear infinite; }
.ring-item {
position: absolute; top: 50%; left: 50%;
transform: rotate(var(--a)) translateX(var(--r)) rotate(calc(var(--a) * -1));
animation: spin 14s linear infinite reverse;
}
@keyframes spin { to { transform: rotate(360deg); } }The discipline of 3D
Less is tectonic
One tilted element per viewport maximum. Two competing 3D moves feel like a funhouse, not a product. And gate tilt/flip to fine pointers: on touch there's no hover, so make the 3D a click or a scroll response.