Overview
CSS shimmer skeleton loaders fill the layout shape of incoming content with muted blocks that shimmer to indicate work is happening. The sweep is a linear gradient with a bright center stop that background-position animates across each block. The skeleton itself stays static — only the motion layer above it moves — so layout never shifts when the real content swaps in.
When to use it
Reach for shimmer skeletons on any content that arrives async and has a predictable layout shape (feed cards, profile rows, product tiles, chat-message lists). Skip them for content under 200ms load time — the skeleton appears and disappears so fast it looks like a glitch. Skip them when the real content has wildly variable shape; a skeleton that does not match the real layout causes a layout shift on swap, which is worse than no skeleton at all.
How it works
Each skeleton block has background: linear-gradient(90deg, neutral, highlight, neutral) with background-size: 200% 100% so the gradient is wider than the block. A @keyframes animation moves background-position from 200% 0 to -200% 0 (or similar) over ~1.4s, so the bright band sweeps left-to-right repeatedly. Multiple blocks can share the same animation with staggered delays for a wave effect. The skeleton geometry (avatar, line widths, media block heights) is hand-authored to match the real content’s layout so the swap is invisible.
The shimmer layer should be treated as an affordance, not the loading state itself. The actual skeleton loader is the stable geometry: reserved media blocks, avatar circles, and text rows sized to match the incoming content. The gradient sweep only tells the user that the placeholder is alive; it cannot fix a placeholder layout that does not match the real DOM.
Production gotchas
Skeleton-to-real swap must preserve layout exactly — if the real content has different padding or line-height than the skeleton, the swap visibly jumps. Animate background-position, not transform: translateX on a separate sheen element; sheen-element approaches require an extra layer that can repaint outside its parent. On lower-end mobile the gradient sweep cost across many simultaneous skeletons can drop frames — cap the visible skeletons to what fits in viewport, or pause off-screen ones via content-visibility: auto.
Accessibility
Skeletons are decorative; wrap them in aria-hidden="true" and pair with anaria-live="polite" announcement when the real content arrives (e.g. "Posts loaded."). Under prefers-reduced-motion: reduce freeze the sweep at its rest position; the muted blocks alone still communicate “loading” without the motion.
References
Implementation depth
The skeleton is the reserved geometry, not the shimmer. Match avatar size, media ratio, row count, and line widths to the loaded content first; then use linear-gradient motion as a progress affordance.
Pause work you cannot see. Many simultaneous background-position animations can cost real battery on long feeds, so combine content-visibility, viewport limits, and reduced motion to keep loading states quiet.