Back to the library

Quarantine hold, held verdict

Variant: Held verdict

Same close and sweep, but the cleared verdict is parked for nearly half the loop before the containment releases, so the resolved state is what the eye lands on most of the time. Use it when the outcome matters more than the process — a queue where most items clear, or a status chip a reviewer glances at. The long hold is pure delay structure: the sweep crosses at the same constant rate as the single beat, it just finishes earlier in a longer cycle.

moderationstatusverdictholdloop

The source

A complete HTML document. Paste it into an empty .html file and it plays — no stylesheet, script, font, or image to fetch.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Quarantine hold — held verdict</title>
<style>
:root {
  /* Neutrals — fixed, never remixed. */
  --ivory: #faf9f5;
  --paper: #ffffff;
  --oat: #e3dacc;
  --slate: #141413;

  /* Knobs — the remix API. A knob exists iff declared here and used below. */
  --hue: 38.8;
  --lift: 0;
  --chroma: 1;
  --round: 1;
  --speed: 1;

  /* Accents — derived from the knobs with the family's offsets baked in,
     so one hue roll moves them together. Defaults reproduce the original hex
     (#d97757 / #b85c3e / #788c5d / #b04a3f). */
  --clay: oklch(calc(0.6724 + var(--lift)) calc(0.1308 * var(--chroma)) var(--hue));
  --clay-d: oklch(calc(0.5797 + var(--lift)) calc(0.127 * var(--chroma)) var(--hue));
  --olive: oklch(calc(0.6118 + var(--lift)) calc(0.0713 * var(--chroma)) calc(var(--hue) + 88.3));
  --rust: oklch(calc(0.5408 + var(--lift)) calc(0.1357 * var(--chroma)) calc(var(--hue) - 10.3));

  /* Clock — every duration in the document runs off --t. */
  --cycle: 4.4s;
  --t: calc(var(--cycle) / var(--speed));

  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

  --_pip: calc(0.16s / var(--speed));
}

* { box-sizing: border-box; }

html, body { margin: 0; }

body {
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: var(--ivory);
  color: var(--slate);
  font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  padding: 16px;
}

.stage {
  width: min(360px, 92vw);
  display: grid;
  gap: clamp(10px, 3vw, 14px);
}

.plate {
  position: relative;
  padding: clamp(14px, 4vw, 18px);
  background: var(--paper);
  border: 1px solid var(--oat);
  border-radius: calc(14px * var(--round));
  overflow: hidden;
}

.item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: clamp(10px, 3vw, 13px);
  align-items: center;
  animation: hold var(--t) var(--ease-in-out) infinite;
}

.thumb {
  width: clamp(38px, 12vw, 46px);
  height: clamp(38px, 12vw, 46px);
  border-radius: calc(9px * var(--round));
  background: linear-gradient(135deg, var(--clay) 0%, var(--rust) 100%);
  position: relative;
  flex: none;
}

.thumb::after {
  content: "";
  position: absolute;
  inset: 28% 24% 30% 24%;
  border-radius: calc(3px * var(--round));
  background: var(--paper);
  opacity: 0.55;
}

.lines { display: grid; gap: 7px; }

.line {
  height: 8px;
  border-radius: 999px;
  background: var(--oat);
}

.line.a { width: 72%; }
.line.b { width: 44%; }

.frame {
  position: absolute;
  inset: clamp(7px, 2.4vw, 10px);
  pointer-events: none;
}

.corner {
  position: absolute;
  width: 16%;
  height: 26%;
  border: 2px solid var(--rust);
  opacity: 0;
  animation: clamp-in var(--t) var(--ease-out) infinite;
}

.corner.tl { top: 0; left: 0; border-right: 0; border-bottom: 0; border-top-left-radius: calc(5px * var(--round)); transform-origin: top left; }
.corner.tr { top: 0; right: 0; border-left: 0; border-bottom: 0; border-top-right-radius: calc(5px * var(--round)); transform-origin: top right; }
.corner.bl { bottom: 0; left: 0; border-right: 0; border-top: 0; border-bottom-left-radius: calc(5px * var(--round)); transform-origin: bottom left; }
.corner.br { bottom: 0; right: 0; border-left: 0; border-top: 0; border-bottom-right-radius: calc(5px * var(--round)); transform-origin: bottom right; }

