Preloader Handoff
Three loaders — skeleton shell, dot leader, shimmer sweep — handing off to a content panel that reserves their exact box. The waiting is a labelled timer and the counters that land are the library's build-time totals.
What you should hear: Tab reaches the control, Enter or Space activates it — and every state change is announced in the same words the page shows.
Preloader choreography
0% of the fake load
Stage 1 of 3 · Skeleton shell
borrowed from Skeleton Profile Card · 1.3 KB
Press run: three loaders hand off to the content panel. Nothing is fetched.
Loader choreography is usually where a site lies about speed, so this one is explicit: no request is made, the bar is a timer, and the first three rows name the catalog asset each stage borrows its motion from.
Ambience is a live light wash over the preview. Full palette re-theming of every demo arrives with Theme Studio (Pro).
// Preloader Handoff — the demo's own source, extracted verbatim.
// From src/components/demos/scenes-17.tsx, export PreloaderHandoff.
// Needs React 18+ and Tailwind CSS v4. The classes below include this site's
// token utilities (bg-panel, text-ink-dim, chip …); their values are served as
// CSS custom properties at /api/exports/tokens.json. The reduced-motion branch
// is part of the source, not an afterthought.
// 5 helpers the demo imports are inlined above it, verbatim.
// Catalog-dependent: the array below is a three-record sample of the site's own data.
import { useEffect, useRef, useState } from "react";
// The demo reads the site's own catalog: 362 catalogued assets. Three real
// records are inlined below — long fields trimmed — so this file runs as
// written. Replace the array with your own data, or read the full list from
// the site, where /api/search?q= returns the matching records as JSON.
const COMPONENTS = [
{"slug":"prism-switch","kind":"element","title":"Prism Switch","description":"A theme-aware toggle that sweeps a six-stop prism gradient across the track on every flip. Pure…","tags":["switch","toggle"],"behaviors":["click","hover"],"stack":["React","HTML/CSS"],"deps":[],"bundleKb":1.2,"themeable":true,"a11yScore":98,"qualityScore":96,"status":"live","license":"MIT","version":"1.2.0","author":"Motif Studio","published":"2026-06-02","demo":"prism-switch","props":[{"name":"size","label":"Size","type":"range","min":20,"max":64,"step":2,"unit":"px","defaultValue":42},{"name":"hueSpeed","label":"Gradient speed","type":"range","min":0,"max":4,"step":0.1,"unit":"s","defaultValue":1.4}],"copies":4820,"views":21400,"aliases":[],"trend":[]},
{"slug":"halo-button","kind":"element","title":"Halo Button","description":"Primary button with a mouse-tracking halo, press ripple and a springy scale. Ships as React + T…","tags":["button","magnetic"],"behaviors":["hover","click"],"stack":["React","HTML/CSS"],"deps":[],"bundleKb":2.1,"themeable":true,"a11yScore":96,"qualityScore":94,"status":"live","license":"MIT","version":"1.1.0","author":"Motif Studio","published":"2026-06-18","demo":"halo-button","props":[{"name":"magnet","label":"Magnet strength","type":"range","min":0,"max":60,"step":1,"defaultValue":24,"unit":""},{"name":"glow","label":"Halo glow","type":"range","min":0,"max":100,"step":1,"defaultValue":70,"unit":"%"}],"copies":3910,"views":17300,"aliases":["cta","call to action"],"trend":[]},
{"slug":"pulse-loader","kind":"element","title":"Pulse Loader","description":"Three-note loader with staggered radial pulse and optional progress text. Reduced-motion safe a…","tags":["loader","loading"],"behaviors":[],"stack":["HTML/CSS","React"],"deps":[],"bundleKb":0.8,"themeable":true,"a11yScore":99,"qualityScore":93,"status":"live","license":"MIT","version":"1.0.3","author":"Motif Studio","published":"2026-05-28","demo":"pulse-loader","props":[{"name":"speed","label":"Pulse speed","type":"range","min":0.2,"max":2.4,"step":0.05,"unit":"s","defaultValue":1},{"name":"dots","label":"Dots","type":"range","min":1,"max":8,"step":1,"unit":"","defaultValue":3}],"copies":5220,"views":19800,"aliases":["spinner","loading"],"trend":[]},
];
// The demo reads the site's own catalog: 118 guides. Three real
// records are inlined below — long fields trimmed — so this file runs as
// written. Replace the array with your own data, or read the full list from
// the site, where /api/search?q= returns the matching records as JSON.
const LEARN_ARTICLES = [
{"slug":"hero-that-breathes-in-20-min","kicker":"Build along","series":{"name":"The fundamentals","part":1,"of":4},"title":"Build a hero that breathes — in 20 minutes","deck":"Most hero sections die on the fold because nothing in them moves with intent. This walkthrough …","minutes":20,"level":"Beginner","tags":["hero","animation"],"updated":"2026-09-09","blocks":[{"h":"Why heroes feel dead","body":["A hero is one glance. If every pixel is still, the eye treats it like a magazine cover — read once, scroll on. You don't need autoplay video or a 3D library; you need three cheap layers of life that reinforce one message.","The recipe we use at Motif: a living backdrop (motion you can ignore), one typographic moment (motion you can't miss), and one honest action (motion under your finger)."],"links":[{"label":"Aurora Veil — living backdrop","href":"/components/aurora-veil"},{"label":"Wipe Reveal — typographic moment","href":"/components/wipe-reveal"}],"code":{},"callout":{}},{"h":"Step 1 — stack the veil","body":["Start with the backdrop so everything after it has something to sit on. Aurora Veil is three radial gradients drifting on a slow clock plus a grain pass — GPU-composited, zero JavaScript.","Resist cranking the speed. A drift that takes 16–20s per cycle reads as 'alive but expensive'. Faster reads as 'screensaver'."],"links":[],"code":{"title":"veil layer","lang":"tsx","text":"<section className=\"relative min-h-[90vh] overflow-hidden bg-[#07080d]\">\n {/* layer 1: living backdrop */}\n <AuroraVeil hueA={262} hueB={192} speed={18} grain\n className=\"absolute inset-0\" />\n {/* layer 2+3 arrive below */}\n</section>"},"callout":{}}],"takeaways":[],"revised":"","revisedNote":"","author":"","prereqs":[],"footnotes":[]},
{"slug":"easing-is-a-language","kicker":"Field guide","series":{"name":"The fundamentals","part":2,"of":4},"title":"Easing is a language — a field guide to curves","deck":"The single highest-leverage skill in web animation is choosing the right curve. This guide maps…","minutes":12,"level":"Intermediate","tags":["easing","motion"],"updated":"2026-09-08","blocks":[{"h":"Start with the feeling, not the curve","body":["Every easing curve is a personality. Linear says 'machine'. ease-out says 'expensive'. back-out says 'confident'. elastic says 'celebration'. If you pick curves by copying presets, your page has the personality of a default theme."],"bullets":[],"code":{},"callout":{},"links":[]},{"h":"The six curves that cover 90% of UI","body":[],"bullets":["linear — progress bars, marquees, anything mechanical. Never for entrance motion.","ease-out-expo (0.16 1 0.3 1) — the workhorse entrance. Fast start, long settle = 'premium'. Use for cards, sections, images entering."],"code":{"title":"the workhorse","lang":"css","text":"/* the 'expensive' entrance: fast to 80%, then glide */\n.card-enter {\n animation: rise .7s cubic-bezier(.16, 1, .3, 1) both;\n}\n@keyframes rise {\n from { opacity: 0; transform: translateY(16px); }\n to { opacity: 1; transform: none; }\n}\n\n/* overshoot for ONE hero element */\n.spotlight {\n transition: transform .5s cubic-bezier(.34, 1.56, .64, 1);\n}"},"callout":{},"links":[]}],"takeaways":[],"revised":"","revisedNote":"","author":"","prereqs":[],"footnotes":[]},
{"slug":"prompt-that-reproduces","kicker":"AI workflows","series":{"name":"The fundamentals","part":3,"of":4},"title":"Write AI prompts that reproduce — not ones that 'inspire'","deck":"Most AI website prompts get you 60% fidelity because they describe vibes ('make it sleek') inst…","minutes":15,"level":"Intermediate","tags":["ai","prompts"],"updated":"2026-09-09","blocks":[{"h":"Constraints beat adjectives","body":["In every run log on this site you'll see the same pattern: prompts with concrete constraints score 10–15 points higher than adjective prompts. 'Sleek' is unmeasurable; '16px radius, near-black #07080B canvas, one violet→cyan accent used only for CTAs' is a spec."],"bullets":["Give the palette as hex/HSL, and say where each colour is *allowed* — restraint instructions land harder than colour names.","Give a component inventory (nav, bento features, pricing toggle) so the model doesn't invent sections."],"code":{},"callout":{},"links":[]},{"h":"The skeleton that scores 90+","body":[],"bullets":[],"code":{"title":"prompt skeleton","lang":"text","text":"Build a [page type] in [stack].\n\nPalette\n• canvas / ink / accent (hex) — and where each is allowed\n• accent reserved for [moments]\n\nAbove the fold\n• [section]: [2-3 concrete requirements, incl. typography scale]\n• [headline copy — write it for me, in this voice: ___]\n\nSections (in order)\n1. [name] — [what it must contain + interaction]\n2. [name] — ...\n3. [name] — ...\n\nMotion budget\n• entrances: [easing/duration hint]\n• interactive: [hover/click rules]\n• prohibited: [list]\n\nTone & traps\n• write copy like [persona]\n• avoid [stock clichés]\n• accessibility: [WCAG notes]"},"callout":{},"links":[]}],"takeaways":[],"revised":"","revisedNote":"","author":"","prereqs":[],"footnotes":[]},
];
// The demo reads the site's own catalog: 184 briefs and 661 recorded model runs. Three real
// records are inlined below — long fields trimmed — so this file runs as
// written. Replace the array with your own data, or read the full list from
// the site, where /api/search?q= returns the matching records as JSON.
const PROMPTS = [
{"slug":"dark-saas-pricing-launch","title":"Dark SaaS launch page with aurora hero","titleZh":"暗色 SaaS 发布页:极光 hero","industry":"SaaS","vibe":"dark, premium, minimal","stacks":["Next.js","HTML"],"blocks":["Nav","Hero"],"status":"featured","avgFidelity":92,"bestModel":"Claude 4.6 Sonnet","runs":[{"model":"Claude 4.6 Sonnet","date":"2026-08-04","fidelity":95,"buildError":false,"notes":"Matches reference spacing almost 1:1; fonts swapped gra…"},{"model":"Codex","date":"2026-08-04","fidelity":91,"buildError":false,"notes":"Solid. Needed one nudge to enable the toggle pricing."}],"promptBody":"Build a one-page dark SaaS landing in Next.js + Tailwind.\n• Palette: near-black #07080B canvas,…","author":"Motif Studio","published":"2026-08-04","tokens":[],"estLoc":0,"relatedComponents":[],"revised":"","revisedNote":"","remixes":[],"viewport":""},
{"slug":"3d-portfolio-neon","title":"3D portfolio with neon orbit showcase","titleZh":"","industry":"Portfolio","vibe":"bold, 3D, neon","stacks":["Next.js","React"],"blocks":["Hero","Orbit gallery"],"status":"verified","avgFidelity":87,"bestModel":"Codex","runs":[{"model":"Codex","date":"2026-07-29","fidelity":91,"buildError":false,"notes":"Orbit ring implemented with spring physics — great feel."},{"model":"Claude 4.6 Sonnet","date":"2026-07-29","fidelity":85,"buildError":false,"notes":"Accurate but used heavier 3D lib than asked; suggested …"}],"promptBody":"Design a portfolio hero that feels like a neon gallery.\n• Left: name + role in large display ty…","author":"Motif Studio","published":"2026-07-29","tokens":[],"estLoc":0,"relatedComponents":[],"revised":"","revisedNote":"","remixes":[],"viewport":""},
{"slug":"fintech-trust-page","title":"Fintech trust page (compliance tone)","titleZh":"金融科技信任页(合规语气)","industry":"Fintech","vibe":"calm, trustworthy, crisp","stacks":["Next.js","HTML"],"blocks":["Hero","Security bento"],"status":"verified","avgFidelity":89,"bestModel":"Claude 4.6 Sonnet","runs":[{"model":"Claude 4.6 Sonnet","date":"2026-08-01","fidelity":92,"buildError":false,"notes":"Excellent typographic hierarchy for legal-adjacent copy."},{"model":"GLM-4.6 (CN)","date":"2026-08-01","fidelity":88,"buildError":false,"notes":"Very close; metric band needed re-spacing."}],"promptBody":"A fintech page that earns trust, not hype.\n• Off-white paper background, ink navy text, single …","author":"Motif Studio","published":"2026-08-01","tokens":[],"estLoc":0,"relatedComponents":[],"revised":"","revisedNote":"","remixes":[],"viewport":""},
];
function useReducedMotion() {
const [reduced, setReduced] = useState<boolean>(() =>
typeof window !== "undefined" ? window.matchMedia("(prefers-reduced-motion: reduce)").matches : false
);
useEffect(() => {
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
const on = (e: MediaQueryListEvent) => setReduced(e.matches);
mq.addEventListener("change", on);
return () => mq.removeEventListener("change", on);
}, []);
return reduced;
}
const STAGE_MS = 420;
const PRELOAD_STAGES = [
{ id: "shell", label: "Skeleton shell", source: "skeleton-card" },
{ id: "chase", label: "Dot leader", source: "dot-leader-loading" },
{ id: "sweep", label: "Shimmer sweep", source: "shimmer-text" },
];
const sourceLine = (slug: string) => {
const c = COMPONENTS.find((x) => x.slug === slug);
return c ? `${c.title} · ${c.bundleKb.toFixed(1)} KB` : slug;
};
const LIBRARY_TOTALS = [
{ label: "components", value: COMPONENTS.length },
{ label: "prompts", value: PROMPTS.length },
{ label: "guides", value: LEARN_ARTICLES.length },
];
export function PreloaderHandoff() {
const reduced = useReducedMotion();
const [stage, setStage] = useState(0);
const [running, setRunning] = useState(false);
const [note, setNote] = useState("Press run: three loaders hand off to the content panel. Nothing is fetched.");
const hostRef = useRef<HTMLDivElement>(null);
const elapsedRef = useRef(0);
useEffect(() => {
if (!running) return;
const id = window.setInterval(() => {
elapsedRef.current += STAGE_MS;
const next = Math.min(PRELOAD_STAGES.length, Math.floor(elapsedRef.current / STAGE_MS));
setStage(next);
if (next >= PRELOAD_STAGES.length) {
setRunning(false);
setNote("Content in. The three loaders passed the frame along without a jump between them.");
}
}, STAGE_MS);
return () => window.clearInterval(id);
}, [running]);
// The loaders loop through the animation API so the loops can be cancelled
// the moment a stage ends (and so nothing runs at all under reduced motion).
useEffect(() => {
const host = hostRef.current;
if (!host || reduced || !running) return;
const anims: Animation[] = [];
host.querySelectorAll<HTMLElement>("[data-pulse]").forEach((el, i) => {
anims.push(
el.animate([{ opacity: 0.2 }, { opacity: 1 }], {
duration: 640,
delay: i * 120,
iterations: Infinity,
direction: "alternate",
}),
);
});
const sweep = host.querySelector<HTMLElement>("[data-sweep]");
if (sweep) {
anims.push(
sweep.animate([{ transform: "translateX(-80%)" }, { transform: "translateX(150%)" }], {
duration: 1100,
iterations: Infinity,
}),
);
}
return () => anims.forEach((a) => a.cancel());
}, [running, stage, reduced]);
const run = () => {
if (reduced) {
setStage(PRELOAD_STAGES.length);
setRunning(false);
setNote("Reduced motion: the three stages land in one frame, no movement between them.");
return;
}
elapsedRef.current = 0;
setStage(0);
setRunning(true);
setNote(`Running — ${PRELOAD_STAGES.length} stages, ${STAGE_MS} ms each.`);
};
const active = PRELOAD_STAGES[Math.min(stage, PRELOAD_STAGES.length - 1)];
const loaded = stage >= PRELOAD_STAGES.length;
const progress = Math.round((Math.min(stage, PRELOAD_STAGES.length) / PRELOAD_STAGES.length) * 100);
return (
<div className="flex h-full w-full flex-col items-center justify-center bg-[radial-gradient(70%_90%_at_50%_0%,rgba(56,189,248,0.10),transparent_60%),#08090f] px-6 py-6">
<div className="w-full max-w-md">
<div className="mb-2 flex items-baseline justify-between gap-3">
<p className="text-[10px] font-bold uppercase tracking-[0.24em] text-sky-300/80">Preloader choreography</p>
<p className="text-[10px] tabular-nums text-ink-faint">{progress}% of the fake load</p>
</div>
<div ref={hostRef} className="overflow-hidden rounded-2xl border border-white/10 bg-white/[.02]">
<div className="flex items-center gap-2 border-b border-white/10 px-3 py-2">
<span className="flex gap-1" aria-hidden>
<span className="h-2 w-2 rounded-full bg-white/20" />
<span className="h-2 w-2 rounded-full bg-white/20" />
<span className="h-2 w-2 rounded-full bg-white/20" />
</span>
<span className="font-mono text-[10px] text-ink-faint">motif.local · fake load, no network</span>
<span className="ml-auto font-mono text-[10px] tabular-nums text-sky-200/80">{progress}%</span>
</div>
<div className="h-56 p-3">
{!loaded ? (
<div className="flex h-full flex-col">
<p className="text-[10px] font-bold uppercase tracking-widest text-ink-faint">
Stage {stage + 1} of {PRELOAD_STAGES.length} · {active.label}
</p>
<p className="mt-0.5 font-mono text-[10px] text-ink-faint">borrowed from {sourceLine(active.source)}</p>
<div className="mt-3 flex-1">
{active.id === "shell" ? (
<div className="space-y-2">
<div className="flex items-center gap-2">
<span data-pulse className="h-8 w-8 rounded-full bg-sky-300/25" />
<span data-pulse className="h-2.5 w-28 rounded-full bg-sky-200/20" />
</div>
<span data-pulse className="block h-2 w-full rounded-full bg-white/10" />
<span data-pulse className="block h-2 w-5/6 rounded-full bg-white/10" />
<span data-pulse className="block h-2 w-2/3 rounded-full bg-white/10" />
</div>
) : active.id === "chase" ? (
<div className="flex items-center gap-2 font-mono text-[12px] text-sky-100">
<span>installing the shell</span>
<span className="flex gap-1" aria-hidden>
<span data-pulse className="h-1.5 w-1.5 rounded-full bg-sky-300" />
<span data-pulse className="h-1.5 w-1.5 rounded-full bg-sky-300" />
<span data-pulse className="h-1.5 w-1.5 rounded-full bg-sky-300" />
</span>
</div>
) : (
<div className="relative mt-4 h-3 overflow-hidden rounded-full bg-white/10">
<span data-sweep className="absolute inset-y-0 w-1/3 rounded-full bg-gradient-to-r from-transparent via-sky-200/70 to-transparent" />
</div>
)}
</div>
<div className="mt-2 h-1 overflow-hidden rounded-full bg-white/[.06]">
<span className="block h-full rounded-full bg-sky-300/70 transition-[width] duration-300" style={{ width: `${progress}%` }} />
</div>
</div>
) : (
<div className="flex h-full flex-col justify-center">
<p className="text-[12px] font-bold text-ink">Everything is in place</p>
<p className="mt-1 text-[11px] leading-relaxed text-ink-dim">
Three stages, {PRELOAD_STAGES.length * STAGE_MS} ms of waiting, and the panel behind this line never shifted: each loader
reserved the same box the content now fills.
</p>
<dl className="mt-3 grid grid-cols-3 gap-2">
{LIBRARY_TOTALS.map((t) => (
<div key={t.label} className="rounded-xl border border-white/10 bg-white/[.03] px-2 py-1.5">
<dt className="text-[9px] uppercase tracking-widest text-ink-faint">{t.label}</dt>
<dd className="font-mono text-[13px] tabular-nums text-sky-100">{t.value}</dd>
</div>
))}
</dl>
<p className="mt-2 text-[10px] leading-relaxed text-ink-faint">
Those three numbers are read from the library at build time — the waiting is the only theatre here, and it is labelled as
such above.
</p>
</div>
)}
</div>
</div>
<div className="mt-2 flex flex-wrap items-center gap-2">
<button type="button" onClick={run} className="btn btn-ghost !px-3 !py-1.5 text-[10px]">
{loaded ? "Run it again" : "Run the hand-off"}
</button>
<button
type="button"
onClick={() => {
setStage(PRELOAD_STAGES.length);
setRunning(false);
setNote("Skipped the remaining stages. That skip is the same path the reduced-motion setting takes.");
}}
disabled={loaded}
className="btn btn-ghost !px-3 !py-1.5 text-[10px] disabled:opacity-40"
>
Skip the rest
</button>
</div>
<p aria-live="polite" className="mt-2 min-h-[1rem] text-[10px] leading-relaxed text-sky-200/80">
{note}
</p>
<p className="mt-1 text-[10px] leading-relaxed text-ink-faint">
Loader choreography is usually where a site lies about speed, so this one is explicit: no request is made, the bar is a timer, and
the first three rows name the catalog asset each stage borrows its motion from.
</p>
</div>
</div>
);
}
Snippet provenance
lines 1–252 · v1.0.0No changelog entry mentions “Preloader Handoff”, so the catalog records a single version for it.
Every line above is attributed to the single version this catalog records — we keep no per-line history, so no per-line attribution is invented. See the provenance page for the rule and the gaps.
Review notes · anchored to code lines
Threads attach to a line number in this asset's snippet, so a remark about the focus trap stays next to it.
Loading notes…
The snippet on this page is 252 lines long, so anchors run 1–252. Notes are per-asset and per-browser; a shared review thread is a server feature, and this panel is the local stand-in that proves the anchoring works.
Course rail — understand it before you ship it
Live variants
Variant gallery
The same Preloader Handoff demo re-lit in three tones — no code change, three personalities.
Preloader choreography
0% of the fake load
Stage 1 of 3 · Skeleton shell
borrowed from Skeleton Profile Card · 1.3 KB
Press run: three loaders hand off to the content panel. Nothing is fetched.
Loader choreography is usually where a site lies about speed, so this one is explicit: no request is made, the bar is a timer, and the first three rows name the catalog asset each stage borrows its motion from.
Preloader choreography
0% of the fake load
Stage 1 of 3 · Skeleton shell
borrowed from Skeleton Profile Card · 1.3 KB
Press run: three loaders hand off to the content panel. Nothing is fetched.
Loader choreography is usually where a site lies about speed, so this one is explicit: no request is made, the bar is a timer, and the first three rows name the catalog asset each stage borrows its motion from.
Preloader choreography
0% of the fake load
Stage 1 of 3 · Skeleton shell
borrowed from Skeleton Profile Card · 1.3 KB
Press run: three loaders hand off to the content panel. Nothing is fetched.
Loader choreography is usually where a site lies about speed, so this one is explicit: no request is made, the bar is a timer, and the first three rows name the catalog asset each stage borrows its motion from.
Theme Studio preview
Theming example — two token themes
Assets are token-driven (100% design-token driven). Here the same scene runs under two theme presets from the token sheet.
Preloader choreography
0% of the fake load
Stage 1 of 3 · Skeleton shell
borrowed from Skeleton Profile Card · 1.3 KB
Press run: three loaders hand off to the content panel. Nothing is fetched.
Loader choreography is usually where a site lies about speed, so this one is explicit: no request is made, the bar is a timer, and the first three rows name the catalog asset each stage borrows its motion from.
--accent 262 · dark surfaces
Preloader choreography
0% of the fake load
Stage 1 of 3 · Skeleton shell
borrowed from Skeleton Profile Card · 1.3 KB
Press run: three loaders hand off to the content panel. Nothing is fetched.
Loader choreography is usually where a site lies about speed, so this one is explicit: no request is made, the bar is a timer, and the first three rows name the catalog asset each stage borrows its motion from.
--accent 40 · paper surfaces
90-second read
Usage recipe — where this fits
Use it as the signature of a single screen — a hero backdrop, an entrance, a transition. One signature per screen; everything else stays quiet.
Compose it with calm content around it: the motion earns attention only when the layout does not compete for it.
Cost honesty
Performance note
Cheaper alternative — the static preview variant removes the animation loop and keeps the layout — a quieter, cheaper sibling.
What it needs
Browser support strip
No polyfills ship with the asset — if a listed feature is missing in a target browser, the graceful fallback is the static layout.
prefers-reduced-motion
Reduced-motion fallback
With prefers-reduced-motion: reduce, the animation loop and travel are removed; state changes keep working through opacity and colour (≤ 300ms). The demo you see above honours the switch — try it in your OS settings and the loop will quiet down.
Design rule: the reduced branch is a second design, not a stripped page — every state change stays visible.
Pressable step-through
Keyboard walk demo
Step through what a keyboard user experiences with this asset.
Tab to the scene — motion is paused or non-essential for keyboard users
Auto-suggested
Composition map — what pairs well
Same-family assets that compose cleanly with this one:
Aurora Veil
Layered mesh of drifting colour fields behind a fine grain veil. Built on GPU-composited CSS gradients — a 60 fps full-page backdrop at 0 JS.
AnimatedHalo Trail
Cursor trail of fading halos that also reacts to pressed state — an attention magnet for product demos. Pointer-events transparent, fully throttled.
AnimatedOrbit Deck
A 3D ring of cards/badges orbiting a focal chip with depth-of-field blur. Pause on hover, tilt with drag, auto-rotate for ambient showcase.
One original micro-case
Inspiration context
The hero that held attention for one beat longer
A marketing page's hero used a stock fade-in that finished in 200ms — invisible to the eye, useless to the story. Re-timed with a slow settle (0.6s, ease-out-quart) and one typographic wipe, the hero read as 'alive' without a single new asset. The change was purely a curve and a delay.
Written in-house — no screenshots, no borrowed imagery. The pattern, not the pixels.
Every version, why
Changelog — this asset
Current release — this page documents the asset as shipped.
Token pass: colours and radii moved to the design-token sheet; a11y score raised to the current bar.
Initial release to the library — original markup, MIT licensed.
30-day sparkline
Copy history
Sample series from this asset's usage fingerprint · last 12 days shown.
Future queue
Community remixes
Queue opens after launch. Remixes arrive as alternate versions of this asset — same job, different voice — submitted by users and audited by the studio before they appear beside the original.
Star / favourite
Save this asset
Your collection is stored in this browser (localStorage) — no account needed.
Build a page around it
Pairs well with a prompt
This asset belongs inside a bigger build — here is the prompt that would generate a page containing it:
Motion-spec page — the animation before the layout
Spec sheet · Timing table · Easing curves
Exact tree
Bundled dependencies — disclosed
Full snippet ≈ 20.6 KB raw, ~2 KB gzip — before any framework you already load.
Download
Export as single file
Grab the asset as one file, ready to paste into your project.
Files are generated in your browser from the snippets on this page — nothing leaves the tab.
Scale, not clutter
Sizing system — three densities
Density is a token decision, not a per-page override — pick one density per product surface and hold it. Active: Default.
Runnable copy
Open in a sandbox
Opens this asset in a new tab with its stylesheet injected — a real, editable HTML document in your browser. No account, no upload.
If the tab is blocked, use the single-file export instead — same content, saved locally.
Written version first
Tutorial transcript
This asset does not have a video yet — so the written version stands in: the code line walk below narrates the snippet line by line, and the design rationale on this page explains the default decisions. When a video ships, this exact panel hosts its full transcript.
Annotated snippet
Code line walk
The lines that matter, with the reason they matter. Click one:
Trade-off, visible
Size vs. quality slider
Standard · CSS + hover states
~6 KBadds transitions, focus-visible and reduced-motion rules
The slider is honest: the slim tier has no JS, the pro tier pays for behaviour. Start slim, upgrade only the surfaces that need it.
HTML → React, priced
Stack-switch cost note
Taking the HTML/CSS version into a React component costs real work — and the cost is predictable:
Event wiring
Hover, focus and click become handlers — the CSS stays, the state appears.
Props for the knobs
Every inline value that should vary becomes a prop with a default — the defaults must match the CSS exactly.
The estimate
A clean port of this asset is 20–45 minutes for a React developer — the 0-character stylesheet is the easy half.
Find it the way you say it
Tag synonyms
Search on this site resolves synonyms globally — "dropdown", "select" and "picker" land on the same asset, so you never need to know the catalog's internal vocabulary.
When to pick which
Sibling comparison
| Compare | Preloader Handoff | Aurora Veil | Halo Trail |
|---|---|---|---|
| Kind | Animated | Animated | Animated |
| Bundle | 2.3 KB gzip | 2.6 KB gzip | 5.8 KB gzip |
| a11y | 95/100 | 100/100 | 93/100 |
| Dependencies | none | none | none |
| Copies / mo | 210 | 8,940 | 6,330 |
| Interactions | click, keyboard | scroll | drag, hover |
The honest verdict — Preloader Handoff is the lighter default here; reach for Aurora Veil when its extra weight buys an interaction this one lacks.
Similar to this — scored on shared tags, not hand-picked
Related assets
Prism Switch
1.2 KB · a11y 98 · Q 96
Halo Button
2.1 KB · a11y 96 · Q 94
Pulse Loader
0.8 KB · a11y 99 · Q 93
Read next — two guides and a prompt
picked by tag overlap, not by handGuide
The choreography question: story or decoration?
8 min · Beginner · updated 2026-09-10
Guide
Motion choreography systems
4 min · Advanced · updated 2026-09-14
Prompt
3D portfolio with neon orbit showcase
Portfolio · 87% fidelity
How these three were chosen: essays scored on shared tags (1 and 1 matches), then prompt scored on block/vibe overlap (0 matches). The rule lives in src/lib/spine.ts, so the same asset always links to the same three pages and a second reader can reproduce the choice.
The counter is a note to yourself — it adds one each time you tap, in motif:remix-count:v1, and goes nowhere. A real, public remix count would need a server and accounts; this site has neither, and it would rather say that than print a number it cannot own.