Use <Skeleton /> while async content resolves. The shimmer
sweep is CSS-only and disables itself when the user has
prefers-reduced-motion: reduce set; in that case the bar holds
a static rgba(255,255,255,0.06) tint with no animation.
Variants
rect (default)
<Skeleton width="100%" height="120px" /> text
<Skeleton variant="text" width="80%" /> circle
<Skeleton variant="circle" height="48px" /> variants.astro | astro
<!-- rect (default) -->
<Skeleton width="100%" height="120px" />
<!-- text — three lines, decreasing width -->
<Skeleton variant="text" width="90%" />
<Skeleton variant="text" width="75%" />
<Skeleton variant="text" width="55%" />
<!-- circle — avatar placeholders -->
<Skeleton variant="circle" height="32px" />
<Skeleton variant="circle" height="48px" />
<Skeleton variant="circle" height="64px" /> Composed: card placeholder
A common combination — circle avatar, two text lines, a rect body.
card-placeholder.astro | astro
<div class="card">
<div class="card__head">
<Skeleton variant="circle" height="40px" />
<div class="card__lines">
<Skeleton variant="text" width="50%" />
<Skeleton variant="text" width="30%" />
</div>
</div>
<Skeleton width="100%" height="180px" />
</div> Without shimmer
Pass shimmer={false} for static placeholders (e.g. cached states).
static.astro | astro
<Skeleton width="100%" height="48px" shimmer={false} /> Anatomy
| Property | Behavior |
|---|---|
| Box | The placeholder rectangle/circle/line. Sized by `width` / `height` props. |
| Shimmer | CSS-only sweep on a linear-gradient background. Disabled under reduced-motion. |
| Radius | `rect` → --ds-radius-md, `text` → --ds-radius-sm, `circle` → --ds-radius-full. |
Accessibility
- Each skeleton renders
role="presentation"— assistive tech skips them entirely. - The shimmer animation respects
prefers-reduced-motion: reduceautomatically — no per-call opt-out needed. - For long async waits (>2s), pair Skeleton with a polite
role="status"region carrying a real message ("Loading orders…") so SR users know what is happening. - Outer dimensions should match the final content; this avoids the screen-reader's focus jumping after hydration.
Do / Don't
| Property | Do | Don't |
|---|---|---|
| Layout parity | Match the final content shape — same number of lines, same circle sizes. | Show a single rect for a complex layout — the jolt on hydration is jarring. |
| Duration | Show skeletons after 100-200ms of latency; never below 100ms (looks like a flash). | Keep skeletons on screen >2s — switch to a real loading message at that point. |
| Shimmer | Default shimmer on; trust the prefers-reduced-motion media query to silence it. | Force `shimmer={false}` for everyone — most users benefit from the activity cue. |
| Cached states | Use `shimmer={false}` for content that came from cache (no real wait). | Use Skeleton for placeholders that will never resolve — render an empty state instead. |
| Above the fold | Render the same outer dimensions as the resolved content — avoids cumulative layout shift. | Animate height during load — destroys CLS scores. |
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
width | string (CSS length) | 100% | Ignored on circle when height is set. |
height | string (CSS length) | 1rem | Forced to 1em for variant="text". |
variant | "rect" | "text" | "circle" | "rect" | Picks the corner radius. |
shimmer | boolean | true | Auto-disabled under prefers-reduced-motion: reduce. |
class | string | "" | Appended after default classes. |
Tokens consumed
--ds-radius-md— rect variant corners--ds-radius-sm— text variant corners--ds-radius-full— circle variant
Source
src/components/ui/Skeleton.astro