Episode 1 — Fundamentals / 1.12 — CSS Animations and Motion Design

1.12.c — 3D Transforms

In one sentence: CSS 3D transforms add a depth axis (Z) and a camera-like perspective so you can rotate elements in 3D space, build card flips, and create spatial UI — all without JavaScript or WebGL.

Navigation: ← 1.12.b — CSS Transform · 1.12.d — Keyframe Animations →


1. Perspective — the virtual camera

Without perspective, 3D rotations look flat. Perspective controls how "far" the viewer is from the Z = 0 plane.

.scene {
  perspective: 800px;
}
  perspective: 200px  (close)        perspective: 1000px  (far away)
  ╱─────────────╲                    ┌───────────────┐
 ╱               ╲                   │               │
╱     extreme     ╲                  │    subtle     │
╲   foreshortening╱                  │  foreshortening│
 ╲               ╱                   │               │
  ╲─────────────╱                    └───────────────┘
ApproachSyntaxScope
Parent propertyperspective: 800px on the containerShared vanishing point for all children
Function in transformtransform: perspective(800px) rotateY(45deg)Per-element; each element has its own vanishing point

Tip: for multiple cards in a grid, use perspective on the parent so they share one natural vanishing point.

perspective-origin

Moves the vanishing point (default: center of the container):

.scene {
  perspective: 800px;
  perspective-origin: left top;
}

2. 3D translate

FunctionAxis
translateZ(value)Toward / away from viewer
translate3d(x, y, z)All three axes at once
.popup {
  transform: translateZ(60px);  /* comes toward viewer */
}

Positive Z = closer (appears larger under perspective). Negative Z = farther (appears smaller).


3. 3D rotate

FunctionWhat it does
rotateX(angle)Tip forward/backward (like a garage door)
rotateY(angle)Spin left/right (like a revolving door)
rotateZ(angle)Same as 2D rotate() — spin in-plane
rotate3d(x, y, z, angle)Rotate around an arbitrary axis vector
  rotateX(45deg)          rotateY(45deg)          rotateZ(45deg)
       ───                     │                     ╲
      ╱   ╲                   ╱│                      ╲───┐
     │     │                 ╱ │                       │   │
     │     │                ╱  │                       │   │
      ╲   ╱                    │                      ┌───╱
       ───                     │                     ╱
  (tips toward you)    (turns like a door)    (flat spin, same as 2D)

4. transform-style: preserve-3d

By default, child transforms are flattened into the parent's plane. To let children live in true 3D space:

.card-inner {
  transform-style: preserve-3d;
  transition: transform 600ms ease;
}

Without preserve-3d, a child rotated on a different axis than its parent will look flat — it won't "pop out" of the parent's plane.


5. backface-visibility

When an element rotates past 90°, its back face becomes visible. Control this:

.card-face {
  backface-visibility: hidden;  /* hide the back when facing away */
}
ValueBehavior
visible (default)Back face is shown (mirrored content)
hiddenElement is invisible when facing away from the viewer

6. Classic card-flip animation

<div class="card-scene">
  <div class="card">
    <div class="card__front">Front</div>
    <div class="card__back">Back</div>
  </div>
</div>
.card-scene {
  perspective: 600px;
  width: 300px;
  height: 200px;
}

.card {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  transition: transform 600ms ease;
}

.card-scene:hover .card {
  transform: rotateY(180deg);
}

.card__front,
.card__back {
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  border-radius: 8px;
}

.card__front {
  background: #3b82f6;
  color: white;
}

.card__back {
  background: #1e293b;
  color: white;
  transform: rotateY(180deg);
}

How it works step-by-step

  Initial state                  After hover (rotateY 180°)
  ┌───────────┐                  ┌───────────┐
  │           │                  │           │
  │   FRONT   │  ──rotateY──►   │   BACK    │
  │  (facing) │   (180deg)      │  (facing) │
  │           │                  │           │
  └───────────┘                  └───────────┘
  Front: backface visible        Front: backface hidden (gone)
  Back:  rotateY(180°) hidden    Back:  now at 360° total (visible)
  1. .card uses preserve-3d so both faces live in 3D.
  2. Both faces use backface-visibility: hidden.
  3. .card__back starts pre-rotated 180° (facing away).
  4. On hover, the whole .card rotates 180° — front hides, back reveals.

7. 3D carousel concept

.carousel {
  transform-style: preserve-3d;
}
.carousel__item:nth-child(1) { transform: rotateY(  0deg) translateZ(250px); }
.carousel__item:nth-child(2) { transform: rotateY( 60deg) translateZ(250px); }
.carousel__item:nth-child(3) { transform: rotateY(120deg) translateZ(250px); }
.carousel__item:nth-child(4) { transform: rotateY(180deg) translateZ(250px); }
.carousel__item:nth-child(5) { transform: rotateY(240deg) translateZ(250px); }
.carousel__item:nth-child(6) { transform: rotateY(300deg) translateZ(250px); }

Each item rotates to its slice of 360° then pushes outward on Z. Rotate the parent to spin the carousel.


8. Performance notes

  • 3D transforms promote elements to their own compositor layer (GPU texture).
  • Too many layers = high GPU memory. A page with hundreds of preserve-3d children can stutter.
  • Keep 3D transforms to focal elements: cards, modals, hero sections — not every list item.
  • Test on low-end devices; integrated GPUs have limited VRAM.

9. Key takeaways

  1. perspective creates depth — set it on the parent for shared vanishing point.
  2. rotateX/Y/Z spin elements in 3D; translateZ moves toward/away from the viewer.
  3. preserve-3d lets nested elements exist in shared 3D space.
  4. backface-visibility: hidden is essential for card-flip patterns.
  5. Use 3D transforms sparingly — they consume GPU memory per promoted layer.

Explain-It Challenge

Explain without notes:

  1. The difference between perspective: 800px on a parent vs transform: perspective(800px) on the element itself — when does each produce different results?
  2. Why both card faces need backface-visibility: hidden and why the back face starts with rotateY(180deg).
  3. Why a carousel with 50 items using preserve-3d might perform poorly on a mobile device.

Navigation: ← 1.12.b — CSS Transform · 1.12.d — Keyframe Animations →