Corriger « Layout shift culprits » dans Lighthouse et PageSpeed
Ce qui a bougé pendant le chargement de la page, comment vérifier l'élément que Lighthouse met en cause et comment corriger les décalages causés par les images, les polices, les annonces, les bannières et le CSS tardif.
Pourquoi « Layout shift culprits » est rouge dans PageSpeed Insights ?
« Layout shift culprits » passe au rouge quand les décalages de mise en page pendant le chargement atteignent un CLS cumulé de 0,1 ou plus : « Des décalages de mise en page se produisent lorsque des éléments se déplacent sans aucune interaction de l'utilisateur. » Lighthouse appelait auparavant cet audit « Avoid large layout shifts ». Il liste ce qui a bougé et en déduit la cause. Réservez de l'espace pour ce qui arrive tard : width et height sur les images, hauteur sur les encarts publicitaires.
Vous cherchez la solution rapide ? Passez directement aux images, aux polices web, aux annonces et intégrations ou aux bannières.
Comment lire le tableau « Layout shift culprits » ?
Lighthouse enregistre un chargement de la page sur écran mobile et regroupe ses décalages de mise en page par rafales (les décalages espacés de moins d'une seconde), avec un tableau par rafale. L'avertissement est rouge quand la pire rafale atteint 0,1 ou plus, et gris en dessous. Visez 0 même quand il est gris : un décalage inférieur à 0,1 déplace tout de même le texte que lit le visiteur.
Sur une page de recette montée comme un blog culinaire WordPress, avec un titre en police web, un plugin de newsletter, une annonce sous le header et un plugin de fiche recette, l'avertissement était rouge avec une seule rafale à 0,241.

L'avertissement sur la page de recette dans Lighthouse 13.5, mobile, bridage simulé comme dans PageSpeed Insights.
- Total est le score de la rafale ; le Total de la pire rafale correspond au CLS rapporté par Lighthouse.
- Element désigne ce qui a le plus bougé lors de ce décalage, souvent un grand conteneur comme
<main>. Ce qui l'a poussé se trouve généralement au-dessus. - Layout shift score est la part de ce décalage dans le Total.
- La ligne sous une entrée indique la cause supposée par Lighthouse : « Unsized image element » avec l'image, ou « Web font » avec le fichier de police. Une entrée peut comporter les deux, ou aucun des deux.
Sur la page de recette, Lighthouse a désigné la mauvaise image sous trois des quatre décalages : vérifiez quel élément a réellement bougé avant de modifier quoi que ce soit.
Pourquoi la page saute-t-elle pendant son chargement ?
Le visiteur commence à lire et le texte glisse, ou il appuie sur un lien et touche ce qui s'est déplacé à sa place.
Le navigateur affiche la page avec ce qu'il a. Tout ce qui arrive plus tard sans espace réservé repousse le contenu situé en dessous : une image sans dimensions commence avec une hauteur nulle, une police web rend le titre plus large que le fallback. Chaque déplacement constitue un décalage de mise en page, mesuré selon la surface d'écran déplacée et la distance parcourue. Les décalages espacés de moins d'une seconde forment une rafale, et le CLS correspond au score de la pire rafale ; de petits décalages rapprochés s'additionnent donc.
Sur la page de recette, chaque cause reste sous les 0,1 prise isolément, et les quatre surviennent dans une même rafale :
| Cause sur la page de recette | CLS isolé | Après correction |
|---|---|---|
| Police web du titre | 0,035 | 0 |
| Photo de la recette sans width et height | 0,036 | 0 |
| Barre de newsletter au-dessus du header | 0,058 | 0 |
| Annonce dans un emplacement vide | 0,054 | 0 |
| Les quatre cumulées | 0,241 | 0 |
Le total des quatre dépasse la simple somme des lignes, car sur la page complète, la police et la photo déplacent chacune une plus grande surface d'écran. Corriger ces quatre causes a fait passer le score de performance mobile de 87 à 100, avec un FCP, un LCP et un Total Blocking Time inchangés. Le CLS représente un quart de ce score, mais Google classe la page selon le CLS constaté chez les visiteurs réels.

Avant, premier affichage

Avant, à 2 secondes

Après, premier affichage

Après, à 2 secondes
La page de recette au format mobile de Lighthouse. Le cadre en pointillés indique où se trouvait la photo au premier affichage.
Lighthouse a-t-il ciblé le bon élément ?
Lighthouse déduit la cause selon le timing : une police web dont le téléchargement s'est achevé juste avant le décalage, une image que le navigateur a affichée juste après. Une image qui n'a fait que subir la poussée peut donc se retrouver listée comme « Unsized image element ». L'image mise en avant sur la page de recette possède pourtant width="1200" height="675", mais Lighthouse la cite tout de même sous les décalages de la police, de la barre de newsletter et de l'annonce. Pour une ligne sans indication en dessous, commencez à l'étape 2.
- L'image désignée a-t-elle des dimensions ? L'audit « Image elements do not have explicit width and height », sous Diagnostics dans le même rapport, liste celles qui en manquent. Dans les DevTools, l'onglet Computed de l'image affiche un ratio pour aspect-ratio (auto 1200 / 675) quand son espace est réservé, et simplement auto quand il ne l'est pas. Une image dimensionnée n'est pas la cause du décalage, sauf si le thème applique width: auto (Unsized image element plus bas) : examinez l'autre mention sous la ligne, souvent « Web font », ou passez à l'étape 2.
- Qu'est-ce qui est apparu à cet instant ? Enregistrez un rechargement dans le panneau Performance. Chaque décalage correspond à un losange violet sur la piste Layout shifts ; survolez-en un pour voir son score et une capture d'écran de cet instant avec la zone déplacée mise en évidence. Sur la page de recette, le décalage de 0,0583 montre la barre verte de newsletter qui venait d'apparaître en haut. Pour les décalages survenant au scroll, cliquez sur Record, scrollez et cliquez sur Stop.
- Bloquez l'élément suspect et rechargez. Dans le panneau Network, faites un clic droit sur la requête suspecte (comme le fichier de police ou le script publicitaire), choisissez Block requests, puis Block request URL, et relancez un enregistrement. Le losange qui disparaît correspondait à cette requête : bloquer la police sur la page de recette a supprimé un décalage et laissé les trois autres. Décochez ensuite le blocage dans le panneau Request conditions.

Étape 2 sur la page de recette : Chrome 155, Slow 4G et ralentissement CPU 4×.
Sans les DevTools, la fonctionnalité cRUM de CoreDash effectue cette vérification : elle juxtapose une trace Chrome de la page et les données réelles des visiteurs, nomme l'élément qui a bougé et indique ce que le visiteur a fait juste avant. La capture ci-dessous provient d'une autre page : commencez par Chrome trace et Real users.

CoreDash cRUM pour une page sur acme-pagespeed.com, mobile.
Quels éléments se décalent pour les visiteurs réels ?
Lighthouse charge la page une seule fois, comme un nouvel arrivant, sans jamais faire défiler ni appuyer sur l'écran. Vos field data enregistrent chaque visite réelle, y compris l'annonce au milieu d'un article qui se charge quand le visiteur arrive dessus et le header qui rétrécit au défilement. Quand Lighthouse est au vert ou gris et que la Search Console indique un CLS médiocre, les décalages ont généralement lieu après le chargement.
Dans CoreDash, réglez l'appareil sur mobile et groupez le CLS par Attribution Element (CLS) pour voir quels éléments ont bougé ; Last interaction (CLS), sous More dimensions, indique si cela s'est produit après un scroll. Pour trouver ce qui a repoussé ces éléments, enregistrez la page dans le panneau Performance pendant que vous scrollez (étape 2 de la vérification ci-dessus).

CoreDash sur mobile, p75 : commencez par le CLS le plus élevé.
Comment corriger « Layout shift culprits » ?
Commencez par le plus grand décalage dans le tableau ayant le Total le plus élevé, une fois sa cause confirmée par la vérification ci-dessus.
Unsized image element
Une image sans dimensions commence avec une hauteur nulle et repousse tout vers le bas au moment de son chargement. WordPress ajoute un width et un height aux images issues de la médiathèque ; les images générées directement par un thème, un constructeur de page, un slider ou un plugin en manquent souvent, comme la photo de la fiche recette. Si vous ne pouvez pas modifier le thème, WP Rocket ajoute les dimensions manquantes (Media, Add missing image dimensions) ; dans un template, écrivez-les vous-même :
<img src="shakshuka.jpg" width="800" height="600" alt="Shakshuka in the pan">
img { max-width: 100%; height: auto; } Le navigateur convertit ces deux attributs en un ratio et réserve la bonne hauteur pour n'importe quelle largeur d'écran avant l'arrivée du fichier. Un width: auto dans le CSS du thème annule ce ratio (auto sizing images). Sur Shopify, le filtre image_tag ajoute les dimensions ; un thème ou une application qui génère sa propre balise <img> a besoin de width="{{ image.width }}" height="{{ image.height }}". Dans Next.js, next/image nécessite width et height, ou fill à l'intérieur d'un parent dimensionné.
Web font
Le titre s'affiche d'abord avec la police de fallback, puis avec la police web, qui est plus large ou plus étroite : les lignes sautent sur d'autres mots et tout ce qui se trouve en dessous bouge. Sur WordPress (CSS additionnel) et Shopify (la feuille de style du thème sous Modifier le code ; attention, une mise à jour du thème peut l'écraser), vous ajoutez une font-face de fallback qui ajuste une police locale aux proportions de la police web. Dans Next.js, next/font intègre directement un fallback adapté. Voici la police du titre sur la page de recette :
@font-face {
font-family: "Young Serif Fallback";
src: local("Times New Roman"), local("Liberation Serif");
size-adjust: 131%;
ascent-override: 79.85%;
descent-override: 27.94%;
line-gap-override: 0%;
}
h1 { font-family: "Young Serif", "Young Serif Fallback", serif; } Un size-adjust de 122 % issu d'un outil de calcul laissait encore un CLS de 0,029 ; 131 % a donné 0. L'article sur font-display fournit la police pour Android, la marche à suivre pour obtenir vos propres valeurs et font-display: optional, qui supprime le décalage en servant le fallback aux nouveaux visiteurs.
Annonces, vidéos et autres intégrations
Un script publicitaire insère son iframe dans l'emplacement une fois l'enchère terminée ; si l'emplacement était vide, tout ce qui se trouve en dessous chute de la hauteur de l'annonce. D'après le State of Web Vitals, 89 % des sites sans régie publicitaire valident le CLS sur mobile, contre 73 % des sites utilisant deux régies ou plus. C'est une corrélation.
Dans le thème ou le plugin publicitaire, donnez au conteneur de l'annonce la hauteur du format le plus fréquent, et passez l'iframe en bloc :
.ad-slot { min-height: 50px; }
.ad-slot iframe { display: block; } Une iframe repose par défaut sur la ligne de texte : l'emplacement réservé sur la page de recette s'agrandissait donc de 8 pixels jusqu'à ce que l'iframe soit passée en bloc. Laissez l'emplacement ouvert lorsqu'il n'y a pas d'annonce : le fermer déplace la page tout autant que le remplir. Les annonces automatiques AdSense se placent là où Google le décide : excluez les zones du premier écran (Paramètres des annonces, Zones exclues) et placez-y un bloc d'annonces manuel dans un emplacement dimensionné.
YouTube, Google Maps et les autres intégrations sont également des iframes, et sur une iframe, les attributs width et height ne définissent aucun ratio : avec height: auto, elle se réduit à environ 150 pixels. Donnez un aspect-ratio à l'iframe, ou utilisez une façade, à savoir une miniature dimensionnée qui se transforme en iframe au clic (YouTube, Google Maps) :
.video-embed iframe { width: 100%; height: auto; aspect-ratio: 16 / 9; } Barres et bannières injectées par un script
Un bandeau de newsletter ou une barre de livraison gratuite qu'un script insère au-dessus du header pousse toute la page vers le bas.
Commencez par vérifier les options de disposition du plugin ou de l'application : une barre en bas d'écran, un encart ou une pop-up flottent au-dessus de la page sans rien décaler. Les outils de consentement comme OneTrust et CookieYes proposent les mêmes formats ; Lighthouse voit leur bannière à chaque exécution car il démarre sans cookies. Une barre qui doit obligatoirement se placer au-dessus du header doit recevoir son espace dans le thème, à la hauteur indiquée par l'onglet Computed à la largeur mobile :
<div id="promo-slot" style="min-height: 48px"></div> Pour une barre qui ne s'affiche qu'à certaines visites, laissez-la flotter ou effectuez son rendu dans le HTML sur le serveur. Le contenu visible provenant d'une balise Google Tag Manager arrive toujours après le premier affichage : intégrez-le dans le template. Sur Shopify, une application qui injecte un widget (comme des étoiles d'avis ou une barre d'annonce) nécessite une hauteur sur le conteneur accueillant son bloc d'application.
CSS chargé après le premier affichage
Une feuille de style chargée avec l'astuce du media="print", ou différée ou chargée de manière asynchrone par un plugin d'optimisation, arrive une fois la page affichée à l'écran et redimensionne les éléments déjà en place : un header qui s'agrandit repousse tout ce qui se trouve en dessous. Dans le plugin, excluez-la de l'option qui retarde le CSS ou le charge de manière asynchrone ; dans le thème, chargez-la avec une simple balise <link rel="stylesheet"> dans le head.
Animations, sliders et blocs générés en JavaScript
Ils proviennent généralement du thème, d'un plugin ou d'une application : cherchez-y une option. À défaut, transmettez à un développeur l'élément mentionné dans le rapport ainsi que le code ci-dessous.
Une animation portant sur top, left, height ou margin modifie la mise en page à chaque image. Lighthouse la répertorie sous forme de plusieurs petites lignes sans cause indiquée, et l'audit « Avoid non-composited animations » sous Diagnostics nomme l'élément. Animez plutôt transform : cela déplace les pixels de l'élément sans toucher au reste, si bien que le navigateur ne compte aucun décalage de mise en page.
.promo { position: fixed; top: 0; left: 0; right: 0;
transform: translateY(-100%); transition: transform .3s; }
.promo.is-open { transform: translateY(0); } Un slider affiche ses diapositives empilées tant que son script ne s'est pas exécuté ; limitez l'affichage à la première diapositive jusqu'à ce que le script ajoute sa classe de statut prêt (utilisez celle propre à votre slider) :
.slider:not(.is-ready) > :not(:first-child) { display: none; } Un bloc que JavaScript génère après le chargement, comme des avis ou des produits recommandés, a besoin d'un conteneur vide fixé à sa hauteur finale, ou d'un rendu côté serveur. Pour les animations au défilement, consultez les animations déclenchées au scroll et les transitions CSS.
Décalages causés par du code non modifiable
Vous ne pouvez pas modifier ce qu'insère un visuel publicitaire, une application sans réglage de disposition ou le script d'un tiers. Réservez la hauteur qu'il occupe habituellement, placez-le sous le premier écran, ou remplacez-le par une solution qui ne déborde pas de son espace.
Ces modifications ont-elles ramené le CLS à 0 ?
Vérifiez le correctif dans Lighthouse sur mobile, après avoir purgé votre plugin de cache et le CDN. Une fois chaque décalage supprimé, « Layout shift culprits » passe dans les audits réussis et le CLS tombe à 0. Un enregistrement dans le panneau Performance, effectué une fois au rechargement et une fois pendant le défilement, présente une piste Layout shifts vide. CoreDash affiche le nouveau CLS par élément dès les visites suivantes ; Search Console met jusqu'à 28 jours à se mettre à jour.

