Back to the library
Scaffold frame settle (cascade)
Variant: Cascade
The rails and blocks of the base animation, retimed so each of the four hairlines draws 60ms after the last and each content block rises on its own 70ms offset. Use it when the panel holds a list or a stack of fields and you want the eye led top-to-bottom rather than handed everything at once. Stagger steps stay inside the 30–80ms window so the cascade reads as sequence, not lag.
skeletonloadingstaggerstate-changestructure
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>Scaffold frame settle — cascade</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: 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);
/* Two stagger steps, both inside the 30-80ms band. */
--_rail-step: calc(0.06s / var(--speed));
--_block-step: calc(0.07s / var(--speed));
--_block-delay: calc(0.78s / 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);
}
.stage {
width: min(360px, 88vw);
padding: clamp(10px, 3vw, 16px);
}
.card {
position: relative;
background: var(--paper);
border: 1px solid var(--oat);
border-radius: calc(14px * var(--round));
padding: clamp(16px, 5vw, 22px);
display: grid;
gap: clamp(9px, 2.6vw, 13px);
}
.rail {
position: absolute;
background: var(--clay);
opacity: 0;
}
.rail-v {
top: clamp(10px, 3vw, 14px);
bottom: clamp(10px, 3vw, 14px);
width: 1px;
transform-origin: 50% 0%;
animation: rail-v var(--t) var(--ease-out) infinite;
}
.rail-h {
left: clamp(10px, 3vw, 14px);
right: clamp(10px, 3vw, 14px);
height: 1px;
transform-origin: 0% 50%;
animation: rail-h var(--t) var(--ease-out) infinite;
}
.rail-h.c { top: clamp(10px, 3vw, 14px); animation-delay: calc(var(--_rail-step) * 0); }
.rail-v.a { left: clamp(10px, 3vw, 14px); animation-delay: calc(var(--_rail-step) * 1); }
.rail-v.b { right: clamp(10px, 3vw, 14px); animation-delay: calc(var(--_rail-step) * 2); }
.rail-h.d { bottom: clamp(10px, 3vw, 14px); animation-delay: calc(var(--_rail-step) * 3); }
@keyframes rail-v {
0% { opacity: 0; transform: scaleY(0.04); }
12% { opacity: 0.85; transform: scaleY(1); }
62% { opacity: 0.85; transform: scaleY(1); }
80% { opacity: 0; transform: scaleY(1); }
100% { opacity: 0; transform: scaleY(1); }
}
@keyframes rail-h {
0% { opacity: 0; transform: scaleX(0.04); }
12% { opacity: 0.85; transform: scaleX(1); }
62% { opacity: 0.85; transform: scaleX(1); }
80% { opacity: 0; transform: scaleX(1); }
100% { opacity: 0; transform: scaleX(1); }
}
.block {
height: clamp(10px, 3vw, 13px);
border-radius: calc(4px * var(--round));
background: var(--slate);
opacity: 0;
transform: translateY(6%) scale(0.97);
transform-origin: 0% 50%;
animation: block-in var(--t) var(--ease-out) infinite;
}
.block.w1 { width: 62%; animation-delay: calc(var(--_block-delay) + var(--_block-step) * 0); }
.block.w2 { width: 100%; animation-delay: calc(var(--_block-delay) + var(--_block-step) * 1); }
.block.w3 { width: 44%; background: var(--clay-d); animation-delay: calc(var(--_block-delay) + var(--_block-step) * 2); }
.block.w2, .block.w3 { height: clamp(8px, 2.4vw, 10px); }
@keyframes block-in {
0% { opacity: 0; transform: translateY(6%) scale(0.97); }
6.5% { opacity: 1; transform: translateY(0) scale(1); }
38% { opacity: 1; transform: translateY(0) scale(1); }
52% { opacity: 0; transform: translateY(0) scale(1); }
100% { opacity: 0; transform: translateY(0) scale(1); }
}
.caption {
margin: clamp(10px, 3vw, 14px) 2px 0;
font-size: clamp(11px, 2.9vw, 12px);
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--slate);
opacity: 0.42;
}
@media (max-width: 380px) {
.card { padding: 15px; }
.stage { padding: 8px; }
}
@media (prefers-reduced-motion: reduce) {
/* The stagger survives as an opacity cascade; only travel is dropped. */
.rail-v, .rail-h {
animation: rail-fade calc(var(--cycle) * 2 / var(--speed)) var(--ease-in-out) infinite;
transform: none;
}
.block {
transform: none;
animation: block-fade calc(var(--cycle) * 2 / var(--speed)) var(--ease-in-out) infinite;
}
.rail-h.c { animation-delay: calc(0.06s * 0 / var(--speed)); }
.rail-v.a { animation-delay: calc(0.06s * 1 / var(--speed)); }
.rail-v.b { animation-delay: calc(0.06s * 2 / var(--speed)); }
.rail-h.d { animation-delay: calc(0.06s * 3 / var(--speed)); }
.block.w1 { animation-delay: calc((0.78s + 0.07s * 0) / var(--speed)); }
.block.w2 { animation-delay: calc((0.78s + 0.07s * 1) / var(--speed)); }
.block.w3 { animation-delay: calc((0.78s + 0.07s * 2) / var(--speed)); }
@keyframes rail-fade {
0%, 100% { opacity: 0.2; }
30%, 58% { opacity: 0.7; }
}
@keyframes block-fade {
0%, 100% { opacity: 0.35; }
20%, 44% { opacity: 1; }
}
}
</style>
</head>
<body>
<div class="stage">
<div class="card">
<span class="rail rail-v a"></span>
<span class="rail rail-v b"></span>
<span class="rail rail-h c"></span>
<span class="rail rail-h d"></span>
<span class="block w1"></span>
<span class="block w2"></span>
<span class="block w3"></span>
</div>
<p class="caption">Loading panel</p>
</div>
</body>
</html>
The variant family
Same idea, one axis moved. Compare them side by side before you commit.