Skeleton screens won the loading-state argument years ago: perceived performance improves when the page sketches its own layout instead of showing a spinner in a void. But there's a catch nobody mentions — a skeleton is a promise about layout, and a skeleton that promises three cards then renders a table has just caused the exact jarring swap it existed to prevent.
Velocity X's free loading-skeleton is the CSS half done right; the shape-matching half is a rule you have to keep.
The Money Pattern
The shimmer is one background gradient sliding across each grey block — no JS, no SVG, compositor-friendly:
.skeleton {
background: linear-gradient(
90deg,
theme(colors.neutral.200) 25%,
theme(colors.neutral.100) 50%,
theme(colors.neutral.200) 75%
);
background-size: 200% 100%;
animation: shimmer 1.6s linear infinite;
border-radius: theme(borderRadius.md);
}
@keyframes shimmer {
to { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
.skeleton { animation: none; }
}
Compose blocks to mirror the real component: a circle for the avatar, a 60%-width bar for the title, two full-width bars for body text — with the same spacing utilities the real component uses, so loading and loaded are pixel-compatible. That's the whole discipline: the skeleton imports its dimensions from the design, never approximates them.
The Catch
Skeletons have a speed window. Under ~200ms, rendering one is churn — gate it so fast responses go straight to content. Past ~10 seconds, a skeleton becomes a lie of a different kind ("almost there!" forever) — long waits need progress or honesty, not shimmer.
And mark the region aria-busy="true" while loading. Without it a screen reader announces a page of nothing; with it, assistive tech knows to wait. Remove it when content lands — it's the cheapest correct loading semantics on the web.
The Verdict
Skeleton if you know the shape, spinner if you don't, nothing if it's fast. The shimmer is free and MIT — loading-skeleton, source in the public repo — and the app-shell variants (card grids, data grids) live in the full library.









