Why This Question Exists
Card Flip tests a specific CSS 3D transform technique that trips up most candidates. The immediate instinct is to toggle between two divs using conditional rendering, but that produces no animation. The correct solution uses CSS transform-style: preserve-3d and backface-visibility: hidden — two properties that most developers have heard of but never truly understood.
The Wrong Approach: Conditional Render
// ❌ WRONG — no animation, just swap
{isFlipped ? <div>Back</div> : <div>Front</div>}
// Even adding a transition here doesn't help — you're toggling different elements
The Three-Layer Structure
/* Layer 1: Perspective context — NEVER rotates itself */
.scene {
perspective: 800px; /* distance of viewer — larger = more subtle 3D */
width: 220px;
height: 140px;
}
/* Layer 2: The card — this is what ROTATES */
.card {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d; /* children live in 3D space */
transition: transform 0.6s;
}
.card.flipped {
transform: rotateY(180deg);
}
/* Layer 3: Both faces — occupy the SAME space */
.face {
position: absolute;
inset: 0;
backface-visibility: hidden; /* hide when rotated > 90deg */
}
.back {
transform: rotateY(180deg); /* starts facing away — hidden initially */
}
ℹ Interview TipSay: "The three layers are: perspective wrapper (sets the 3D viewing distance and never moves), the flipper (rotates 180deg on click, has transform-style: preserve-3d so children exist in 3D space), and the two faces (absolute-positioned on top of each other, backface-visibility: hidden so only the forward-facing one is visible at any time)."
Why backface-visibility: hidden is required
/* Without backface-visibility: hidden: */
/* At 0deg: front visible, BACK ALSO VISIBLE (mirrored through) */
/* At 90deg: edge-on, neither visible */
/* At 180deg: back visible, FRONT ALSO VISIBLE (mirrored through) */
/* With backface-visibility: hidden: */
/* At 0deg: front visible only */
/* At 90deg: nothing visible (both hidden) */
/* At 180deg: back visible only — correct! */
This property hides an element when its backface (the 180°-rotated side) is facing the viewer. Without it, both faces would bleed through each other and you'd see a confusing overlap.
Why preserve-3d is required
/* Without transform-style: preserve-3d on .card: */
/* The browser flattens children into 2D before compositing */
/* Both faces are flat images that cross-fade, not 3D objects */
/* With transform-style: preserve-3d: */
/* Children (front, back) are positioned in the same 3D space */
/* The rotation of .card rotates both children in that 3D space */
Without preserve-3d, the browser composites the children as flat 2D textures before applying the parent's transform. The flip looks like a flat box rotating, not a card with two sides.
⚠ Common Pitfall: Adding perspective to the card itselfIf you add perspective to .card instead of a parent wrapper, the perspective origin moves with the card during rotation — causing a disorienting "fish-eye" effect. Perspective must be set on a non-rotating ancestor.