Pure CSS Scenes & objects #loop

Folding map in pure CSS

Nested panels, each hinged on its parent’s edge, folding up like a paper map.

  • 39 lines of CSS
  • 11 lines of HTML
  • No JavaScript
  • No dependencies
  • MIT licensed

How it works

  1. Panels are nested, not siblings: each one lives inside the previous one, so it inherits every fold before it.
  2. Each nested panel is positioned at left: 100% with transform-origin: left — a hinge on the parent’s right edge.
  3. Every level needs transform-style: preserve-3d, or the chain flattens at that level.
  4. Alternate the fold direction (+150° / −150°) for a zig-zag.

Key techniques

  • nested preserve-3d
  • transform-origin: left
  • alternating keyframes

Copy-paste code

HTML 11 lines

<div class="scene">
  <div class="map">
    <div class="panel">
      <div class="panel">
        <div class="panel">
          <div class="panel"></div>
        </div>
      </div>
    </div>
  </div>
</div>

CSS 39 lines

.scene {
  perspective: 900px;
}

.map {
  width: 240px;          /* 4 panels × 60px */
  height: 140px;
  transform-style: preserve-3d;
  transform: rotateX(28deg) rotateY(-8deg);
}

.panel {
  width: 60px;
  height: 100%;
  transform-style: preserve-3d;
  background: linear-gradient(90deg, #2ee6d6, #157a72);
  border: 1px solid rgb(255 255 255 / 0.25);
}

.panel .panel {
  position: absolute;
  top: -1px;
  left: 100%;
  transform-origin: left center;
  animation: fold-back 3.4s ease-in-out infinite alternate;
}

.panel .panel .panel        { animation-name: fold-front; }
.panel .panel .panel .panel { animation-name: fold-back; }

@keyframes fold-back {
  0%, 12%   { transform: rotateY(0deg); }
  88%, 100% { transform: rotateY(150deg); }
}

@keyframes fold-front {
  0%, 12%   { transform: rotateY(0deg); }
  88%, 100% { transform: rotateY(-150deg); }
}

Sass source 58 lines

$panel: 54px;

.d-fold {
  width: $panel * 4;
  height: 124px;
  transform-style: preserve-3d;
  transform: rotateX(28deg) rotateY(-8deg);

  .p {
    width: $panel;
    height: 100%;
    transform-style: preserve-3d;
    background: linear-gradient(90deg, var(--accent-2), color-mix(in srgb, var(--accent-2) 55%, #000));
    border: 1px solid rgb(255 255 255 / 0.25);
  }

  // Every nested panel hangs off its parent's right edge and hinges on its own left edge.
  .p .p {
    position: absolute;
    top: -1px;
    left: 100%;
    transform-origin: left center;
    animation: d-fold-back 3.4s ease-in-out infinite alternate;
  }

  // Alternate the direction to get a zig-zag.
  .p .p .p {
    animation-name: d-fold-front;
  }

  .p .p .p .p {
    animation-name: d-fold-back;
  }
}

@keyframes d-fold-back {
  0%,
  12% {
    transform: rotateY(0deg);
  }

  88%,
  100% {
    transform: rotateY(150deg);
  }
}

@keyframes d-fold-front {
  0%,
  12% {
    transform: rotateY(0deg);
  }

  88%,
  100% {
    transform: rotateY(-150deg);
  }
}
Standalone snippet · plain CSS, no build step, no dependencies.