Back to the library
Mirrored sync indicator
Variant: Standard
The default weight of the paired-progress cue: two bars close on the centre line, touch, and withdraw. Use it inline above a list or inside a settings row where a sync is in flight and both endpoints matter. Track height, travel distance and seam opacity are all pulled back from the bold sibling while the clock and the ease-in-out curve are untouched, so it can sit beside a heavier instance without looking out of time.
loadersyncprogressmirrorstatus
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>Mirrored sync indicator — Standard</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: 3.2s;
--t: calc(var(--cycle) / var(--speed));
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
/* Weight tokens — the only thing that moves across this family. */
--_bar-h: 8px;
--_track-w: min(300px, 76vw);
--_reach: 34%;
--_alpha-track: 0.34;
--_seam-op: 0.6;
--_seam-w: 1.5px;
}
* { box-sizing: border-box; }
body {
margin: 0;
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, 28px);
display: grid;
justify-items: center;
gap: clamp(14px, 4vw, 20px);
}
.label {
font-size: clamp(11px, 3vw, 13px);
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--slate);
opacity: 0.55;
}
.track {
position: relative;
width: var(--_track-w);
height: var(--_bar-h);
border-radius: 999px;
background: color-mix(in oklch, var(--oat) calc(var(--_alpha-track) * 100%), var(--paper));
overflow: hidden;
}
.bar {
position: absolute;
top: 0;
height: 100%;
width: 50%;
border-radius: 999px;
}
.bar.a {
left: 0;
background: var(--clay);
transform: translateX(calc(-1 * var(--_reach)));
animation: pushA var(--t) var(--ease-in-out) infinite;
}
.bar.b {
right: 0;
background: var(--olive);
transform: translateX(var(--_reach));
animation: pushB var(--t) var(--ease-in-out) infinite;
}
.seam {
position: absolute;
left: 50%;
top: -20%;
width: var(--_seam-w);
height: 140%;
margin-left: calc(var(--_seam-w) / -2);
background: var(--slate);
opacity: 0;
transform: scaleY(0.5);
animation: seam calc(var(--t) / 2) var(--ease-in-out) infinite;
}
@keyframes pushA {
0% { transform: translateX(calc(-1 * var(--_reach))); }
/* contact at the exact half-clock so both sides always arrive together */
50% { transform: translateX(0%); }
100% { transform: translateX(calc(-1 * var(--_reach))); }
}
@keyframes pushB {
0% { transform: translateX(var(--_reach)); }
50% { transform: translateX(0%); }
100% { transform: translateX(var(--_reach)); }
}
@keyframes seam {
0%, 100% { opacity: 0; transform: scaleY(0.5); }
46% { opacity: 0; transform: scaleY(0.5); }
62% { opacity: var(--_seam-op); transform: scaleY(1); }
88% { opacity: 0; transform: scaleY(0.7); }
}
.legend {
display: flex;
gap: clamp(14px, 5vw, 24px);
font-size: clamp(11px, 3vw, 12px);
color: var(--slate);
opacity: 0.7;
}
.legend span {
display: inline-flex;
align-items: center;
gap: 7px;
}
.dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
}
.dot.a { background: var(--clay); }
.dot.b { background: var(--olive); }
.frame {
padding: clamp(14px, 4vw, 20px);
border-radius: calc(16px * var(--round));
background: var(--paper);
border: 1px solid var(--oat);
display: grid;
justify-items: center;
gap: clamp(12px, 3.5vw, 16px);
}
@media (max-width: 380px) {
.card { padding: 12px; gap: 12px; }
.frame { padding: 12px; }
}
@media (prefers-reduced-motion: reduce) {
.bar.a,
.bar.b {
animation: none;
transform: translateX(0%);
}
.bar.a { animation: fadeA calc(var(--t) * 2) var(--ease-in-out) infinite; }
.bar.b { animation: fadeB calc(var(--t) * 2) var(--ease-in-out) infinite; }
.seam {
animation: seamQuiet calc(var(--t) * 2) var(--ease-in-out) infinite;
transform: scaleY(1);
}
}
@keyframes fadeA {
0%, 100% { opacity: 0.45; }
50% { opacity: 1; }
}
@keyframes fadeB {
0%, 100% { opacity: 1; }
50% { opacity: 0.45; }
}
@keyframes seamQuiet {
0%, 100% { opacity: 0.25; }
50% { opacity: var(--_seam-op); }
}
</style>
</head>
<body>
<div class="card">
<div class="frame">
<div class="label">Syncing both sides</div>
<div class="track">
<div class="bar a"></div>
<div class="bar b"></div>
<div class="seam"></div>
</div>
<div class="legend">
<span><i class="dot a"></i>Local</span>
<span><i class="dot b"></i>Remote</span>
</div>
</div>
</div>
</body>
</html>The variant family
Same idea, one axis moved. Compare them side by side before you commit.