Skip to content
Luna UI
Luna UI
loading webcore y2k
please wait…
HTML
<div class="tv-webcore-hourglass" role="status">
  <svg class="tv-webcore-hourglass-cursor" viewBox="0 0 16 16" aria-hidden="true">
    <path class="tv-webcore-hourglass-paper" d="M1.5 1.5v12l3-3 2.5 4.5 2-1-2.5-4.5h4.5z" />
  </svg>
  <svg class="tv-webcore-hourglass-glass" viewBox="0 0 16 16" aria-hidden="true">
    <path class="tv-webcore-hourglass-paper" d="M4.5 3.5h7v2l-2.5 2.5 2.5 2.5v2h-7v-2l2.5-2.5-2.5-2.5z" />
    <path class="tv-webcore-hourglass-sand tv-webcore-hourglass-top" d="M5 4h6v1.5L8 7.5 5 5.5z" />
    <rect class="tv-webcore-hourglass-sand tv-webcore-hourglass-stream" x="7.5" y="7.5" width="1" height="4" />
    <path class="tv-webcore-hourglass-sand tv-webcore-hourglass-pile" d="M5 12h6v-1.5L8 8.5 5 10.5z" />
    <rect class="tv-webcore-hourglass-cap" x="3" y="1" width="10" height="2" />
    <rect class="tv-webcore-hourglass-cap" x="3" y="13" width="10" height="2" />
  </svg>
  <span class="tv-webcore-hourglass-label">please wait…</span>
</div>
CSS
/* A pixel hourglass: the top sand drains into the pile, then the glass flips and starts over. */
.tv-webcore-hourglass {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  color: var(--tv-webcore-navy, #2c2b9f);
  font: 14px/1.2 "Lucida Console", "Courier New", ui-monospace, monospace;
  letter-spacing: 0.06em;
}

.tv-webcore-hourglass svg {
  flex-shrink: 0;
  shape-rendering: crispEdges;
  overflow: visible;
}

.tv-webcore-hourglass-glass {
  width: 64px;
  height: 64px;
  animation: tv-webcore-hourglass-flip 3s steps(6) infinite;
}

.tv-webcore-hourglass-paper {
  fill: var(--tv-webcore-paper, #ffffff);
  stroke: var(--tv-webcore-navy, #2c2b9f);
  stroke-width: 1;
}

.tv-webcore-hourglass-cap {
  fill: var(--tv-webcore-navy, #2c2b9f);
}

.tv-webcore-hourglass-sand {
  fill: var(--tv-webcore-pink, #f7b5e4);
  transform-box: fill-box;
}

.tv-webcore-hourglass-top {
  transform-origin: center bottom;
  animation: tv-webcore-hourglass-drain 3s steps(8) infinite;
}

.tv-webcore-hourglass-pile {
  transform-origin: center bottom;
  animation: tv-webcore-hourglass-fill 3s steps(8) infinite;
}

.tv-webcore-hourglass-stream {
  animation: tv-webcore-hourglass-stream 3s steps(1) infinite;
}

/* Sand runs for the first 80% of the cycle; the flip takes the rest. */
@keyframes tv-webcore-hourglass-flip {
  0%, 80% { transform: rotate(0deg); }
  100% { transform: rotate(180deg); }
}

@keyframes tv-webcore-hourglass-drain {
  0% { transform: scaleY(1); }
  75%, 100% { transform: scaleY(0); }
}

@keyframes tv-webcore-hourglass-fill {
  0% { transform: scaleY(0); }
  75%, 100% { transform: scaleY(1); }
}

@keyframes tv-webcore-hourglass-stream {
  0%, 74% { opacity: 1; }
  75%, 100% { opacity: 0; }
}

.tv-webcore-hourglass-cursor {
  display: none;
  width: 48px;
  height: 48px;
}

/* With cursor: a pixel arrow with a small hourglass tucked under its tip, like a busy desktop. */
.tv-webcore-hourglass--cursor {
  gap: 0;
}

.tv-webcore-hourglass--cursor .tv-webcore-hourglass-cursor {
  display: block;
}

.tv-webcore-hourglass--cursor .tv-webcore-hourglass-glass {
  width: 40px;
  height: 40px;
  margin: 28px 14px 0 -10px;
}

/* Still: a half-run glass with no stream. */
@media (prefers-reduced-motion: reduce) {
  .tv-webcore-hourglass-glass,
  .tv-webcore-hourglass-top,
  .tv-webcore-hourglass-pile,
  .tv-webcore-hourglass-stream {
    animation: none;
  }

  .tv-webcore-hourglass-top,
  .tv-webcore-hourglass-pile {
    transform: scaleY(0.5);
  }
}
Markdown MIT License
Usage & credits
Use when
Short waits on Y2K pages: loading a guestbook, sending a form, a fake install.
Avoid when
Long waits that need real progress, or more than one busy indicator on screen.
Requires
Change the label to describe what is loading; it is announced through role="status".