.sweep {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -18%;
  width: 18%;
  background: linear-gradient(90deg,
    transparent 0%,
    color-mix(in srgb, var(--clay) 22%, transparent) 50%,
    transparent 100%);
  animation: pass var(--t) linear infinite;
}

.sweep::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 2px;
  background: var(--clay);
}

.status {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: clamp(11px, 3.2vw, 12.5px);
  letter-spacing: 0.04em;
}

.pip {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--rust);
  flex: none;
  animation: verdict var(--t) var(--ease-out) infinite;
}

.label { opacity: 0.72; }

.meter {
  height: 3px;
  border-radius: 999px;
  background: var(--oat);
  overflow: hidden;
}

.meter i {
  display: block;
  height: 100%;
  background: var(--clay-d);
  transform: scaleX(0);
  transform-origin: left center;
  animation: fill var(--t) linear infinite;
}

/* Sweep occupies the same real-time span as the single beat (~1.2s);
   the extra cycle length is spent sitting in the cleared state. */
@keyframes hold {
  0%, 5%      { opacity: 1; filter: saturate(1); transform: scale(1); }
  13%, 47%    { opacity: 0.58; filter: saturate(0.35); transform: scale(0.97); }
  56%, 100%   { opacity: 1; filter: saturate(1); transform: scale(1); }
}

@keyframes clamp-in {
  0%        { opacity: 0; transform: scale(1.5); }
  8%, 48%   { opacity: 1; transform: scale(1); }
  57%, 100% { opacity: 0; transform: scale(1.18); }
}

@keyframes pass {
  0%, 11%   { transform: translateX(0); opacity: 0; }
  13%       { transform: translateX(0); opacity: 1; }
  41%       { transform: translateX(640%); opacity: 1; }
  44%, 100% { transform: translateX(640%); opacity: 0; }
}

@keyframes fill {
  0%, 13%   { transform: scaleX(0); }
  41%, 100% { transform: scaleX(1); }
}

@keyframes verdict {
  0%, 43%   { background: var(--rust); transform: scale(0.94); }
  49%       { background: var(--olive); transform: scale(1); }
  100%      { background: var(--olive); transform: scale(1); }
}

@media (max-width: 380px) {
  .plate { padding: 13px; }
  .stage { gap: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  .item { animation: hold-rm calc(var(--t) * 1.8) ease-in-out infinite; }
  .corner { animation: clamp-rm calc(var(--t) * 1.8) ease infinite; }
  .sweep { display: none; }
  .meter i {
    animation: fill-rm calc(var(--t) * 1.8) linear infinite;
    transform: scaleX(1);
  }
  .pip { animation: verdict-rm calc(var(--t) * 1.8) ease infinite; }
  @keyframes hold-rm {
    0%, 5%    { opacity: 1; filter: saturate(1); }
    13%, 47%  { opacity: 0.68; filter: saturate(0.4); }
    56%, 100% { opacity: 1; filter: saturate(1); }
  }
  @keyframes clamp-rm {
    0%        { opacity: 0.15; }
    8%, 48%   { opacity: 1; }
    57%, 100% { opacity: 0.15; }
  }
  @keyframes fill-rm {
    0%, 13%   { opacity: 0.25; }
    41%, 100% { opacity: 1; }
  }
  @keyframes verdict-rm {
    0%, 43% { background: var(--rust); }
    49%     { background: var(--olive); }
    100%    { background: var(--olive); }
  }
}
</style>
</head>
<body>
  <div class="stage">
    <div class="plate">
      <div class="item">
        <div class="thumb"></div>
        <div class="lines">
          <div class="line a"></div>
          <div class="line b"></div>
        </div>
      </div>
      <div class="frame">
        <span class="corner tl"></span>
        <span class="corner tr"></span>
        <span class="corner bl"></span>
        <span class="corner br"></span>
      </div>
      <div class="sweep"></div>
    </div>
    <div class="meter"><i></i></div>
    <div class="status">
      <span class="pip"></span>
      <span class="label">Held for review</span>
    </div>
  </div>
</body>
</html>

The variant family

Same idea, one axis moved. Compare them side by side before you commit.