This component is pure CSS — no JavaScript required.
# Skeleton — AGENTS.md
> Auto-generated from `skeleton.docs.md` + the modifier applicability matrix.
> Edit those source files; this file regenerates on build.
## Identity
- **Component:** `skeleton`
- **Layer:** foundations
- **Status:** stable
- **Last updated:** 2026-05-18
## Summary
- The page is loading layout-significant content (cards, lists,
## 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.
## Modifier applicability
Per ADR-0007. Modifiers attach via `data-{category}="value"` attributes.
| Category | Accepts |
|---|---|
| `background` | — |
| `surface` | — |
| `effect` | — |
| `shader` | — |
| `rhythm` | — |
| `motion` | `none`, `standard` |
| `behavior` | — |
## Anatomy
```html
<!-- 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 consumed
### From semantic tier
- `--color-surface-raised` — base color
- `--color-surface-canvas` — highlight color (alternates with base in
shimmer)
- `--radius-button` — default radius
- `--radius-card` — rect variant radius
- `--radius-pill` — pill variant radius
- `--radius-tag` — text variant radius
- `--font-body-regular-size` — text variant height
### Component-tier (defined inline)
- `--skeleton-base` — overridable base color
- `--skeleton-highlight` — overridable highlight color
- `--skeleton-duration` — overridable shimmer cycle
## Accessibility contract
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.
## Guidance
### Do
- Match the placeholder layout to the final content shape.
- Use `.-text` for any single line of text — the height token keeps
vertical rhythm stable.
- Group related skeletons (avatar + name lines) so they read as one
loading unit.
### Don't
- Don't apply skeletons over content the user can already see. Use
them only for layout that hasn't rendered yet.
- Don't add semantic ARIA roles to skeletons. They are decorative.
- Don't keep skeletons visible for sub-100ms loads. Render directly
for fast paths and reserve skeletons for waits where the user would
otherwise see blank space for more than a moment.