Comment faire un loader ou un skeleton screen en CSS pur ?

Un loader indique qu'il se passe quelque chose pendant que le contenu arrive. La plupart sont en CSS pur : une bordure ou un arc SVG que l'on fait tourner avec @keyframes, des points qui grossissent l'un après l'autre grâce à animation-delay, ou des skeleton screens, des blocs gris qui ont la forme du contenu final et que traverse un reflet en mouvement.

Comment choisir, performances et accessibilité
  • Préférez un skeleton screen comme Card Skeleton quand vous connaissez la mise en page de ce qui charge, un Classic Spinner ou des Bouncing Dots pour une attente courte et incertaine, et une barre de progression quand l'avancement est mesurable.
  • Ajoutez role="status" avec un texte alternatif pour que les lecteurs d'écran annoncent l'attente, n'animez que transform et opacity et ralentissez le tout pour prefers-reduced-motion.
  • Classic Spinner, SVG Morphing Loaders, Bouncing Dots et Card Skeleton sont gratuits avec leur code complet ; les autres sont inclus dans Premium.

Classic Spinner

Cercle qui tourne, simple et efficace.

CSS Minimal
Page de l’effet →
Voir le code source
HTML
<div class="demo-preview">
  <div class="loader-spinner"></div>
</div>
CSS
.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
  box-sizing: border-box
}

.demo-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 250px;
  overflow: hidden;
  position: relative;
  background: rgb(26, 26, 36);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.loader-spinner {
  width: 50px;
  height: 50px;
  border: 4px solid rgba(99, 102, 241, 0.2);
  border-top-color: #6366f1;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
JavaScript
/* CSS pur — aucun JavaScript requis */
Blob
Goutte
Étoile

SVG Morphing Loaders

Trois indicateurs organiques dont la forme se morphe en boucle fluide (interpolation de path SVG native). Zéro JavaScript, repli statique en prefers-reduced-motion.

💡 Idéal pour : états de chargement, attente IA, splash screens, boutons de traitement.

SVG SMIL Zero-JS
Page de l’effet →
Voir le code source
HTML
<div class="demo-preview">
  <div class="sml-row">
    <div class="sml-item"><svg class="sml-anim" width="54" height="54" viewBox="0 0 54 54" aria-hidden="true">
        <path fill="url(#smlGradA)">
          <animate attributeName="d" dur="2.2s" repeatCount="indefinite" calcMode="spline"
            keyTimes="0;.5;1" keySplines=".4 0 .2 1;.4 0 .2 1"
            values="M27 5 C41 5 49 13 49 27 C49 41 41 49 27 49 C13 49 5 41 5 27 C5 13 13 5 27 5 Z;M27 8 C44 3 51 16 46 29 C42 44 30 52 18 46 C6 41 2 26 9 16 C14 8 20 10 27 8 Z;M27 5 C41 5 49 13 49 27 C49 41 41 49 27 49 C13 49 5 41 5 27 C5 13 13 5 27 5 Z"/>
        </path>
      </svg>
      <svg class="sml-static" width="54" height="54" viewBox="0 0 54 54" aria-hidden="true"><path fill="url(#smlGradA)" d="M27 5 C41 5 49 13 49 27 C49 41 41 49 27 49 C13 49 5 41 5 27 C5 13 13 5 27 5 Z"/></svg><span class="sml-label">Blob</span></div>
    <div class="sml-item"><svg class="sml-anim" width="54" height="54" viewBox="0 0 54 54" aria-hidden="true">
        <path fill="#5a9bff">
          <animate attributeName="d" dur="1.6s" repeatCount="indefinite" calcMode="spline"
            keyTimes="0;.5;1" keySplines=".4 0 .2 1;.4 0 .2 1"
            values="M27 6 C34 16 44 24 44 33 C44 43 36 49 27 49 C18 49 10 43 10 33 C10 24 20 16 27 6 Z;M27 10 C38 10 46 18 46 28 C46 39 38 46 27 46 C16 46 8 39 8 28 C8 18 16 10 27 10 Z;M27 6 C34 16 44 24 44 33 C44 43 36 49 27 49 C18 49 10 43 10 33 C10 24 20 16 27 6 Z"/>
        </path>
      </svg>
      <svg class="sml-static" width="54" height="54" viewBox="0 0 54 54" aria-hidden="true"><path fill="#5a9bff" d="M27 6 C34 16 44 24 44 33 C44 43 36 49 27 49 C18 49 10 43 10 33 C10 24 20 16 27 6 Z"/></svg><span class="sml-label">Goutte</span></div>
    <div class="sml-item"><svg class="sml-anim" width="54" height="54" viewBox="0 0 54 54" aria-hidden="true">
        <path fill="#ffd166">
          <animate attributeName="d" dur="1.9s" repeatCount="indefinite" calcMode="spline"
            keyTimes="0;.5;1" keySplines=".4 0 .2 1;.4 0 .2 1"
            values="M27 6 L33 20 L48 21 L37 31 L41 46 L27 38 L13 46 L17 31 L6 21 L21 20 Z;M27 12 L31 22 L42 23 L34 30 L36 41 L27 36 L18 41 L20 30 L12 23 L23 22 Z;M27 6 L33 20 L48 21 L37 31 L41 46 L27 38 L13 46 L17 31 L6 21 L21 20 Z"/>
        </path>
      </svg>
      <svg class="sml-static" width="54" height="54" viewBox="0 0 54 54" aria-hidden="true"><path fill="#ffd166" d="M27 6 L33 20 L48 21 L37 31 L41 46 L27 38 L13 46 L17 31 L6 21 L21 20 Z"/></svg><span class="sml-label">Étoile</span></div>
    <svg width="0" height="0" aria-hidden="true"><defs><linearGradient id="smlGradA" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#7c3aed"/><stop offset="1" stop-color="#db2777"/></linearGradient></defs></svg>
  </div>
</div>
CSS
.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
  box-sizing: border-box
}

