Skeleton
Pulse bars that stand in for content while a value is fetching.
Pulse bars
Placeholder bars for unknown values. Known labels stay as text. The fill
is bg-skeleton so the pulse reads on cards and tinted drawers — not
--muted, which disappeared on light.
You might be looking for:
- Shimmer
Animated text highlight while copy is generating.
- Page Loading
Full-viewport wait — mark only, no content bars.
How we compose them
Start from the loaded layout and swap only what you do not know yet. Do not invent a second structure, a spinner, or a generic pulse block that ignores the frame.
Keep the same shell, gaps, and row heights. If a title or caption is
already known, render it as text and pulse the value. When the loaded UI
is a shared component, use its *Skeleton sibling so loading and loaded
cannot drift — those pages document the extra props (known title, row
count, plot silhouette). One-off screens compose Skeleton bars by hand
with h-* / w-*. Do not use Page Loading
for in-page content.
Fill is always bg-skeleton. Do not hardcode bg-muted or
bg-neutral-* on bars.
Usage
import { Skeleton } from "@sophic/portal-ui/components/ui/skeleton";
import {
ItemListSkeleton,
SectionCard,
StatMetricCardSkeleton,
} from "@sophic/portal-ui";
<Skeleton className="h-7 w-40" />
<StatMetricCardSkeleton title="Money-weighted" subtitle />
<SectionCard title="Pending orders">
<ItemListSkeleton rows={3} />
</SectionCard>Props
Skeleton also accepts every native <div> attribute.
Prop
Type
Default
Changelog
- 2 September 2026 — Pulse uses
--skeleton(bg-skeleton) instead of--muted. Holding return tiles pulse an All-time subtitle row. Full notes