Avant

Après
Avant et après les quatre correctifs : Lighthouse 13.5, mobile, bridage simulé.
Certains décalages viennent d'endroits inaccessibles : un encart rempli par votre régie publicitaire, une application qui injecte son widget, un décalage que seuls les visiteurs réels constatent en scrollant. Lors d'une Core Web Vitals power hour, nous les passons en revue dans vos field data et dans un enregistrement de la page, puis nous déterminons la hauteur à attribuer à chaque conteneur.

Besoin d'un site plus rapide ?
Je suis Arjen et je répare des sites lents depuis 17 ans. Si vous rencontrez des problèmes que vous ne pouvez pas résoudre vous-même, envoyez-moi votre URL et parlons-en.
Envoyez-moi votre URL
Pourquoi notre fix ne fonctionne pas ?
Parfois le CDN sert encore l'ancien HTML, ou le fix a été appliqué sur le template desktop alors que le mobile en utilise un autre. Envoyez-moi votre URL et le problème, et je vous réponds avec quelques créneaux. J'analyse d'abord vos field data, puis nous passons une heure sur votre site, et sous 24 heures vous avez mes notes par écrit. 350 € HT.
Envoyez-moi votre URLPas de développeur pour réaliser le fix ? Je fais le travail moi-même, sur une copie de votre site.