Loaders, Spinners & Chargement
Loaders, spinners et skeleton loading pour une UX d'attente optimale.
Mis à jour le
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 quetransformetopacityet ralentissez le tout pourprefers-reduced-motion. - Classic Spinner, SVG Morphing Loaders, Bouncing Dots et Card Skeleton sont gratuits avec leur code complet ; les autres sont inclus dans Premium.
Voir le code source
<div class="demo-preview">
<div class="loader-spinner"></div>
</div>.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);
}
}/* CSS pur — aucun JavaScript requis */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.
Page de l’effet →Voir le code source
<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>.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
}
}/* CSS pur — aucun JavaScript requis */Voir le code source
<div class="demo-preview">
<div class="loader-dots">
<span></span>
<span></span>
<span></span>
</div>
</div>.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);
}
}/* CSS pur — aucun JavaScript requis */Card Skeleton
Placeholder de carte classique avec avatar, titre et lignes de texte animées par un shimmer glissant.
Page de l’effet →Voir le code source
<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>.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;
}
}/* CSS pur — aucun JavaScript requis */List Skeleton
Liste d'éléments avec avatars et lignes de texte en chargement progressif.
Page de l’effet →Image Grid Skeleton
Grille 2x2 de placeholders d'images avec shimmer décale pour un effet cascade.
Page de l’effet →Profile Skeleton
Page profil en chargement : grand avatar, nom, bio et rangée de statistiques.
Page de l’effet →Article Skeleton
Squelette d'article de blog : image hero, titre, paragraphes et informations auteur.
Page de l’effet →Dashboard Skeleton
Mini tableau de bord avec cartes de stats, zone graphique et lignes de tableau en chargement.
Page de l’effet →Pulse Loading
Même structure de carte mais avec une animation pulse/respiration au lieu du shimmer classique.
Page de l’effet →Wave Shimmer
Shimmer ondulant qui coule comme de l'eau, avec un mouvement fluide et organique.
Page de l’effet →Rainbow Shimmer
Squelette avec un shimmer dégradé arc-en-ciel coloré qui balaye les éléments.
Page de l’effet →Skeleton to Content
Cliquez pour basculer entre l'état squelette et le contenu réel avec une transition fluide.
Page de l’effet →