Back to the library
Signature edge save
Variant: Standard
The same fore-edge save indicator at a normal UI weight: thicker page rules, a fuller edge stain, and a seal mark you can read at a glance across a panel. Use it as the default when the badge sits alone in a sidebar footer or toolbar rather than inline with type. Only stroke, travel and opacity change from the hairline sibling — the settle stagger stays at 46ms and the sweep stays linear so the rhythm is identical.
savestatusambienteditorialloop
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>Signature edge save — 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: 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);
/* Weight tokens — the one axis this family varies. */
--_rule: 2px;
--_travel: 9%;
--_ink: 0.78;
--_seal: 3px;
--_stain: 0.72;
}
* { box-sizing: border-box; }
body {
margin: 0;
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);
}
.card {
width: min(420px, 92vw);
padding: clamp(16px, 5vw, 26px);
background: var(--paper);
border: 1px solid var(--oat);
border-radius: calc(10px * var(--round));
display: grid;
gap: clamp(12px, 3.4vw, 18px);
}
.head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
}
.title {
font-size: clamp(12px, 3.3vw, 13.5px);
letter-spacing: 0.08em;
text-transform: uppercase;
font-weight: 600;
}
.state {
font-size: clamp(11px, 3vw, 12px);
color: var(--clay-d);
letter-spacing: 0.04em;
opacity: 0.35;
animation: state-ink var(--t) var(--ease-out) infinite;
}
.block {
position: relative;
height: clamp(86px, 26vw, 112px);
overflow: hidden;
border-radius: calc(4px * var(--round));
}
.rules {
position: absolute;
inset: 0;
display: grid;
grid-template-rows: repeat(7, 1fr);
align-content: stretch;
}
.rule {
position: relative;
align-self: center;
height: var(--_rule);
background: var(--slate);
opacity: 0;
transform-origin: left center;
/* Pages settle in from the spine side; ease-out so the stack lands. */
animation: page-settle var(--t) var(--ease-out) infinite;
animation-delay: calc(var(--_i) * 46ms / var(--speed));
}
.rule:nth-child(1) { --_i: 0; width: 88%; }
.rule:nth-child(2) { --_i: 1; width: 94%; }
.rule:nth-child(3) { --_i: 2; width: 79%; }
.rule:nth-child(4) { --_i: 3; width: 96%; }
.rule:nth-child(5) { --_i: 4; width: 84%; }
.rule:nth-child(6) { --_i: 5; width: 91%; }
.rule:nth-child(7) { --_i: 6; width: 72%; }
.stain {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 100%;
background: linear-gradient(90deg,
color-mix(in oklab, var(--clay) 0%, transparent) 0%,
var(--clay) 42%,
var(--clay) 58%,
color-mix(in oklab, var(--clay) 0%, transparent) 100%);
opacity: var(--_stain);
transform: translateX(-100%);
mix-blend-mode: multiply;
animation: edge-sweep var(--t) linear infinite;
}
.seal {
position: absolute;
top: 12%;
bottom: 12%;
right: 2%;
width: var(--_seal);
background: var(--rust);
transform-origin: center bottom;
transform: scaleY(0.04);
opacity: 0;
animation: seal-close var(--t) var(--ease-out) infinite;
}
.foot {
display: flex;
align-items: center;
justify-content: space-between;
font-size: clamp(10.5px, 2.9vw, 11.5px);
letter-spacing: 0.06em;
color: color-mix(in oklab, var(--slate) 52%, var(--paper));
}
.tick {
width: clamp(46px, 14vw, 62px);
height: 3px;
background: var(--oat);
border-radius: 999px;
overflow: hidden;
}
.tick i {
display: block;
height: 100%;
width: 100%;
background: var(--olive);
transform-origin: left center;
transform: scaleX(0);
animation: tick-fill var(--t) linear infinite;
}
@keyframes page-settle {
0% { opacity: 0; transform: translateX(calc(var(--_travel) * -1)) scaleX(0.985); }
16%, 74% { opacity: var(--_ink); transform: translateX(0) scaleX(1); }
90%, 100%{ opacity: 0; transform: translateX(0) scaleX(1); }
}
@keyframes edge-sweep {
0%, 34% { transform: translateX(-100%); }
70%, 100% { transform: translateX(100%); }
}
@keyframes seal-close {
0%, 62% { opacity: 0; transform: scaleY(0.04); }
76%, 88% { opacity: 1; transform: scaleY(1); }
100% { opacity: 0; transform: scaleY(1); }
}
@keyframes tick-fill {
0% { transform: scaleX(0); }
72%, 100% { transform: scaleX(1); }
}
@keyframes state-ink {
0%, 58% { opacity: 0.35; }
76%, 92% { opacity: 1; }
100% { opacity: 0.35; }
}
@media (max-width: 380px) {
.card { padding: 14px; gap: 11px; }
.block { height: 82px; }
}
@media (prefers-reduced-motion: reduce) {
.rule {
opacity: var(--_ink);
transform: none;
animation: page-hold calc(var(--t) * 2) var(--ease-in-out) infinite;
animation-delay: calc(var(--_i) * 46ms / var(--speed));
}
.stain {
transform: none;
animation: stain-breathe calc(var(--t) * 2) var(--ease-in-out) infinite;
}
.seal {
transform: scaleY(1);
animation: seal-hold calc(var(--t) * 2) var(--ease-in-out) infinite;
}
.tick i {
transform: scaleX(1);
animation: state-ink calc(var(--t) * 2) var(--ease-in-out) infinite;
}
.state { animation: state-ink calc(var(--t) * 2) var(--ease-in-out) infinite; }
}
@keyframes page-hold {
0%, 100% { opacity: calc(var(--_ink) * 0.55); }
50% { opacity: var(--_ink); }
}
@keyframes stain-breathe {
0%, 100% { opacity: calc(var(--_stain) * 0.4); }
50% { opacity: var(--_stain); }
}
@keyframes seal-hold {
0%, 100% { opacity: 0.4; }
50% { opacity: 1; }
}
</style>
</head>
<body>
<div class="card">
<div class="head">
<span class="title">Draft block</span>
<span class="state">committing</span>
</div>
<div class="block">
<div class="rules">
<span class="rule"></span>
<span class="rule"></span>
<span class="rule"></span>
<span class="rule"></span>
<span class="rule"></span>
<span class="rule"></span>
<span class="rule"></span>
</div>
<div class="stain"></div>
<div class="seal"></div>
</div>
<div class="foot">
<span>edge sealed</span>
<span class="tick"><i></i></span>
</div>
</div>
</body>
</html>
The variant family
Same idea, one axis moved. Compare them side by side before you commit.