Pure CSS Shapes & solids #loop

Exploded layers in pure CSS

Isometric plates that rest spread apart along the Z axis and close up together, each placed by one custom property.

  • 30 lines of CSS
  • 8 lines of HTML
  • No JavaScript
  • No dependencies
  • Snippet under MIT
GitHub

How it works

  1. rotateX(58deg) rotateZ(-45deg) on the parent gives the classic isometric viewing angle.
  2. All plates are stacked at the same position; only translateZ separates them.
  3. Each plate carries its index in --i, and its resting offset is calc((var(--i) - 1.5) * 40 * var(--u)) — one line, four different results.
  4. The 1.5 is what keeps the deck honest. Spread from 0 and the stack climbs off the top of its own box; spread from the middle and it opens both ways, so the picture stays where the box is however far it is open.
  5. The single keyframe closes the deck: to { translateZ(0) }. An alternate animation starts from the element’s own transform, so the pose a paused card shows is the open one.
  6. Every length is a multiple of one base unit, --u, so the deck is the same share of a gallery card, the editor and a recording canvas. A plate is 140 of those units square.

Key techniques

  • rotateX + rotateZ isometric view
  • translateZ
  • var() in the resting pose, one keyframe to close

Copy-paste code

HTML 8 lines

<div class="scene">
  <div class="layers">
    <i style="--i:0"></i>
    <i style="--i:1"></i>
    <i style="--i:2"></i>
    <i style="--i:3"></i>
  </div>
</div>

CSS 30 lines

.scene {
  /* one base unit: every length below is a multiple of it, so the deck is the same share of a
     card, the editor, a full screen and a recording canvas */
  --u: 0.32vmin;
  perspective: calc(900 * var(--u));
}

.layers {
  position: relative;
  width: calc(140 * var(--u));
  height: calc(140 * var(--u));
  transform-style: preserve-3d;
  transform: rotateX(58deg) rotateZ(-45deg);
}

/* the deck rests open, 40 units between plates, measured from the MIDDLE of the four: it grows
   both ways at once, so what is drawn stays centred on the model box at every moment */
.layers i {
  position: absolute;
  inset: 0;
  border-radius: calc(16 * var(--u));
  border: calc(1 * var(--u)) solid rgb(255 255 255 / 0.35);
  background: hsl(calc(255 + var(--i) * 32) 85% 62% / 0.78);
  transform: translateZ(calc((var(--i) - 1.5) * 40 * var(--u)));
  animation: close 2.6s ease-in-out infinite alternate;
}

@keyframes close {
  to { transform: translateZ(0); }
}
Standalone snippet · plain CSS, no build step, no dependencies.