Eight tiles stand in a tilted 3D ring that turns while a wave runs around it, lifting each tile in turn. Tiles show pale backs as they pass behind; the wave rides on the separate translate property.
Also known as: 3d carousel loader, 3d ring of squares, rotating tiles wave.
<div class="ldr-tile-ring-3d" role="status" aria-label="Loading">
<div class="ldr-tile-ring-3d-ring">
<span style="--i: 0"></span><span style="--i: 1"></span><span style="--i: 2"></span><span style="--i: 3"></span>
<span style="--i: 4"></span><span style="--i: 5"></span><span style="--i: 6"></span><span style="--i: 7"></span>
</div>
</div>.ldr-tile-ring-3d {
--s: var(--ldr-size, 48px);
--c: var(--ldr-color, #3b82f6);
display: grid;
place-items: center;
width: calc(var(--s) * 1.4);
height: var(--s);
perspective: calc(var(--s) * 5);
}
.ldr-tile-ring-3d-ring {
position: relative;
width: calc(var(--s) * 0.26);
height: calc(var(--s) * 0.26);
transform-style: preserve-3d;
animation: ldr-tile-ring-3d-turn 4s linear infinite;
}
.ldr-tile-ring-3d span {
position: absolute;
inset: 0;
transform-style: preserve-3d;
transform: rotateY(calc(var(--i) * 45deg)) translateZ(calc(var(--s) * 0.5));
animation: ldr-tile-ring-3d-wave 1.6s cubic-bezier(0.45, 0, 0.55, 1) infinite;
animation-delay: calc(var(--i) * -0.4s);
}
.ldr-tile-ring-3d span::before,
.ldr-tile-ring-3d span::after {
content: "";
position: absolute;
inset: 0;
border-radius: calc(var(--s) / 40);
backface-visibility: hidden;
}
.ldr-tile-ring-3d span::before {
background: var(--c);
}
.ldr-tile-ring-3d span::after {
background: color-mix(in srgb, var(--ldr-color, #3b82f6) 28%, transparent);
transform: rotateY(180deg);
}
@keyframes ldr-tile-ring-3d-wave {
0%, 100% { translate: 0 calc(var(--s) * -0.09); }
50% { translate: 0 calc(var(--s) * 0.09); }
}
@keyframes ldr-tile-ring-3d-turn {
from { transform: rotateX(-14deg) rotateY(0deg); }
to { transform: rotateX(-14deg) rotateY(360deg); }
}
@media (prefers-reduced-motion: reduce) {
.ldr-tile-ring-3d, .ldr-tile-ring-3d *, .ldr-tile-ring-3d::before, .ldr-tile-ring-3d::after, .ldr-tile-ring-3d *::before, .ldr-tile-ring-3d *::after {
animation-duration: 3s;
}
}--ldr-color on the element or a parent.
Currently previewing #3b82f6.--ldr-size. Every dimension in the snippet is
derived from it. Currently previewing 64px.animation shorthand.
Delays on the children are relative, so they keep their rhythm.role="status" and an
aria-label so screen readers announce the wait. The reduced-motion rule
at the end slows the animation for users who asked for less motion.