AI CSS Animations

Pixel Hourglass Loader

A pixel-art hourglass: sand pixels leave the top chamber one tick at a time and pile up in the bottom, then the glass flips. Each grain toggles with a stepped keyframe offset by its own delay.

Also known as: 8-bit hourglass loader, pixel art hourglass, retro sand timer loader.

HTML

<div class="ldr-pixel-hourglass" role="status" aria-label="Loading">
  <span style="--x: 3; --y: 1; --i: 0"></span><span style="--x: 2; --y: 1; --i: 1"></span><span style="--x: 4; --y: 1; --i: 2"></span>
  <span style="--x: 1; --y: 1; --i: 3"></span><span style="--x: 5; --y: 1; --i: 4"></span><span style="--x: 3; --y: 2; --i: 5"></span>
  <span style="--x: 2; --y: 2; --i: 6"></span><span style="--x: 4; --y: 2; --i: 7"></span><span style="--x: 3; --y: 3; --i: 8"></span>
  <span style="--x: 3; --y: 6; --i: 0; --o: 1"></span><span style="--x: 2; --y: 6; --i: 1; --o: 1"></span><span style="--x: 4; --y: 6; --i: 2; --o: 1"></span>
  <span style="--x: 1; --y: 6; --i: 3; --o: 1"></span><span style="--x: 5; --y: 6; --i: 4; --o: 1"></span><span style="--x: 3; --y: 5; --i: 5; --o: 1"></span>
  <span style="--x: 2; --y: 5; --i: 6; --o: 1"></span><span style="--x: 4; --y: 5; --i: 7; --o: 1"></span><span style="--x: 3; --y: 4; --i: 8; --o: 1"></span>
</div>

CSS

.ldr-pixel-hourglass {
  --s: var(--ldr-size, 48px);
  --c: var(--ldr-color, #3b82f6);
  --p: calc(var(--s) / 8);
  position: relative;
  width: calc(var(--p) * 7);
  height: var(--s);
  animation: ldr-pixel-hourglass-flip 4s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
.ldr-pixel-hourglass::before,
.ldr-pixel-hourglass::after {
  content: "";
  position: absolute;
  inset: 0;
}
.ldr-pixel-hourglass::before {
  background: color-mix(in srgb, var(--ldr-color, #3b82f6) 14%, transparent);
  clip-path: polygon(14.29% 12.5%, 85.71% 12.5%, 85.71% 25%, 71.43% 25%, 71.43% 37.5%, 57.14% 37.5%, 57.14% 62.5%, 71.43% 62.5%, 71.43% 75%, 85.71% 75%, 85.71% 87.5%, 14.29% 87.5%, 14.29% 75%, 28.57% 75%, 28.57% 62.5%, 42.86% 62.5%, 42.86% 37.5%, 28.57% 37.5%, 28.57% 25%, 14.29% 25%);
}
.ldr-pixel-hourglass::after {
  background:
    linear-gradient(var(--c) 0 0) top / 100% 11% no-repeat,
    linear-gradient(var(--c) 0 0) bottom / 100% 11% no-repeat;
}
.ldr-pixel-hourglass span {
  --o: 0;
  position: absolute;
  left: calc(var(--x) * var(--p));
  top: calc(var(--y) * var(--p));
  width: var(--p);
  height: var(--p);
  border: calc(var(--p) * 0.08) solid transparent;
  background: var(--c) padding-box;
  animation: ldr-pixel-hourglass-grain 4s step-end infinite;
  animation-delay: calc(var(--i) * 0.16s + var(--o) * 2.08s - 3.88s);
}
@keyframes ldr-pixel-hourglass-grain {
  0%       { opacity: 0; }
  50%, 100% { opacity: 1; }
}
@keyframes ldr-pixel-hourglass-flip {
  0%, 40%  { transform: rotate(0deg); }
  50%, 90% { transform: rotate(180deg); }
  100%     { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .ldr-pixel-hourglass, .ldr-pixel-hourglass *, .ldr-pixel-hourglass::before, .ldr-pixel-hourglass::after, .ldr-pixel-hourglass *::before, .ldr-pixel-hourglass *::after {
    animation-duration: 3s;
  }
}

Customizing this loader

  • Color: set --ldr-color on the element or a parent. Currently previewing #3b82f6.
  • Size: set --ldr-size. Every dimension in the snippet is derived from it. Currently previewing 64px.
  • Speed: change the duration on the animation shorthand. Delays on the children are relative, so they keep their rhythm.
  • Accessibility: the root carries 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.

Similar loaders