Skeleton
Status: stableAccessibility reviewedtenet-ui@0.4.0
import { Skeleton } from 'tenet-ui';Also accepts every prop of HTMLAttributes<HTMLSpanElement>.
Source: src/components/Skeleton/Skeleton.tsx
Usage guidelines
Section titled “Usage guidelines”When to use: as a placeholder for content whose shape you know while it loads (a card, an avatar + name, a few lines of copy). It reduces perceived wait and prevents layout shift versus a blank space. If you can’t predict the layout, or the wait is short/indeterminate with no structure to mimic, use a Spinner instead. If you can show real progress, use a progress bar.
- The skeleton is decorative. It renders
aria-hidden="true", so screen readers skip it. Announce the wait on the surrounding region, e.g.aria-busy="true"on the container plus a visually-hidden status (or aSpinnerwith alabel). Don’t rely on the skeleton blocks to convey “loading” to assistive tech. - Match the shape and size of the real content so swapping in the loaded UI doesn’t shift layout. Use
width/heightto sizerect, and onecirclefor an avatar. width/heightare applied inline (number → px, string like'60%'→ as-is). This is the one place inlinestyleis correct — caller-driven dimensions aren’t a token concern. All color, radius, and spacing stay on tokens.circleis always a full-radius square: passwidthand it mirrors to height; theradiusprop is ignored for it.- The shimmer honors
prefers-reduced-motion— it drops to a gentle opacity pulse — so you don’t need to gate it yourself.
Do / Don’t
Section titled “Do / Don’t”- ✅ Stack a few
textlines of decreasing width to mimic a paragraph:<div aria-busy="true"><span className="sr-only">Loading article…</span><Skeleton variant="text" /><Skeleton variant="text" width="80%" /><Skeleton variant="text" width="60%" /></div> - ✅ Avatar + name placeholder:
<Skeleton variant="circle" width={48} /> - ❌ Relying on the skeleton alone for the loading announcement — it’s
aria-hidden, so this strands screen-reader users:<Skeleton aria-label="Loading" /> {/* hidden from AT — the label never reaches anyone */}
See also: Spinner and ProgressBar for other waits, EmptyState once data loads empty; system guideline data-display (loading rules).
Examples
Section titled “Examples”5 stories for Skeleton — open in Storybook
- Default
components-skeleton--default - Text
components-skeleton--text - Rect
components-skeleton--rect - Circle
components-skeleton--circle - Card
components-skeleton--card
The full matrix (all args, controls, accessibility panel) is in Storybook.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'text' | 'rect' | 'circle'SkeletonVariant | text | Shape of the placeholder. text mimics a line of copy, rect a block, circle an avatar/icon. |
width | string | number | — | Width applied inline (number → px, string → as-is, e.g. '60%'). |
height | string | number | — | Height applied inline (number → px, string → as-is). For circle it mirrors width. |
radius | 'small' | 'medium' | 'large' | 'full'SkeletonRadius | — | Corner radius token. circle always renders as full regardless of this prop. |