Skip to content

Skeleton

A decorative placeholder shown while content loads. Mark instances aria-hidden="true" and set aria-busy on the region they replace. Honors prefers-reduced-motion.

Live example
<div
  role="status"
  aria-busy="true"
  aria-label="Loading"
  style="display: flex; gap: var(--re-space-3); align-items: center; width: min(22rem, 100%)"
>
  <div
    class="re-skeleton"
    data-shape="circle"
    aria-hidden="true"
    style="width: 3rem; height: 3rem; flex: none"
  ></div>
  <div style="flex: 1; display: grid; gap: var(--re-space-2)">
    <div class="re-skeleton" data-shape="text" aria-hidden="true" style="width: 70%"></div>
    <div class="re-skeleton" data-shape="text" aria-hidden="true" style="width: 40%"></div>
  </div>
</div>
Live example
<div class="re-skeleton" aria-hidden="true" style="max-width: 22rem; height: 8rem"></div>
  • Semantics — Each .re-skeleton is a non-interactive <div> with no role and is not focusable, so there is no keyboard interaction. The placeholders are purely decorative: mark them aria-hidden="true" so assistive technology skips them.
  • Loading state — Announce the load on the region the skeleton replaces, not on the placeholder. The card demo wraps the placeholders in role="status" with aria-busy="true" and aria-label="Loading", so screen readers announce “Loading” while the content is pending; clear aria-busy (or swap in the real content) once it arrives.
  • Reduced motion — The shimmer is animated by default but fully disabled under prefers-reduced-motion: reduce (both the animation and the moving gradient are removed), leaving a static muted block.

See the accessibility guide for shared patterns.