Pure CSS Loaders & patterns #loop

Tile wave in pure CSS

A 4×4 grid of two-sided tiles flipping in a diagonal wave.

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

How it works

  1. Each tile has two faces from ::before and ::after, back to back, both with backface-visibility: hidden.
  2. Every tile runs the same flip animation.
  3. The wave comes purely from animation-delay = (row + column) × step. Each tile sets its --d, row + column, in its style.
  4. Every length is a multiple of one base unit, --u, tied to the canvas: a tile is 48 units square, 8 apart, and sinks 14 as it turns, so the wave is the same share of a gallery card, the editor and a recording canvas.

Key techniques

  • backface-visibility
  • delay = (row + column) × step
  • --d per tile in its style

Copy-paste code

HTML 8 lines

<div class="scene">
  <div class="tiles">
    <i style="--d:0"></i><i style="--d:1"></i><i style="--d:2"></i><i style="--d:3"></i>
    <i style="--d:1"></i><i style="--d:2"></i><i style="--d:3"></i><i style="--d:4"></i>
    <i style="--d:2"></i><i style="--d:3"></i><i style="--d:4"></i><i style="--d:5"></i>
    <i style="--d:3"></i><i style="--d:4"></i><i style="--d:5"></i><i style="--d:6"></i>
  </div>
</div>

CSS 40 lines

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

.tiles {
  display: grid;
  grid-template-columns: repeat(4, calc(48 * var(--u)));
  gap: calc(8 * var(--u));
  transform-style: preserve-3d;
  transform: rotateX(38deg) rotateZ(-8deg);
}

.tiles i {
  position: relative;
  height: calc(48 * var(--u));
  transform-style: preserve-3d;
  animation: tile-flip 3.2s ease-in-out infinite;
  animation-delay: calc(var(--d) * 0.13s);   /* --d = row + column */
}

.tiles i::before,
.tiles i::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: calc(8 * var(--u));
  backface-visibility: hidden;
}

.tiles i::before { background: #8b6cff; }
.tiles i::after  { background: #2ee6d6; transform: rotateY(180deg); }

@keyframes tile-flip {
  0%, 15%   { transform: rotateY(0deg); }
  45%, 65%  { transform: rotateY(180deg) translateZ(calc(-14 * var(--u))); }
  95%, 100% { transform: rotateY(360deg); }
}
Standalone snippet · plain CSS, no build step, no dependencies.