A skeleton that does not lie about the layout
CaveatA skeleton whose blocks are not the size of the real content is worse than a spinner: the page visibly rearranges the moment data lands. Match the line heights and widths you will actually render.
FallbackWrap the sweep in prefers-reduced-motion. A looping shimmer is exactly the kind of endless movement that setting exists to stop.
.line {
background: linear-gradient(90deg,
#1a212d 25%, #26303f 50%, #1a212d 75%)
0 0 / 300% 100%;
animation: sweep 1.4s linear infinite;
}
@keyframes sweep {
to { background-position: -300% 0; }
}
Published
Questions
Why does background-position not move my gradient?
Percentage positions are measured against the gap between the element and the image, so a background sized at 100% has no gap and cannot move. Make the gradient larger than the box, as the 300% 100% size here does, and the percentage offset starts to travel.
Should a shimmer animate transform instead of background-position?
Animating background-position repaints the element on every frame. A gradient on a pseudo-element moved with transform can run on the compositor, which matters once dozens of placeholder lines shimmer at once. Clip it with overflow: hidden on the block.
How do I make a skeleton loader accessible to screen readers?
Set aria-busy="true" on the region that is loading and hide the placeholder blocks with aria-hidden="true", since they carry no content. Remove aria-busy when the real content lands so assistive technology reads the finished region.