Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Skeleton

A loading placeholder with an opacity-only pulse — text bars, rectangles, or circles — for surfaces still waiting on data.

Import

import { Skeleton } from "@roger-emerson/ploxum-primitives";

Usage

{/* KPI tile loading state */}
<div className="flex w-56 flex-col gap-3 rounded border border-[var(--ploxum-color-border-subtle)] p-4">
  <Skeleton height={12} label="Loading attendance metrics" width={96} />
  <Skeleton height={28} width={120} />
  <Skeleton lines={2} variant="text" />
</div>
 
{/* Operator row loading state */}
<div className="flex items-center gap-3">
  <Skeleton variant="circle" width={32} />
  <Skeleton height={14} width={160} />
</div>

Props

PropTypeDefaultDescription
variant"text" | "rect" | "circle""rect"Shape. text renders stacked bars; circle renders square aspect.
widthnumber | string"100%" (40 for circle)Width in px or any CSS length.
heightnumber | string16 (40 for circle)Height in px or any CSS length.
linesnumber3Bar count for the text variant; the last bar renders at ~60% width.
labelstringAccessible loading text (sr-only, sets role="status"). When omitted the skeleton is aria-hidden.
classNamestringMerged onto the root.
refRef<HTMLDivElement>Ref to the root.

Design rules

  • Opacity-only pulse (1600ms) — gradient shimmer sweeps are banned by the motion spec. Don't add one with className.
  • Never place Skeleton inside Banner or AlertCard critical contexts. Critical surfaces render their real content or nothing — a pulsing placeholder next to a P1 reads as motion on critical state.
  • The pulse is gated in CSS via motion-safe, so it renders static under prefers-reduced-motion: reduce with no JS.
  • Set label on one skeleton per loading region and leave the rest unlabeled (aria-hidden) — one status announcement per region, not one per bar.

Storybook

Open Skeleton in Storybook ↗