.demo-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 250px;
  overflow: hidden;
  position: relative;
  background: rgb(26, 26, 36);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.sml-row {
  display: flex;
  gap: 34px;
  align-items: center;
  justify-content: center
}

.sml-item {
  display: grid;
  place-items: center;
  gap: 8px
}

.sml-item svg {
  display: block
}

.sml-label {
  font-size: .62rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #8f8fb0
}

.sml-item .sml-static {
  display: none
}

@media (prefers-reduced-motion: reduce) {
  .sml-item .sml-anim {
    display: none
  }

  .sml-item .sml-static {
    display: block
  }
}
JavaScript
/* CSS pur — aucun JavaScript requis */

Bouncing Dots

Trois points qui rebondissent en cascade.

CSS Playful
Page de l’effet →
Voir le code source
HTML
<div class="demo-preview">
  <div class="loader-dots">
    <span></span>
    <span></span>
    <span></span>
  </div>
</div>
CSS
.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
  box-sizing: border-box
}

.demo-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 250px;
  overflow: hidden;
  position: relative;
  background: rgb(26, 26, 36);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.loader-dots {
  display: flex;
  gap: 8px;
}

.loader-dots span {
  width: 12px;
  height: 12px;
  background: #6366f1;
  border-radius: 50%;
  animation: dotBounce 1.4s ease-in-out infinite;
}

.loader-dots span:nth-child(1) {
  animation-delay: -0.32s;
}

.loader-dots span:nth-child(2) {
  animation-delay: -0.16s;
}

@keyframes dotBounce {

  0%,
  80%,
  100% {
    transform: scale(0);
  }

  40% {
    transform: scale(1);
  }
}
JavaScript
/* CSS pur — aucun JavaScript requis */

Pulse Ring

Anneaux qui pulsent en expansion.

CSS Radar

Progress Bar

Barre de progression indéterminée.

CSS Linear

Cube Flip

Cube 3D qui se retourne.

CSS 3D

Wave Bars

Barres en forme d'onde audio.

CSS Audio

Orbit

Point qui orbite autour d'un centre.

CSS Circular

Skeleton Screen

Placeholder shimmer pour contenu.

CSS Content

DNA Helix

Double hélice en rotation 3D.

CSS 3D

Cube Grid

Grille 3x3 de cubes en sequence.

CSS Grid

Morphing Shape

Forme qui se transforme en rotation.

CSS Transform

Circular Progress

Cercle SVG qui se remplit progressivement.

SVG Progress

Bouncing Squares

Carres rebondissants en cascade.

CSS Playful

Text Loading

Texte avec points animés.

CSS Text

Gradient Ring

Anneau gradient tournant.

CSS Gradient

Heartbeat

Effet battement de coeur.

CSS Pulse

Infinity Loop

Boucle infinie animée.

CSS Loop

Glitch Text

Effet glitch rétro sur texte.

CSS Glitch

Card Skeleton

Placeholder de carte classique avec avatar, titre et lignes de texte animées par un shimmer glissant.

