Back to the library

Handoff baton pass, bold

Variant: Bold

The heaviest reading: 3px rings and rail, a large token with a wide soft halo, a taller arc, and labels that go nearly black on arrival. Use it on a standalone status screen, a kiosk, or an empty-state panel where the rail is the only thing on the page. The travel arc grows with the token so the baton still clears the rail visually rather than dragging through it; every keyframe percentage is unchanged from Standard.

handoffprogressstateboldstatus

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>Handoff baton pass — Bold</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.2s;
  --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);

  /* File-specific: rail geometry and per-variant weight. */
  --_ring: 3px;
  --_rail: 3px;
  --_token: 18px;
  --_lift-y: -20%;
  --_dim: 0.4;
}

* { 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;
}

.card {
  width: min(420px, 92vw);
  padding: clamp(18px, 5vw, 26px) clamp(16px, 5vw, 26px);
  background: var(--paper);
  border: 1px solid var(--oat);
  border-radius: calc(14px * var(--round));
}

.head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: clamp(20px, 6vw, 28px);
}

.title {
  font-size: clamp(12.5px, 3.4vw, 14px);
  font-weight: 600;
  letter-spacing: 0.01em;
}

.sub {
  font-size: clamp(11px, 3vw, 12px);
  color: color-mix(in oklab, var(--slate) 52%, var(--ivory));
}

.rail {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: start;
}

.line {
  position: absolute;
  left: 16.666%;
  right: 16.666%;
  top: calc(var(--_token) + 6px);
  height: var(--_rail);
  background: var(--oat);
  border-radius: 999px;
}

.station {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 10px;
}

.dot {
  position: relative;
  width: calc(var(--_token) + 12px);
  height: calc(var(--_token) + 12px);
  border-radius: 50%;
  border: var(--_ring) solid var(--oat);
  background: var(--paper);
  display: grid;
  place-items: center;
}

.core {
  width: var(--_token);
  height: var(--_token);
  border-radius: 50%;
  background: var(--clay);
  opacity: 0;
}

.label {
  font-size: clamp(10.5px, 2.9vw, 11.5px);
  text-align: center;
  color: color-mix(in oklab, var(--slate) 78%, var(--ivory));
  opacity: var(--_dim);
}

.baton {
  position: absolute;
  top: 6px;
  left: 16.666%;
  width: var(--_token);
  height: var(--_token);
  margin-left: calc(var(--_token) / -2);
  border-radius: 50%;
  background: var(--clay);
  box-shadow: 0 0 0 calc(var(--_ring) + 4px) color-mix(in oklab, var(--clay) 22%, transparent);
  animation: hop var(--t) var(--ease-in-out) infinite;
}

@keyframes hop {
  0%, 16%   { transform: translate(0, 0) scale(1); }
  22%       { transform: translate(0, var(--_lift-y)) scale(0.97); }
  38%, 54%  { transform: translate(100%, 0) scale(1); }
  60%       { transform: translate(100%, var(--_lift-y)) scale(0.97); }
  76%, 92%  { transform: translate(200%, 0) scale(1); }
  /* Fade-return instead of sliding back, so the loop never reads as a reversal. */
  96%       { transform: translate(200%, 0) scale(1); opacity: 1; }
  97.5%     { transform: translate(200%, 0) scale(0.94); opacity: 0; }
  98%       { transform: translate(0, 0) scale(0.94); opacity: 0; }
  100%      { transform: translate(0, 0) scale(1); opacity: 1; }
}

.s1 .dot { animation: held var(--t) var(--ease-out) infinite; }
.s2 .dot { animation: held var(--t) var(--ease-out) calc(0.38 * var(--t)) infinite; }
.s3 .dot { animation: held var(--t) var(--ease-out) calc(0.76 * var(--t)) infinite; }

@keyframes held {
  0%       { border-color: var(--clay); transform: scale(1); }
  6%       { border-color: var(--clay); transform: scale(1.08); }
  16%      { border-color: var(--clay); transform: scale(1); }
  34%      { border-color: var(--oat);  transform: scale(1); }
  100%     { border-color: var(--oat);  transform: scale(1); }
}

.s1 .label { animation: lit var(--t) var(--ease-out) infinite; }
.s2 .label { animation: lit var(--t) var(--ease-out) calc(0.38 * var(--t)) infinite; }
.s3 .label { animation: lit var(--t) var(--ease-out) calc(0.76 * var(--t)) infinite; }

@keyframes lit {
  0%, 20%  { opacity: 1; }
  36%      { opacity: var(--_dim); }
  100%     { opacity: var(--_dim); }
}

@media (max-width: 380px) {
  .card { padding: 16px 12px; }
  .head { margin-bottom: 18px; }
}

@media (prefers-reduced-motion: reduce) {
  .baton { display: none; }
  .s1 .dot, .s2 .dot, .s3 .dot { animation: heldCalm calc(var(--t) * 2) var(--ease-out) infinite; }
  .s2 .dot { animation-delay: calc(0.38 * var(--t) * 2); }
  .s3 .dot { animation-delay: calc(0.76 * var(--t) * 2); }
  .s1 .label, .s2 .label, .s3 .label { animation: lit calc(var(--t) * 2) var(--ease-out) infinite; }
  .s2 .label { animation-delay: calc(0.38 * var(--t) * 2); }
  .s3 .label { animation-delay: calc(0.76 * var(--t) * 2); }
  .core { opacity: 1; animation: coreCalm calc(var(--t) * 2) var(--ease-out) infinite; }
  .s2 .core { animation-delay: calc(0.38 * var(--t) * 2); }
  .s3 .core { animation-delay: calc(0.76 * var(--t) * 2); }
}

@keyframes heldCalm {
  0%, 16%  { border-color: var(--clay); }
  34%      { border-color: var(--oat); }
  100%     { border-color: var(--oat); }
}

@keyframes coreCalm {
  0%, 16%  { opacity: 1; }
  34%      { opacity: 0.12; }
  100%     { opacity: 0.12; }
}
</style>
</head>
<body>
  <div class="card">
    <div class="head">
      <span class="title">Request handoff</span>
      <span class="sub">in transit</span>
    </div>
    <div class="rail">
      <div class="line"></div>
      <div class="station s1"><div class="dot"><div class="core"></div></div><div class="label">Intake</div></div>
      <div class="station s2"><div class="dot"><div class="core"></div></div><div class="label">Review</div></div>
      <div class="station s3"><div class="dot"><div class="core"></div></div><div class="label">Release</div></div>
      <div class="baton"></div>
    </div>
  </div>
</body>
</html>

The variant family

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