Back to the library

Archive wrap confirmation, held verdict

Variant: Held verdict

A quick wrap followed by a long hold on the confirmed state before the loop resets, so the tick and label sit legible for most of the cycle. Use it where the confirmation must survive a glance away — a row that stays on screen after the action, or a list item the user will look back at. The band pass matches the single beat exactly; the whole extra time is spent in the settled state, not in the motion.

confirmationarchivestate-changeholdweave

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>Archive wrap confirmation — 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: 6s;
  --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);

  /* Band pass identical to the single beat; the long cycle is all dwell. */
  --_band: calc(0.26s / var(--speed));
  --_band-step: 0s;
  --_d1: calc(0.18s / var(--speed));
}

* { box-sizing: border-box; }

html, body { margin: 0; }

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

.stage {
  width: min(360px, 90vw);
  display: grid;
  gap: clamp(10px, 3vw, 14px);
  justify-items: center;
}

.card {
  position: relative;
  width: 100%;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(10px, 3vw, 14px);
  padding: clamp(12px, 3.6vw, 16px);
  background: var(--paper);
  border: 1px solid var(--oat);
  border-radius: calc(14px * var(--round));
  overflow: hidden;
  transform-origin: 50% 100%;
  animation: cardSettle var(--t) var(--ease-out) infinite;
}

.glyph {
  width: clamp(30px, 9vw, 36px);
  height: clamp(30px, 9vw, 36px);
  flex: none;
  border-radius: calc(9px * var(--round));
  background: color-mix(in oklab, var(--clay) 16%, var(--paper));
  display: grid;
  place-items: center;
}

.glyph svg { width: 62%; height: 62%; display: block; }

.lines { display: grid; gap: 7px; min-width: 0; }

.line {
  height: 7px;
  border-radius: 999px;
  background: var(--oat);
}
.line.a { width: 66%; background: color-mix(in oklab, var(--slate) 42%, var(--oat)); }
.line.b { width: 42%; }

.mark {
  width: clamp(24px, 7vw, 28px);
  height: clamp(24px, 7vw, 28px);
  flex: none;
}
.mark svg { width: 100%; height: 100%; display: block; overflow: visible; }

.mark .ring {
  fill: none;
  stroke: color-mix(in oklab, var(--olive) 40%, var(--oat));
  stroke-width: 2;
}

.mark .tick {
  fill: none;
  stroke: var(--olive);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 22;
  stroke-dashoffset: 22;
  animation: tickDraw var(--t) var(--ease-out) infinite;
}

.weave {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.band {
  position: absolute;
  left: 0;
  width: 100%;
  transform: translate3d(-102%, 0, 0);
  will-change: transform;
}

.band i {
  display: block;
  height: 100%;
  border-radius: 999px;
}

.band.b1 { top: 8%;  height: 22%; animation: sweep var(--_band) var(--ease-in-out) infinite; animation-delay: var(--_d1); }
.band.b2 { top: 39%; height: 22%; animation: sweep var(--_band) var(--ease-in-out) infinite; animation-delay: calc(var(--_d1) + var(--_band-step)); }
.band.b3 { top: 70%; height: 22%; animation: sweep var(--_band) var(--ease-in-out) infinite; animation-delay: calc(var(--_d1) + var(--_band-step) * 2); }

.band.b1 i { background: linear-gradient(90deg, transparent, color-mix(in oklab, var(--clay) 22%, transparent) 30%, color-mix(in oklab, var(--clay) 22%, transparent) 70%, transparent); }
.band.b2 i { background: linear-gradient(90deg, transparent, color-mix(in oklab, var(--rust) 18%, transparent) 30%, color-mix(in oklab, var(--rust) 18%, transparent) 70%, transparent); }
.band.b3 i { background: linear-gradient(90deg, transparent, color-mix(in oklab, var(--olive) 20%, transparent) 30%, color-mix(in oklab, var(--olive) 20%, transparent) 70%, transparent); }

.caption {
  font-size: clamp(11px, 3vw, 12.5px);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--slate) 52%, var(--ivory));
  opacity: 0.35;
  animation: capLift var(--t) var(--ease-out) infinite;
}

@keyframes sweep {
  from { transform: translate3d(-102%, 0, 0); }
  to   { transform: translate3d(102%, 0, 0); }
}

@keyframes cardSettle {
  0%, 6%    { transform: translate3d(0, 0, 0) scale(1); }
  11%       { transform: translate3d(0, 0, 0) scale(0.985); }
  19%, 100% { transform: translate3d(0, 0, 0) scale(1); }
}

/* Verdict is reached by ~22% of the cycle and held until the very end. */
@keyframes tickDraw {
  0%, 10%   { stroke-dashoffset: 22; opacity: 0.25; }
  17%       { opacity: 1; }
  22%, 95%  { stroke-dashoffset: 0; opacity: 1; }
  100%      { stroke-dashoffset: 22; opacity: 0.25; }
}

@keyframes capLift {
  0%, 12%  { opacity: 0.35; }
  23%, 94% { opacity: 1; }
  100%     { opacity: 0.35; }
}

@media (max-width: 380px) {
  .card { padding: 11px; gap: 9px; }
  .stage { gap: 9px; }
}

@media (prefers-reduced-motion: reduce) {
  .band { display: none; }
  .card { animation: none; }
  .mark .tick {
    stroke-dashoffset: 0;
    animation: tickFadeRM calc(var(--t) * 1.5) var(--ease-out) infinite;
  }
  .caption { animation: capLift calc(var(--t) * 1.5) var(--ease-out) infinite; }
  @keyframes tickFadeRM {
    0%, 10%  { opacity: 0.35; }
    22%, 95% { opacity: 1; }
    100%     { opacity: 0.35; }
  }
}
</style>
</head>
<body>
  <div class="stage">
    <div class="card">
      <div class="glyph">
        <svg viewBox="0 0 24 24" aria-hidden="true">
          <path d="M3 7h18v3H3z" fill="var(--clay-d)"></path>
          <path d="M5 10h14v10H5z" fill="var(--clay)"></path>
          <path d="M9.5 14h5" stroke="var(--paper)" stroke-width="1.8" stroke-linecap="round"></path>
        </svg>
      </div>
      <div class="lines">
        <div class="line a"></div>
        <div class="line b"></div>
      </div>
      <div class="mark">
        <svg viewBox="0 0 24 24" aria-hidden="true">
          <circle class="ring" cx="12" cy="12" r="10"></circle>
          <path class="tick" d="M7 12.4l3.4 3.4L17 9"></path>
        </svg>
      </div>
      <div class="weave" aria-hidden="true">
        <div class="band b1"><i></i></div>
        <div class="band b2"><i></i></div>
        <div class="band b3"><i></i></div>
      </div>
    </div>
    <div class="caption">Kept</div>
  </div>
</body>
</html>

The variant family

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