Vous avez déjà vécu la scène : vous commencez à lire un article, votre doigt s’approche d’un bouton, et au moment de cliquer, tout se décale. Le texte saute, le bouton a bougé, et vous venez d’appuyer sur une publicité. Ce phénomène porte un nom : le décalage de mise en page, ou layout shift. Et Google le mesure avec une métrique dédiée, le Cumulative Layout Shift (CLS), troisième pilier des Core Web Vitals aux côtés du LCP et de l’INP.
Contrairement à ses deux cousins, le CLS ne mesure pas une durée mais une instabilité visuelle. C’est la métrique la plus contre-intuitive du trio, celle dont le mode de calcul est le moins bien compris, et paradoxalement celle dont les corrections sont souvent les plus rapides à mettre en œuvre. Dans cet article, nous décomposons le CLS comme le font nos experts en audit de web performance : le calcul exact du score, les cinq grandes familles de causes, et les corrections concrètes, code à l’appui, pour passer durablement sous la barre des 0,1.
Le CLS, qu’est-ce que c’est exactement ?
Le Cumulative Layout Shift mesure l’instabilité visuelle d’une page en combinant deux informations : quelle proportion du contenu visible s’est déplacée, et sur quelle distance. La définition officielle est donnée par web.dev : le CLS est la plus grande somme de scores de décalage observée sur une fenêtre de temps donnée, pendant toute la durée de vie de la page.
Deux précisions importantes découlent de cette définition. D’abord, le CLS est un nombre sans unité. Là où le LCP s’exprime en secondes et l’INP en millisecondes, le CLS est un score abstrait : 0 signifie une page parfaitement stable, et plus le score grimpe, plus la page bouge sous les yeux de l’utilisateur.
Ensuite, seuls les décalages inattendus comptent. Un déplacement de contenu qui survient dans les 500 millisecondes suivant une interaction de l’utilisateur (un clic, une saisie clavier) est considéré comme voulu et exclu du score. Ouvrir un menu accordéon qui pousse le contenu vers le bas ne pénalise donc pas votre CLS. En revanche, un décalage qui survient au-delà de ce délai de grâce, par exemple après un appel réseau un peu lent, redevient comptabilisé, même s’il découle indirectement d’une action de l’utilisateur.
Comment le score d’un décalage se calcule
Chaque décalage individuel reçoit un score qui est le produit de deux fractions, détaillées dans la documentation de la métrique :
- la fraction d’impact (impact fraction) : la part du viewport occupée par l’union des zones qu’un élément instable couvrait avant et après son déplacement ;
- la fraction de distance (distance fraction) : la distance parcourue par l’élément, rapportée à la plus grande dimension du viewport.
Prenons un exemple concret. Un bloc de texte occupe la moitié de l’écran. Une image s’insère au-dessus de lui et le pousse vers le bas de 25 % de la hauteur du viewport. La zone concernée par le mouvement (position initiale plus position finale du texte) couvre 75 % du viewport : la fraction d’impact vaut 0,75. La distance parcourue vaut 0,25. Le score de ce décalage est donc 0,75 × 0,25 = 0,1875. Un seul événement de ce type suffit à faire basculer la page dans la zone « à améliorer ».
La fraction de distance n’a pas toujours existé : elle a été introduite pour éviter de sur-pénaliser les gros éléments qui ne bougent que de quelques pixels. Ce détail illustre une propriété essentielle du CLS : un petit décalage d’un grand élément et un grand décalage d’un petit élément ne se valent pas, la métrique cherche à refléter la gêne perçue, pas un simple mouvement mécanique.
Des fenêtres de session plutôt qu’un cumul infini
Le « C » de CLS a longtemps été trompeur. À l’origine, tous les décalages étaient additionnés sur toute la durée de vie de la page : une page consultée dix minutes accumulait mécaniquement plus de décalages qu’une page fermée en trente secondes, ce qui pénalisait injustement les pages à longue durée de vie comme les fils d’actualité ou les applications monopages. Google a corrigé ce biais en 2021, comme l’explique l’article Evolving the CLS metric.
Depuis, les décalages sont regroupés en fenêtres de session : une fenêtre démarre au premier décalage, s’étend tant que les décalages s’enchaînent à moins d’une seconde d’intervalle, et se referme au bout de cinq secondes maximum. Chaque fenêtre additionne les scores des décalages qu’elle contient, et le CLS retenu est celui de la pire fenêtre. Une conséquence pratique : une page peut subir des décalages à plusieurs moments de sa vie (au chargement, puis au scroll), et c’est le pire épisode, pas la somme de tous, qui fait foi.
Les seuils à retenir
Pour offrir une bonne expérience, Google fixe l’objectif à un CLS inférieur ou égal à 0,1. Entre 0,1 et 0,25, la page est classée « à améliorer » ; au-delà de 0,25, elle est jugée mauvaise. Comme pour les autres Core Web Vitals, l’évaluation se fait au 75e centile des visites réelles : il ne suffit pas que la page soit stable sur votre machine de développement, il faut qu’elle le soit pour au moins trois visiteurs sur quatre, mobiles compris.
Pourquoi le CLS mérite toute votre attention
Le CLS est probablement la métrique la plus directement liée à la frustration utilisateur. Un LCP médiocre se traduit par de l’attente ; un CLS médiocre se traduit par des erreurs : clic sur le mauvais lien, ajout au panier involontaire, perte du fil de lecture. Sur un site e-commerce, un bouton « Valider la commande » qui se dérobe sous le doigt au profit d’un bandeau promotionnel est un irritant majeur, avec des conséquences mesurables sur la conversion.
S’ajoute l’enjeu SEO : le CLS fait partie des signaux d’expérience de page pris en compte par Google, au même titre que les deux autres Core Web Vitals. Nous avons détaillé ce mécanisme dans notre article sur l’impact SEO de la webperf : la performance ne fait pas tout, mais à contenu comparable, elle départage.
Enfin, le CLS a une propriété précieuse pour prioriser un chantier de performance : ses causes sont peu nombreuses, bien identifiées, et la plupart des corrections sont des ajustements de HTML et de CSS à faible risque, là où un chantier LCP ou INP touche souvent à l’architecture même du site.
Les grandes causes de décalage et comment les corriger
La documentation officielle d’optimisation du CLS identifie quatre grandes familles de causes : les images sans dimensions, les publicités et iframes sans dimensions, le contenu injecté dynamiquement, et les polices web. Nous y ajoutons une cinquième, régulièrement rencontrée en audit : les animations CSS mal choisies. Passons-les en revue, avec les corrections associées.
Les images et médias sans dimensions
C’est historiquement la première cause de CLS sur le web. Sans attributs width et height, le navigateur ignore les proportions d’une image avant de l’avoir téléchargée : il réserve une hauteur nulle, affiche le texte, puis repousse tout vers le bas quand l’image arrive. Le remède existe depuis fin 2019 : lorsque les deux attributs sont présents, le navigateur en déduit le ratio d’affichage et réserve l’espace exact avant même le premier octet de l’image, comme l’explique en détail Jake Archibald (source secondaire, ingénieur Chrome à l’époque de la publication).
<!-- Le ratio 16/9 est réservé avant téléchargement -->
<img src="/img/hero.avif" width="1600" height="900"
alt="Description pertinente">
Ces valeurs n’imposent pas une taille d’affichage fixe : le CSS responsive habituel reste maître des dimensions finales, le ratio étant conservé par le navigateur.
img {
max-width: 100%;
height: auto; /* le ratio issu de width/height est conservé */
}
Pour les images responsives en srcset, il suffit que toutes les variantes partagent le même ratio. Pour la direction artistique en <picture>, les navigateurs modernes acceptent désormais width et height sur chaque <source>, ce qui permet des ratios différents entre mobile et desktop sans décalage. Et pour les images de fond CSS, qui n’ont pas d’équivalent, la propriété aspect-ratio joue le même rôle de réservation d’espace. Nous consacrons un article complet à l’optimisation des images, dont les dimensions explicites ne sont qu’un volet.
Les publicités, iframes et contenus embarqués
Les publicités sont l’un des premiers contributeurs au CLS à l’échelle du web. Les régies servent des formats de taille variable dans des emplacements dont la hauteur n’est pas connue à l’avance : quand la créa arrive, tout le contenu situé dessous descend d’un cran. Même mécanique pour les vidéos YouTube, les cartes Google Maps ou les publications de réseaux sociaux embarquées : le widget ne connaît pas sa propre taille avant de s’être chargé, et la plateforme ne réserve pas l’espace pour vous, comme le rappellent les bonnes pratiques dédiées aux embeds.
La parade est toujours la même : réserver l’espace dans la mise en page initiale, avant l’arrivée du contenu tiers. Une règle min-height calée sur le format le plus probable, ou un aspect-ratio pour les contenus proportionnels, suffit dans la plupart des cas.
/* Emplacement publicitaire : hauteur du format le plus probable */
.ad-slot {
min-height: 250px;
}
/* Iframe video : l'espace est réservé au bon ratio */
.video-embed iframe {
width: 100%;
aspect-ratio: 16 / 9;
}
Deux subtilités méritent d’être soulignées. D’une part, si aucune publicité n’est finalement servie, il ne faut pas replier l’emplacement : retirer un espace réservé provoque exactement le même décalage que d’en insérer un. Mieux vaut y afficher un contenu de substitution. D’autre part, la position compte : un bloc injecté en haut du viewport déplace tout ce qui le suit, alors que le même bloc en bas de page ne déplace presque rien. Placer les contenus tardifs le plus bas possible est une optimisation gratuite.
Le contenu injecté dynamiquement : bandeaux, CMP et scroll infini
Bandeaux de consentement, barres de promotion, encarts d’inscription à la newsletter : tout ce qui s’insère dans le flux de la page après le rendu initial pousse le contenu existant. Le cas du bandeau cookies est emblématique, au point que web.dev lui consacre un guide de bonnes pratiques : la solution consiste soit à réserver l’espace du bandeau dès le premier rendu, soit à le superposer au contenu en position: fixed, hors du flux, pour qu’il ne déplace rien. Notre comparatif de 11 CMP montre à quel point les solutions du marché sont inégales sur ce point.
Le scroll infini est un autre piège classique : si les nouveaux éléments se chargent sans espace réservé pendant que l’utilisateur fait défiler, chaque lot injecté produit son décalage, y compris longtemps après le chargement initial. Addy Osmani (ingénieur Chrome) détaille dans Infinite scroll without layout shifts (source secondaire) le pattern correct : squelettes de réservation dimensionnés à l’avance et insertion hors écran. Plus généralement, pour tout contenu chargé à la demande, trois stratégies évitent le décalage : remplacer l’ancien contenu dans un conteneur de taille fixe, faire déclencher le chargement par l’utilisateur (le décalage sous 500 millisecondes est alors exclu du score), ou charger hors écran et notifier.
Les polices d’écriture
Quatrième grande cause, plus sournoise : les webfonts. Pendant le téléchargement d’une police personnalisée, le navigateur affiche soit un texte invisible (FOIT), soit une police de substitution (FOUT). Dans les deux cas, quand la police définitive arrive, le texte est redessiné : si la police de substitution n’occupe pas exactement le même espace (chasse, hauteur de ligne), les blocs de texte changent de hauteur et tout le bas de page se décale. Notre article sur l’impact des polices sur la performance couvre le sujet en profondeur ; concentrons-nous ici sur le volet CLS.
Le levier le plus efficace est d’ajuster la police de substitution pour qu’elle mime la police finale, grâce aux descripteurs size-adjust, ascent-override, descent-override et line-gap-override, introduits par Chrome et documentés dans Improved font fallbacks :
/* Fallback dimensionnee pour mimer Poppins */
@font-face {
font-family: "Poppins-fallback";
src: local("Arial");
size-adjust: 112%;
ascent-override: 93%;
descent-override: 31%;
}
body {
font-family: Poppins, "Poppins-fallback", Arial, sans-serif;
}
Avec une police de fallback bien calibrée, le basculement devient quasiment invisible et le CLS lié aux textes tombe à zéro. Complétez avec font-display: swap pour que le texte soit lisible immédiatement, un préchargement des fichiers critiques via <link rel="preload">, et, pour les cas où la stabilité prime sur le rendu exact, font-display: optional qui renonce au basculement si la police n’est pas prête au premier rendu, comme le recommande le guide des bonnes pratiques polices.
Calculer ces descripteurs à la main est fastidieux : c’est précisément pour cela que nous avons développé No More Font Shift, notre outil de génération de fontes de fallback optimisées. Vous téléversez votre webfont, l’outil vous aide à calculer les valeurs de size-adjust et des overrides, puis produit un CSS prêt à l’emploi, avec Arial et Roboto en polices de repli pour les sans-serif, de loin les plus courantes (des piles Times New Roman et Roboto Serif ou Impact et Roboto Condensed couvrent les polices serif et condensées). Un simulateur de chargement intégré permet même de comparer visuellement, avec et sans fallback ajustée, le décalage réellement subi par vos visiteurs selon la vitesse de leur connexion.
Les animations CSS mal choisies
Animer les propriétés top, left, width ou margin déclenche des recalculs de mise en page à chaque frame, et ces déplacements comptent comme des décalages. La règle, détaillée dans le guide High-performance animations, est simple : tout mouvement doit passer par transform, qui opère à l’étape de composition, n’affecte pas la position des autres éléments et n’entre pas dans le calcul du CLS.
/* A eviter : declenche layout + layout shift */
.banner { animation: slide-bad 300ms ease-out; }
@keyframes slide-bad {
from { top: -80px; }
to { top: 0; }
}
/* A privilegier : composition seule, aucun CLS */
.banner { animation: slide-good 300ms ease-out; }
@keyframes slide-good {
from { transform: translateY(-80px); }
to { transform: translateY(0); }
}
Dans la même veine, méfiez-vous des éléments positionnés en position: absolute montés tardivement par JavaScript, et des carrousels d’ancienne génération qui recalculent leurs dimensions au chargement : nous avons documenté ce dernier cas dans notre article sur les sliders.
Votre site est-il aussi rapide que vos visiteurs l’espèrent ?
Comment mesurer et diagnostiquer le CLS ?
Avant de corriger, encore faut-il mesurer au bon endroit. Le CLS est la métrique où l’écart entre outils de laboratoire et données de terrain est le plus fréquent, et le plus déroutant pour les équipes.
Données de laboratoire contre données terrain
Lighthouse mesure le CLS pendant un simple chargement de page, sans interaction ni défilement. Le Chrome UX Report (CrUX), lui, mesure le CLS sur toute la durée de vie de la page chez de vrais utilisateurs : scroll, interactions, contenus chargés à la demande. Un score Lighthouse de 0 avec un CrUX à 0,25 n’est donc pas une anomalie : cela signifie que vos décalages surviennent après le chargement, typiquement au scroll sur du contenu lazy-loadé ou lors des transitions de vues d’une application monopage.
C’est un cas que nous rencontrons systématiquement en audit de SPA, où les changements de route au-delà des 500 millisecondes de grâce alimentent le CLS de terrain, invisibles en laboratoire. PageSpeed Insights affiche les deux jeux de données côte à côte, et leur divergence est en soi un signal de diagnostic, comme nous l’expliquions dans PageSpeed Insights vs Lighthouse.
Traquer les décalages dans les DevTools
Le panneau Performance de Chrome DevTools dispose d’une piste « Layout shifts » qui regroupe les décalages en grappes : chaque losange est un décalage individuel, sa taille est proportionnelle à son score, et un clic rejoue l’animation du décalage en surlignant les éléments déplacés. La vue « métriques en direct » du même panneau permet d’interagir avec la page en surveillant le score en temps réel, idéal pour reproduire un décalage post-chargement.
Le guide Debug layout shifts détaille la méthode complète. Attention à une subtilité : les outils listent les éléments déplacés, qui ne sont pas toujours les éléments fautifs. Un bloc inséré en haut de page fait apparaître dans les rapports tous les éléments situés dessous ; la cause est au-dessus, dans l’élément inséré.
Mesurer en continu sur vos vrais utilisateurs
Pour aller au-delà du diagnostic ponctuel, la librairie web-vitals de Google expose une version « attribution » qui identifie, pour chaque session réelle, le pire élément décalé et le moment du décalage :
import { onCLS } from "web-vitals/attribution";
onCLS((metric) => {
console.log("CLS :", metric.value);
// L'element le plus decale de la pire fenetre de session
console.log("Cible :", metric.attribution.largestShiftTarget);
navigator.sendBeacon("/analytics", JSON.stringify(metric));
});
Branchée sur un outil de RUM, cette instrumentation transforme le CLS d’un score abstrait en une liste d’éléments coupables, triée par fréquence. C’est exactement l’approche que nous industrialisons dans notre prestation de monitoring de la web performance : sans mesure continue, une régression de CLS introduite par une mise à jour de thème ou une nouvelle régie publicitaire peut passer inaperçue des semaines. Un point de vigilance signalé par la documentation CrUX : les décalages survenant à l’intérieur d’iframes sont comptés par CrUX mais invisibles pour les API JavaScript, votre RUM peut donc légèrement sous-estimer le score officiel.
Le bfcache, l’allié méconnu du CLS
Un levier souvent ignoré ne corrige aucun décalage mais améliore le score global : le cache de navigation avant/arrière (bfcache). Quand un utilisateur revient sur une page via le bouton Précédent, le navigateur peut la restaurer instantanément depuis la mémoire, dans l’état exact où elle a été quittée : aucun rechargement, donc aucun décalage de chargement.
Ces navigations étant très fréquentes (retour aux résultats de recherche, à une page catégorie), leur CLS proche de zéro tire la distribution de terrain vers le bas : lors du déploiement de cette prise en compte, Chrome a observé des améliorations notables du CLS dans CrUX. Encore faut-il que vos pages soient éligibles : un gestionnaire d’événement unload ou certains en-têtes suffisent à priver tout le site de ce bonus. L’éligibilité au bfcache fait partie des points que nous contrôlons dans chaque mission d’optimisation.
Ce qu’il faut retenir
Le CLS est un score sans unité qui mesure la pire fenêtre de décalages inattendus sur toute la vie de la page, avec un objectif de 0,1 au 75e centile. Ses causes tiennent en cinq familles : images sans dimensions, contenus tiers sans espace réservé, injections dynamiques, polices mal substituées et animations déclenchant la mise en page. Le principe directeur de toutes les corrections est le même : dire au navigateur, dès le HTML et le CSS initiaux, la taille de ce qui n’est pas encore arrivé.
C’est aussi la métrique au meilleur ratio effort/résultat : des attributs width et height, quelques min-height, une fallback de police calibrée, et une page peut passer de 0,3 à moins de 0,05 en quelques jours de travail. Encore faut-il diagnostiquer les bons coupables, au bon endroit de la vie de la page. Si votre CLS de terrain résiste à vos corrections, nos experts peuvent l’analyser dans le cadre d’un audit de web performance complet, ou directement corriger votre site via notre prestation d’optimisation de la web performance.