Skip to content

Skeleton / Spinner

Two loading primitives in one file:

  • .bp-skeleton — block placeholder with shimmer animation. Use it to represent content that is loading (text lines, headings, images, cards).
  • .bp-spinner — inline rotating arc. Use it for actions in progress (form submit, button loading state).

Both respect prefers-reduced-motion — animation stops, shape remains visible.

Text lines

<div style="display:flex;flex-direction:column;gap:0.5rem;max-width:24rem">
<span class="bp-skeleton bp-skeleton--heading" style="width:40%"></span>
<span class="bp-skeleton bp-skeleton--text"></span>
<span class="bp-skeleton bp-skeleton--text"></span>
<span class="bp-skeleton bp-skeleton--text" style="width:70%"></span>
</div>

Card skeleton

<div class="demo-skeleton-card">
<span class="bp-skeleton bp-skeleton--rect demo-skeleton-card__image"></span>
<div class="demo-skeleton-card__body">
<span class="bp-skeleton bp-skeleton--heading" style="width:55%"></span>
<span class="bp-skeleton bp-skeleton--text"></span>
<span class="bp-skeleton bp-skeleton--text" style="width:80%"></span>
</div>
</div>

Avatar + text

<div class="demo-skeleton-user">
<span class="bp-skeleton bp-skeleton--circle demo-skeleton-user__avatar"></span>
<div class="demo-skeleton-user__lines">
<span class="bp-skeleton bp-skeleton--text demo-skeleton-user__name"></span>
<span class="bp-skeleton bp-skeleton--text demo-skeleton-user__sub"></span>
</div>
</div>

Default

<span class="bp-spinner" role="status" aria-label="Loading"></span>

Sizes

<span class="bp-spinner demo-spinner--sm" role="status" aria-label="Loading"></span>
<span class="bp-spinner" role="status" aria-label="Loading"></span>
<span class="bp-spinner demo-spinner--lg" role="status" aria-label="Loading"></span>

Button loading state

<button class="bp-btn bp-btn--primary demo-btn-loading" disabled>
<span class="bp-spinner demo-btn-loading__spinner" role="status" aria-label="Saving"></span>
Saving…
</button>
VariableDefaultDescription
--skeleton-color--bp-color-bg-subtleBase color
--skeleton-highlight--bp-color-bg-elevatedShimmer highlight color
--skeleton-radius--bp-radius-mdBorder radius
--skeleton-duration1.5sShimmer animation duration
VariableDefaultDescription
--spinner-color20% primary, transparentTrack color (semi-transparent arc)
--spinner-color-active--bp-primaryArc color
--spinner-size1.5remDiameter
--spinner-width2pxStroke width
--spinner-duration0.7sRotation duration
ClassShape
.bp-skeleton--textFull-width, 1em tall — for body text lines
.bp-skeleton--heading60% wide, 1.5em tall — for headings
.bp-skeleton--circleborder-radius: 50% — for avatars
.bp-skeleton--rectNo border radius — for images

All variants are display: block. Set width and height (or aspect-ratio) on the element or via a wrapper class.

No axe violations tested 2026-05-11
  • Add role="status" and aria-label="Loading" to .bp-spinner so screen readers announce the loading state.
  • Skeleton placeholders are purely visual — no role or label needed. Screen readers will skip them.
  • Both components stop animating under prefers-reduced-motion: reduce. The skeleton becomes a static block; the spinner keeps its shape but stops rotating.
APIAvailabilityUsed forWithout it
@keyframes Widely available Baseline 2012 Shimmer and spin animationsStatic blocks/borders
prefers-reduced-motion Widely available Baseline 2020 Disabling animationAnimation always plays