Zetta v1.6

Components · Data display & feedback

Skeleton

A loading placeholder that mirrors the shape of the content it stands in for, with a subtle shimmer sweep. Use it instead of a spinner when the layout is known in advance.

Preview

Build with the skill

No package to install — hand this to your AI to generate or migrate the component in your stack.

Prompt for your AI
Apply the Zetta "Skeleton" component to this project using the zetta-design-md skill.

WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `skeleton` spec and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `skeleton` spec in this project's stack.

AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.

ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility.

Anatomy & rules

╭─ skeleton-row ──────────────────────────────────────────╮
│  ●          ████████████████  ← circle-md + text-line   │
│             ████████          ← text-line (short)        │
╰─────────────────────────────────────────────────────────╯

╭─ skeleton-rectangle-md ─────────────────────────────────╮
│  [████████████████████████]   ← card image placeholder  │
╰─────────────────────────────────────────────────────────╯

████████████████               ← skeleton-text-title
████████████████████████████   ← skeleton-text
████████████████████████       ← skeleton-text-short
State Visual
Loading {colors.skeleton-bg} fill with shimmer animation
Shimmer Linear gradient sweeping left to right, {motion.duration-slow} loop

When to use

  • Content that takes > 300ms to load.
  • Replace entire sections — not individual words within a sentence.

When not to use

  • Operations under 300ms — an immediate result is better than a flash of skeleton.
  • Error states — use Section Message or Empty State instead.
  • Spinners are better when the full layout is unknown (e.g. dynamic search results with unpredictable shape).

Do

  • Match skeleton shapes to the actual content as closely as possible — same number of lines, same approximate widths.
  • Use skeleton-row and skeleton-block to compose realistic layout placeholders.
  • Always remove skeletons and replace with real content — never leave them visible after load completes.

Don't

  • Don't animate skeletons if prefers-reduced-motion is active.
  • Don't use skeleton for interactive elements — buttons and inputs should be hidden or disabled during loading, not replaced with skeletons.
  • Match skeleton shape to the real content: text lines for text, circles for avatars, rectangles for images.
  • Vary skeleton line widths to look natural — 60%, 80%, 90% of container width for text lines.
  • Never show skeleton and real content simultaneously.
  • Accessible label: the skeleton container should have aria-busy="true" and a visually hidden "Loading…" label.

Keyboard

Skeleton is a visual placeholder and has no keyboard behavior. It is replaced by real content once loading completes.

Accessibility

  • All skeleton elements: aria-hidden="true" — they are decorative placeholders.
  • The loading region should have role="status" with a visually hidden live announcement: "Loading content…".
  • When loading completes, update the live region: "Content loaded" or remove it.
  • Never make skeletons focusable — tabIndex="-1" or omit from tab order entirely.

Spec

Values and token references resolved straight from the Zetta spec.

skeleton
backgroundColor
{colors.skeleton-bg}
shimmerColor
{colors.skeleton-shimmer}
borderRadius
{rounded.base}
animationDuration
1.5s
animationTimingFunction
ease-in-out
animationIterationCount
infinite
skeleton-text
height
14px
borderRadius
{rounded.full}
backgroundColor
{colors.skeleton-bg}
width
100%
skeleton-text-short
width
60%
skeleton-text-title
height
20px
borderRadius
{rounded.full}
backgroundColor
{colors.skeleton-bg}
width
40%
skeleton-circle
borderRadius
{rounded.full}
backgroundColor
{colors.skeleton-bg}
skeleton-circle-sm
width
24px
height
24px
skeleton-circle-md
width
40px
height
40px
skeleton-circle-lg
width
48px
height
48px
skeleton-rectangle
borderRadius
{rounded.base}
backgroundColor
{colors.skeleton-bg}
width
100%
skeleton-rectangle-sm
height
80px
skeleton-rectangle-md
height
160px
skeleton-rectangle-lg
height
240px
skeleton-row
display
flex
alignItems
center
gap
{spacing.sm}
skeleton-block
display
flex
flexDirection
column
gap
{spacing.sm}