Skeleton
A loading placeholder shaped like the content it stands in for.
Shapes
A line of text, an avatar and a card or an image. The pulse runs while the data loads.
html
<div class="vt-skeleton vt-skeleton-line" aria-hidden="true"></div>
<div class="vt-skeleton vt-skeleton-circle" aria-hidden="true"></div>
<div class="vt-skeleton vt-skeleton-card" aria-hidden="true"></div>
Text
Several lines stand in for a paragraph; the last one is shorter, as the last line of text usually is.
html
<div class="vt-flex vt-flex-col vt-gap-2" aria-hidden="true">
<div class="vt-skeleton vt-skeleton-line"></div>
<div class="vt-skeleton vt-skeleton-line"></div>
<div class="vt-skeleton vt-skeleton-line"></div>
</div>
List Row
An avatar and two lines — the shape of a list item or a comment.
html
<div class="vt-flex vt-items-center vt-gap-3" aria-hidden="true">
<div class="vt-skeleton vt-skeleton-circle"></div>
<div class="vt-flex vt-flex-col vt-gap-2 vt-flex-1">
<div class="vt-skeleton vt-skeleton-line"></div>
<div class="vt-skeleton vt-skeleton-line"></div>
</div>
</div>
<div class="vt-flex vt-items-center vt-gap-3" aria-hidden="true">
<div class="vt-skeleton vt-skeleton-circle"></div>
<div class="vt-flex vt-flex-col vt-gap-2 vt-flex-1">
<div class="vt-skeleton vt-skeleton-line"></div>
<div class="vt-skeleton vt-skeleton-line"></div>
</div>
</div>
Card
A card that is loading keeps its frame; the placeholders fill its body.
html
Loading…
<div class="vt-card vt-w-full vt-max-w-sm" aria-busy="true">
<div class="vt-card-body">
<div class="vt-skeleton vt-skeleton-card" aria-hidden="true"></div>
<div class="vt-flex vt-flex-col vt-gap-2" aria-hidden="true">
<div class="vt-skeleton vt-skeleton-line"></div>
<div class="vt-skeleton vt-skeleton-line"></div>
</div>
<span class="vt-sr-only">Loading…</span>
</div>
</div>
Block
A bare vt-skeleton takes whatever size the layout gives it — a button, a chart, a tab.
html
<div class="vt-skeleton vt-w-32 vt-h-8" aria-hidden="true"></div>
<div class="vt-skeleton vt-w-24 vt-h-8" aria-hidden="true"></div>
Guidelines
Do
Don't
Anatomy
div[aria-busy="true"] ← The region that is loading
├── div.vt-skeleton.vt-skeleton-* ← One placeholder per piece of content (aria-hidden)
└── span.vt-sr-only ← "Loading…" for screen readers
API Reference
| Class | Type | Description |
|---|---|---|
vt-skeleton |
Base | The placeholder surface — surface-moderate, radius-md, a slow pulse; a bare block takes the size the layout gives it |
vt-skeleton-line |
Shape | A line of text — full width, 16px tall, radius-2xs; the last of several lines is 60% wide |
vt-skeleton-circle |
Shape | An avatar — 40px, round |
vt-skeleton-card |
Shape | A card or an image — full width, 96px tall, radius-2xl |
Accessibility
| Feature | Details |
|---|---|
| Busy | The region that is loading carries aria-busy="true" until the content arrives |
| Hidden | The placeholders are decoration: aria-hidden="true", with one visually hidden "Loading…" for screen readers |
| Motion | The pulse stops under prefers-reduced-motion; the placeholders stay visible |