CSS Shimmer Card
Page de l’effet →
Voir le code source
HTML
<div class="demo-preview">
  <div class="card-skel">
    <div class="card-skel-header">
      <div class="skel-bone-circle card-skel-avatar"></div>
      <div style="flex:1">
        <div class="skel-bone card-skel-title"></div>
        <div class="skel-bone card-skel-subtitle"></div>
      </div>
    </div>
    <div class="skel-bone card-skel-img"></div>
    <div class="skel-bone card-skel-line"></div>
    <div class="skel-bone card-skel-line"></div>
    <div class="skel-bone card-skel-line"></div>
  </div>
</div>
CSS
.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
  box-sizing: border-box
}

.demo-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 250px;
  overflow: hidden;
  position: relative;
  background: rgb(26, 26, 36);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.skel-bone {
  background: rgba(255, 255, 255, 0.08);
  border-radius: 6px;
  position: relative;
  overflow: hidden;
}

.skel-bone-circle {
  background: rgba(255, 255, 255, 0.08);
  border-radius: 50%;
  position: relative;
  overflow: hidden;
}

.skel-bone::after,
.skel-bone-circle::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
      transparent 0%,
      rgba(255, 255, 255, 0.1) 50%,
      transparent 100%);
  background-size: 200% 100%;
  animation: shimmerSlide 1.8s ease-in-out infinite;
}

.card-skel {
  width: 280px;
  padding: 20px;
  background: rgba(255, 255, 255, 0.03);
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.card-skel-header {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 20px;
}

.card-skel-avatar {
  width: 48px;
  height: 48px;
  flex-shrink: 0;
}

.card-skel-title {
  height: 14px;
  width: 65%;
  margin-bottom: 8px;
}

.card-skel-subtitle {
  height: 10px;
  width: 45%;
}

.card-skel-img {
  height: 120px;
  width: 100%;
  margin-bottom: 16px;
  border-radius: 10px;
}

.card-skel-line {
  height: 10px;
  width: 100%;
  margin-bottom: 10px;
}

.card-skel-line:nth-child(2) {
  width: 92%;
}

.card-skel-line:last-child {
  width: 60%;
  margin-bottom: 0;
}

@keyframes shimmerSlide {
  0% {
    background-position: -200% 0;
  }

  100% {
    background-position: 200% 0;
  }
}
JavaScript
/* CSS pur — aucun JavaScript requis */

List Skeleton

Liste d'éléments avec avatars et lignes de texte en chargement progressif.

CSS Shimmer Liste

Image Grid Skeleton

Grille 2x2 de placeholders d'images avec shimmer décale pour un effet cascade.

CSS Grid Images

Profile Skeleton

Page profil en chargement : grand avatar, nom, bio et rangée de statistiques.

CSS Shimmer Profil

Article Skeleton

Squelette d'article de blog : image hero, titre, paragraphes et informations auteur.

CSS Shimmer Blog

Dashboard Skeleton

Mini tableau de bord avec cartes de stats, zone graphique et lignes de tableau en chargement.

CSS Shimmer Dashboard

Pulse Loading

Même structure de carte mais avec une animation pulse/respiration au lieu du shimmer classique.

CSS Pulse Card

Wave Shimmer

Shimmer ondulant qui coule comme de l'eau, avec un mouvement fluide et organique.

CSS Wave Organique

Rainbow Shimmer

Squelette avec un shimmer dégradé arc-en-ciel coloré qui balaye les éléments.

CSS Rainbow Gradient

Skeleton to Content

Cliquez pour basculer entre l'état squelette et le contenu réel avec une transition fluide.

CSS JS Transition

Questions fréquentes

Comment créer un spinner CSS sans JavaScript ? +

Utilisez un div avec border-radius:50%, une bordure transparente sauf un côté, et animation: spin 1s linear infinite. L'animation @keyframes spin fait une rotation de 0 à 360deg. C'est le loader le plus léger possible.

Quel est le meilleur loader pour un site web ? +

Utilisez un skeleton screen pour les contenus longs (cartes, listes) et un spinner simple pour les actions courtes (bouton submit). Le skeleton donne une impression de rapidité car il montre la structure de la page.

Comment animer un skeleton screen en CSS ? +

Créez des rectangles gris avec border-radius qui miment la structure du contenu. Ajoutez un gradient animé (shimmer) avec background: linear-gradient et animation sur background-position.

Les loaders CSS sont-ils performants ? +

Oui. Les animations CSS utilisant transform et opacity sont traitées par le GPU. Un spinner CSS consomme moins de ressources qu'un GIF ou qu'un SVG animé. Évitez d'animer box-shadow ou width qui déclenchent des repaints.

Comment centrer un loader sur la page ? +

Utilisez display:flex, align-items:center et justify-content:center sur le conteneur. Pour un overlay plein écran, ajoutez position:fixed, inset:0, z-index:9999 et un background semi-transparent.