flyingsalmon

Skeleton

The region-loading placeholder. A pulsing block holds the shape of content that has not arrived yet.

Spinner or skeleton

A skeleton reports that a region is loading: a list, a card, a page of content on first load. A spinner reports that an action is running: a form submits, a setting saves. Never swap a control the user just clicked for a skeleton, and never show both for one wait.

Variants

Three shapes. text is the default: one line tall and full width, so stacked lines scale with the surrounding font. circle and rectangle carry no size of their own.

Light
Dark

Sizing

There are no size props. The consumer sizes every block through className, because a placeholder must match the content it stands in for. Radii come from the radius scale: the base radius for rectangle, a tighter step for text, full rounding for circle. Every block also carries its shape as data-variant, so a consumer can target one shape from CSS or from a test.

Light

Small text line

Heading line

Dark

Small text line

Heading line

Motion

One state: pulsing. The whole block fades from full opacity to half and back on a 2s ease-in-out cycle, on CSS keyframes. There is no enter animation and no exit animation. The skeleton appears at once, and the content replaces it at once.

Accessibility

The skeleton is always aria-hidden and never takes a tab stop, so a screen reader hears nothing from it. The region that is loading owns the announcement: put aria-busy on the container that will hold the real content.