Free pack

Skeleton .mv-skeleton

Squelettes de chargement avec un reflet synchronisé sur toute la page (ou une pulsation), formes de texte, de titre, d’avatar, de média et de bouton, et un fondu flouté vers le contenu chargé, piloté par aria-busy.

CatégorieFeedback
TypeCSS pur (.mv-skeleton)
Statutstable
Keywordsskeleton, placeholder, loading, shimmer, pulse, chargement, reveal

When to use

  • Content is loading and its layout is known, so placeholders can prevent layout shift
  • Lists, cards or profiles are fetched asynchronously and should reveal with a cross-fade once ready
  • Many placeholders appear at once and need one synchronized shimmer instead of competing waves

Avoid when

  • Placeholders should visibly morph into the real content rather than simply fade → use Skeleton Morph instead
  • The loading area has no predictable shape, like a button action or a small inline wait → use Spinner instead
  • The request finished and returned nothing; show a real empty state instead → use Empty instead

Installation

node scripts/add.mjs skeleton --out ./src/marvelous

Agent IA avec le serveur MCP Marvelous UI : install_components({ slugs: ["skeleton"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Fichiers copiés (dépendances comprises) : tokens/tokens.css, core/base.css, components/skeleton/skeleton.css.

Utilisation

Balisage de référence : partez de celui-ci et personnalisez-le avec les attributs, data-* et les variables CSS :

<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,17rem),1fr));gap:1.5rem;width:100%;align-items:start">

  <!-- Card composition (shimmer) -->
  <div aria-hidden="true" style="display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);background:var(--mv-surface)">
    <div class="mv-skeleton" data-shape="media"></div>
    <div class="mv-skeleton" data-shape="heading" style="width:70%"></div>
    <div class="mv-skeleton-text">
      <span class="mv-skeleton"></span>
      <span class="mv-skeleton"></span>
      <span class="mv-skeleton"></span>
    </div>
    <div style="display:flex;gap:.5rem">
      <span class="mv-skeleton" data-shape="button"></span>
      <span class="mv-skeleton" data-shape="button" style="width:4.5rem"></span>
    </div>
  </div>

  <!-- List rows (pulse, set on the container) -->
  <div data-skeleton="pulse" aria-hidden="true" style="display:grid;gap:1.1rem;padding:1.25rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);background:var(--mv-surface)">
    <div style="display:flex;gap:.75rem;align-items:center"><span class="mv-skeleton" data-shape="circle"></span><div class="mv-skeleton-text" style="flex:1"><span class="mv-skeleton" style="width:55%"></span><span class="mv-skeleton" style="width:80%"></span></div></div>
    <div style="display:flex;gap:.75rem;align-items:center"><span class="mv-skeleton" data-shape="circle"></span><div class="mv-skeleton-text" style="flex:1"><span class="mv-skeleton" style="width:40%"></span><span class="mv-skeleton" style="width:70%"></span></div></div>
    <div style="display:flex;gap:.75rem;align-items:center"><span class="mv-skeleton" data-shape="circle"></span><div class="mv-skeleton-text" style="flex:1"><span class="mv-skeleton" style="width:62%"></span><span class="mv-skeleton" style="width:48%"></span></div></div>
    <div style="display:flex;gap:.75rem;align-items:center"><span class="mv-skeleton" data-shape="square" style="--mv-skeleton-size:2.5rem"></span><div class="mv-skeleton-text" style="flex:1"><span class="mv-skeleton" style="width:50%"></span><span class="mv-skeleton" style="width:75%"></span></div></div>
  </div>

  <!-- Reveal: skeleton cross-fades into the loaded content -->
  <div style="display:grid;gap:.75rem">
    <div class="mv-skeleton-reveal" id="sk-demo-reveal" aria-busy="true" aria-live="polite" style="padding:1.25rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);background:var(--mv-surface)">
      <div data-skeleton aria-hidden="true" style="display:grid;gap:1rem">
        <div style="display:flex;gap:.75rem;align-items:center">
          <span class="mv-skeleton" data-shape="circle" style="--mv-skeleton-size:3rem"></span>
          <div class="mv-skeleton-text" style="flex:1"><span class="mv-skeleton" style="width:60%"></span><span class="mv-skeleton" style="width:40%"></span></div>
        </div>
        <div class="mv-skeleton-text"><span class="mv-skeleton"></span><span class="mv-skeleton"></span></div>
      </div>
      <div data-content style="display:grid;gap:1rem">
        <div style="display:flex;gap:.75rem;align-items:center">
          <span style="display:grid;place-items:center;width:3rem;height:3rem;border-radius:50%;background:var(--mv-accent-subtle);color:var(--mv-accent-fg);font-weight:600">CR</span>
          <div style="display:grid;gap:2px"><strong style="font-size:.875rem">Chloe Reed</strong><span style="font-size:.8125rem;color:var(--mv-fg-muted)">Product designer · London</span></div>
        </div>
        <p style="margin:0;font-size:.875rem;line-height:1.55;color:var(--mv-fg-muted)">Working on the customer portal redesign and the new design system.</p>
      </div>
    </div>
    <button class="mv-button" data-variant="outline" data-size="sm" id="sk-demo-reload" style="justify-self:start">Reload</button>
  </div>
</div>
<script type="module">
  const box = document.getElementById("sk-demo-reveal");
  const btn = document.getElementById("sk-demo-reload");
  let timer = 0;
  const load = () => {
    clearTimeout(timer);
    box.setAttribute("aria-busy", "true");
    timer = setTimeout(() => box.setAttribute("aria-busy", "false"), 1800);
  };
  btn?.addEventListener("click", load);
  if (box) load();
</script>

API

Attributes

NameTypeDefaultDescription
data-shapetext | heading | circle | square | button | mediaReady-made shapes (circle/square use --mv-skeleton-size).
data-variantpulse | static(shimmer)Opacity pulse, or no motion.
data-skeleton="pulse"on a containerSwitches every descendant skeleton to pulse.
aria-busytrue | falseOn .mv-skeleton-reveal: loading state.

CSS classes

NameDescription
mv-skeletonLoading block (any width/height, 1rem tall by default).
mv-skeleton-textStack of .mv-skeleton lines; the last one is shorter.
mv-skeleton-revealStacks [data-skeleton] and [data-content]; aria-busy="true" shows the skeleton, "false" reveals the content with a fade + blur.

CSS variables

NameDefaultDescription
--mv-skeleton-baseBlock color (default: text at 8%, adapts to the background).
--mv-skeleton-shineShimmer color.
--mv-skeleton-duration2sDuration of one cycle.
--mv-skeleton-size2.5remSize of the circle / square shapes.

Accessibility

Skeletons are decorative: set aria-hidden="true" on their container, and aria-busy="true" (+ aria-live="polite") on the loading region. The shimmer is synced (background-attachment: fixed) to avoid the flicker of multiple waves. Reduced motion: shimmer and pulse stop, the reveal is instant.

Cette page a été traduite par IA. Signaler un problème de traduction