Malevich

Primitives/

Skeleton

Shimmer placeholder. 5 shapes. Reduced-motion aware.

Skeleton

A layout placeholder rendered while content loads. Composed by the consumer to approximate the shape of the content that will replace it.

When to use

Skeletons work best when the placeholder shape closely matches the final content. A skeleton that looks nothing like the loaded result disorients more than it reassures.

Variants

Variant Class Use for
Default .skeleton Generic rectangle, button-like radius
Circle .skeleton.-circle Avatars, circular icons
Rect .skeleton.-rect Cards, large image placeholders
Pill .skeleton.-pill Tags, pill-shaped buttons
Text .skeleton.-text Single line of body text (height fixed)
Muted .skeleton.-muted De-emphasized on raised surfaces

Modifiers compose with sizing. Width and height are set inline by the consumer because they depend on the final layout.

Anatomy

<!-- Single line of text -->
<div class="skeleton -text" style="inline-size: 80%;"></div>

<!-- Three lines, last one shorter -->
<div class="skeleton -text" style="inline-size: 100%; margin-block-end: 0.5rem;"></div>
<div class="skeleton -text" style="inline-size: 95%; margin-block-end: 0.5rem;"></div>
<div class="skeleton -text" style="inline-size: 60%;"></div>

<!-- Avatar + name pair -->
<div style="display:flex; gap:1rem; align-items:center;">
  <div class="skeleton -circle" style="inline-size: 2.5rem; block-size: 2.5rem;"></div>
  <div style="flex: 1;">
    <div class="skeleton -text" style="inline-size: 40%; margin-block-end: 0.5rem;"></div>
    <div class="skeleton -text" style="inline-size: 25%;"></div>
  </div>
</div>

<!-- Card -->
<div class="skeleton -rect" style="inline-size: 100%; aspect-ratio: 16/9;"></div>

Tokens used

From semantic tier

Component-tier (defined inline)

Accessibility

Skeletons are purely visual placeholders and should not be announced by screen readers. They lack role="status". The parent container that swaps skeletons for content should manage announcements (often via aria-live or aria-busy on the loading region).

For sustained loading where ARIA feedback matters, pair a skeleton group with a single aria-live="polite" region that announces "Loading" when loading begins and the loaded content when it arrives.

The component honors prefers-reduced-motion: reduce by removing the shimmer animation and rendering a flat tone.

Edge cases

Do

Don't