Back to the library
Archive wrap confirmation, slow beat
Variant: Slow beat
The same wrap-and-confirm gesture stretched into a longer, more ceremonial single pass — bands cross at roughly twice the dwell, and the check draws behind them. Use it when the archive action is consequential and the surrounding page is otherwise still, so the extra weight reads as care rather than lag. Geometry and curves are identical to the single beat; only the durations and the gap before the tick change.
confirmationarchivestate-changeweavecheck
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 — slow beat</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);
/* Slow beat: one pass, but the cloth takes its time crossing. */
--_band: calc(0.52s / var(--speed));
--_band-step: 0s;
--_d1: calc(0.24s / 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%, 10% { transform: translate3d(0, 0, 0) scale(1); }
19% { transform: translate3d(0, 0, 0) scale(0.985); }
32%, 100% { transform: translate3d(0, 0, 0) scale(1); }
}
/* The tick waits for the slower cloth to clear before it draws. */
@keyframes tickDraw {
0%, 22% { stroke-dashoffset: 22; opacity: 0.25; }
32% { opacity: 1; }
42%, 90% { stroke-dashoffset: 0; opacity: 1; }
100% { stroke-dashoffset: 22; opacity: 0.25; }
}
@keyframes capLift {
0%, 24% { opacity: 0.35; }
42%, 88% { 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.6) var(--ease-out) infinite;
}
.caption { animation: capLift calc(var(--t) * 1.6) var(--ease-out) infinite; }
@keyframes tickFadeRM {
0%, 22% { opacity: 0.3; }
42%, 90% { opacity: 1; }
100% { opacity: 0.3; }
}
}
</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.