Skeleton

@sushindustries/ui

The wait, drawn as the thing being waited for: a line, a block or a circle, shimmering unless motion is reduced.

5 min read
View as MarkdownEdit on GitHub

Composing it

Each shape assumes a different parent, because each one sizes itself differently:

ShapeSizes itselfNeeds from the parent
line100% wide, 0.9em talla real width - a flex row or a block
block100% wide, 16/9 aspect ratioa real width, or it renders at zero height
circlefixed 40 by 40nothing - ignores the parent entirely

width and height override any of these directly, on any shape.

Variants

shape is the one variant, and it is a data-shape attribute rather than a class, so the stylesheet - not the caller - decides what "line", "block" and "circle" look like:

<Skeleton shape="circle" />
tsx
.skeleton[data-shape="circle"] {
	width: 40px;
	height: 40px;
	border-radius: 999px;
}
css

Motion and reduced motion

Under prefers-reduced-motion: reduce the sweep animation is removed entirely and the shape falls back to a flat --bg-2 fill. Nothing pulses or fades instead - a skeleton is a placeholder, and a placeholder that keeps moving for someone who asked for less motion is still moving.

When not to use it

Not a loading indicator - it has no role="status" and announces nothing, because it is meant to disappear the instant real content is ready, not to tell anyone how long that will take. Reach for SpinnerSpinnerOne ring, one border, one turn - with a visually hidden label, because a spinner with nothing to announce is just an animation.@sushindustries/ui · content when there is an operation in flight worth announcing, and for content already on the page that is merely stale (a table mid-refetch, say) rather than absent.