Skip to main content

Skeleton

A skeleton holds the place of content that is still loading, painted with --muted. It is purely visual: it never takes focus and, unless label is given, is hidden from assistive technology, so the host (or label) is what announces the busy state.

import Skeleton from '@zuilib/components/skeleton'

Shapes

rect is one block, circle a disc the size of the md Avatar (--control-height-md), text a stack of bars each 1em tall so it follows the surrounding font size. With several lines the last bar is 60% wide so the block reads as a paragraph.

Loading example

Composed

Combine shapes to mirror the layout that is loading, so nothing jumps when the content arrives.

Loading example

Animation

pulse fades the surface in and out; wave sweeps a foreground/8 sheen across it so it reads on both the light and the dark surface. Both stop under prefers-reduced-motion.

Loading example

Announced

label renders visually hidden text and makes the root a role="status" region. Screen readers announce changes inside a region that is already mounted, not one inserted together with its text, so keep the region in the document and toggle its content, or let the host carry aria-busy.

Loading example

Props

SkeletonProps extends the <div> attributes minus children, with:

PropTypeDefaultDescription
shape'rect' | 'circle' | 'text''rect'
widthnumber | stringrect / text w-full; circle --control-height-mdNumber is px. A circle given only one of width / height uses it for both
heightnumber | stringrect h-4; circle --control-height-md; text 1em per lineNumber is px. For text it is the height of each line
linesnumber1Bars for shape="text"; the last of several is 60% wide
animate'pulse' | 'wave' | 'none''pulse'
labelstringVisually hidden text; makes the root role="status" instead of aria-hidden
classNamestringMerged last onto the root

Slots

SlotElementNotes
[data-slot="skeleton"]divRoot. Also data-shape and data-animate
[data-slot="skeleton-line"]divOne bar of a text skeleton; aria-hidden
[data-slot="skeleton-label"]spanThe visually hidden label

Tokens

TokenUsed for
--mutedSurface
--foregroundWave sheen (at 8% alpha)
--control-height-mdCircle diameter
--radius-md, --radius-sm, --radius-fullRect, text bar and circle corners
--animate-pulse, --animate-skeleton-waveAnimations

Accessibility

  • Without label the root is aria-hidden; mark the loading host with aria-busy so the state is still conveyed.
  • With label the root is role="status" (polite). It is not aria-busy, since a busy live region defers its own content and would hold back the label.
  • Never focusable and never interactive.
  • Both animations stop under prefers-reduced-motion.
  • Spinner: an indeterminate ring for short waits and buttons.
  • Progress: a determinate bar when the amount is known.
  • Avatar: what the circle shape stands in for.