Points clés
- Le CLS sur Shopify mesure les décalages inattendus de mise en page (layout shift). Chaque décalage a un score basé sur la fraction d’impact multipliée par la fraction de distance, et votre CLS correspond à la pire série de décalages sur la page, et non au total.
- Les seuils de Google sont de 0,1 ou moins pour “Bon” et supérieurs à 0,25 pour “Médiocre”, mesurés au 75e centile des chargements réels des pages.
- Le CLS en labo (Lab data) semble souvent correct alors que CrUX le juge médiocre, car un test en labo ne capture que les décalages lors de son propre chargement sur un seul viewport fixe.
- La plupart des problèmes de CLS sur Shopify proviennent d’une courte liste : images sans dimensions, web fonts (polices web), barres d’annonce, headers sticky et widgets injectés par des apps au-dessus de la ligne de flottaison.
- CrUX est une moyenne mobile sur 28 jours, un correctif déployé prend donc jusqu’à quatre semaines pour se refléter complètement dans les données de terrain (field data) de PageSpeed Insights.
Le CLS sur Shopify est la métrique qui vous pénalise pour le contenu qui bouge après le début de rendu de la page. L’acheteur s’apprête à cliquer sur le bouton Ajouter au panier, un widget d’avis se charge juste au-dessus, et le clic atterrit sur autre chose.
Ce guide explique comment le score est réellement calculé, comment identifier l’élément qui cause le décalage au lieu de deviner, et les causes spécifiques à Shopify qu’il faut vérifier en premier. La partie diagnostic s’applique à n’importe quel site. Les correctifs, eux, se font au niveau du thème et de Liquid.
Pourquoi vous pouvez nous faire confiance
Nous travaillons dans l’écosystème Shopify depuis plus de quatre ans et avons accompagné des centaines de marques Shopify sur leurs vitrines. Jacques a plus de 15 ans d’expérience en développement. Nous développons Fudge, un éditeur de vitrine IA avec une note de 5,0 sur le Shopify App Store et le statut Built for Shopify. Nous travaillons donc sur la même couche de thème là où les décalages de mise en page (layout shift) commencent.
Que mesure le CLS et comment le score est-il calculé ?
Le CLS mesure la plus grande rafale de décalages de mise en page inattendus (layout shift) qui se produit tout au long du cycle de vie d’une page.1
Deux mots de cette définition ont de l’importance.
« Inattendus ». Un décalage qui se produit dans les 500 millisecondes suivant une action utilisateur discrète, comme un tap, un clic ou l’appui sur une touche, reçoit l’indicateur hadRecentInput et est exclu.1 Le scroll, le glisser-déposer et le pincement pour zoomer ne comptent pas comme des entrées récentes, donc un décalage pendant le scroll impacte toujours le score.
« Plus grande rafale ». Le CLS n’est pas la somme de tous les décalages de la page. Les décalages sont regroupés en fenêtres de session (session windows) : une rafale de décalages espacés de moins d’une seconde chacun, avec une fenêtre totale maximale de cinq secondes.1 Votre CLS correspond à la fenêtre avec le score le plus élevé.
Fraction d’impact et fraction de distance
Chaque décalage individuel donne un score :
score de layout shift = fraction d'impact x fraction de distance
La fraction d’impact est la zone visible combinée de chaque élément instable entre l’image (frame) actuelle et la précédente, en tant que fraction de la surface du viewport.1
La fraction de distance est la plus grande distance parcourue par un élément instable dans cette frame, divisée par la plus grande dimension du viewport.1
La conséquence pratique : un petit élément qui se déplace beaucoup et un grand élément qui se déplace peu peuvent avoir le même score. Un hero en pleine largeur repoussé de 100px vers le bas sur mobile coûte cher, car la fraction d’impact est proche de 1.
Quel est un bon score CLS en 2026 ?
| Score | Évaluation |
|---|---|
| 0,1 ou moins | Bon |
| Plus de 0,1 à 0,25 | À améliorer |
| Plus de 0,25 | Médiocre |
Les seuils sont évalués au 75e centile des chargements de page, segmentés séparément pour mobile et desktop.1 Votre visiteur médian peut avoir une expérience parfaite alors que vous échouez quand même, car c’est le quart des chargements les plus lents qui détermine la note.
Google indique que les Core Web Vitals sont alignés avec ce que ses systèmes de classement récompensent, et fixe un CLS de 0,1 comme cible.2 C’est un critère de classement parmi d’autres, pas une formule magique. Pour avoir une vue d’ensemble du niveau actuel des boutiques Shopify, consultez notre rapport sur l’état des performances Shopify (en anglais).
Pourquoi le CLS en Lab semble bon alors que CrUX est mauvais ?
C’est la confusion la plus courante, et ce n’est un bug dans aucun des deux outils.
Les données Lab représentent un seul chargement synthétique : un appareil émulé, un viewport, un cache vide, pas de scroll, pas d’interaction. Lighthouse signale les décalages qu’il observe pendant cette trace, puis s’arrête.
Les données terrain (Field data) proviennent de CrUX, agrégées à partir d’utilisateurs réels de Chrome tout au long du cycle de vie de la page. Elles incluent le décalage qui se produit quand quelqu’un fait défiler la page jusqu’aux avis, change une variante, ou attend sur une connexion lente qu’un widget d’application se charge.
Quatre différences expliquent ce décalage :
- Le timing. Sur une connexion rapide en laboratoire, un script tardif arrive avant le first paint et ne décale rien. Sur une vraie 4G, il arrive après.
- Les interactions. Les tests Lab ne scrollent jamais, donc une page de collection qui saute lors du chargement différé (lazy-load) semble parfaite.
- La taille du viewport. Une bannière qui passe sur deux lignes à 360px de large va décaler tout ce qui se trouve en dessous.
- La géographie. Les convertisseurs de devises et les bannières géolocalisées ne se déclenchent que pour certains visiteurs.
Si le Lab indique 0 et le terrain 0,3, fiez-vous aux données terrain et partez à la recherche des décalages liés aux interactions et au scroll.
Comment diagnostiquer le CLS sur une boutique Shopify
Commencez par analyser les données de terrain (field data), puis reproduisez le problème en local.
1. Lisez les données de terrain. Analysez l’URL en direct sur PageSpeed Insights et lisez la section CrUX en haut, et non le score Lighthouse en dessous. Vérifiez le mobile et le desktop, et testez séparément une page produit, une page collection et la page d’accueil. Le rapport Core Web Vitals de la Search Console regroupe les URL similaires, ce qui vous indique si le problème concerne un seul template ou tout le thème.
2. Enregistrez une trace. Dans le panneau Performance des Chrome DevTools, limitez le réseau à une connexion lente (throttling) et le CPU à 4x, puis enregistrez un rechargement. La piste Layout Shifts va se remplir de barres violettes. Cliquez sur un décalage individuel pour voir l’élément qui a bougé ainsi qu’une capture d’écran avant/après. L’insight “Layout shift culprits” met en évidence le pire cluster et propose une estimation de la cause.
3. Activez l’overlay. Dans DevTools, ouvrez Settings, puis More tools, puis Rendering, et cochez Layout Shift Regions. Les zones qui se décalent clignoteront en violet à chaque mouvement.3 C’est le moyen le plus rapide de repérer les petits décalages qui vous échapperaient sinon.
4. Traquez les décalages liés aux interactions. Les tests en labo ne les trouveront pas, alors générez-les manuellement avec la vue Live metrics du panneau Performance ouverte : scrollez toute la page, changez les variantes de produit, ouvrez le tiroir du panier, changez la devise, appliquez un filtre sur une page de collection. Regardez le chiffre CLS grimper et notez ce que vous faisiez à ce moment-là.
5. Instrumentez sur le terrain. Pour les décalages que vous n’arrivez pas à reproduire, loggez les vrais. L’API Layout Instability vous donne les nœuds responsables dans entry.sources :
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
console.log(
entry.value,
entry.sources.map((s) => s.node),
)
}
}
}).observe({type: 'layout-shift', buffered: true})
Chaque source contient previousRect et currentRect, vous pouvez donc voir exactement de combien de pixels un nœud s’est déplacé. Notez que l’élément qui a bougé est généralement la victime, pas le coupable. Le coupable est l’élément qui est apparu au-dessus.
Causes de CLS spécifiques à Shopify et leurs correctifs
| Cause | Correctif |
|---|---|
| Images ou vidéos sans dimensions | Attributs width et height, ou aspect-ratio sur le wrapper |
| Swap de Web font | font-display, preload du fichier de police, fallback avec métriques ajustées |
| Barre d’annonce ou livraison gratuite | Rendu en Liquid côté serveur, ou réserver une min-height |
| Bannière de cookies | Overlay en fixed en dehors du flux du document |
| Widgets d’apps (avis, devises, upsell) | App blocks avec un conteneur qui réserve une min-height |
| Contenu lazy-loadé above the fold | loading="eager" et fetchpriority="high" sur le hero |
| Header sticky | position: sticky, ou un spacer correspondant à la hauteur du header |
| Changement de variante | aspect-ratio fixe sur le wrapper du média produit |
| Scroll infini | Placeholders de type skeleton, ou un bouton Voir plus (Load more) |
| CSS de section arrivant tardivement | Charger le CSS above the fold de manière synchrone |
Images et vidéos sans dimensions
Définissez width et height pour que le navigateur réserve l’espace de la boîte avant même l’arrivée du fichier, puis laissez le CSS gérer le responsive :
<img src="hero.jpg" width="1600" height="900" alt="Collection Automne" />
img {
width: 100%;
height: auto;
}
En Liquid, le filtre image_tag génère les attributs width et height pour vous, calculés à partir de l’image source :
{{ product.featured_image | image_tag: widths: '400, 800, 1200', sizes: '(min-width: 750px) 50vw, 100vw' }}
Pour les vidéos, les intégrations (embeds), et tout ce qui n’a pas de taille intrinsèque, réservez l’espace directement sur le wrapper :
.video-wrapper {
aspect-ratio: 16 / 9;
}
Polices web et décalage lié au swap
Une police personnalisée se charge, remplace le fallback (police de secours), et chaque ligne de texte est recalculée. Si les deux polices ont des métriques différentes, le bloc change de hauteur et repousse le contenu vers le bas.
Trois correctifs, par ordre de difficulté :
Définissez font-display. Le filtre font_face de Shopify accepte directement ce paramètre :
{{ settings.type_body_font | font_face: font_display: 'swap' }}
swap affiche immédiatement la police de secours puis fait l’échange, donc le texte est toujours visible mais le décalage se produit quand même. optional évite complètement le décalage en refusant de faire le swap si la police met du temps à charger, au prix de certains visiteurs qui ne verront jamais la police de votre marque.
Préchargez le fichier (preload) pour que l’échange ait lieu avant le premier affichage (first paint) plutôt qu’après :
<link rel="preload" href="{{ settings.type_body_font | font_url }}" as="font" type="font/woff2" crossorigin>
Ajustez les métriques du fallback. Cela supprime le décalage sans renoncer à la police. Déclarez un fallback local avec des overrides ajustés sur votre web font, puis ajoutez-le dans la pile CSS :
@font-face {
font-family: 'Body Fallback';
src: local('Arial');
size-adjust: 103%;
ascent-override: 92%;
descent-override: 24%;
line-gap-override: 0%;
}
body {
font-family: 'Your Font', 'Body Fallback', sans-serif;
}
Les pourcentages sont spécifiques à chaque paire de polices. Mesurez-les plutôt que de copier ceux-ci.
Barres d’annonce, bannières de cookies et barres de livraison
Tout ce qui est injecté au-dessus de la ligne de flottaison repousse la page entière vers le bas, ce qui signifie une fraction d’impact proche de 1.
Générez-le en Liquid. Une barre qui arrive directement dans le HTML rendu par le serveur ne décale jamais rien, car elle était là dès la première frame. C’est le bon correctif dans presque tous les cas.
S’il faut que ce soit en JavaScript, réservez la hauteur avant le rendu :
.announcement-bar {
min-height: 40px;
}
Définissez la même valeur sur le placeholder pour chaque breakpoint où le texte passe sur une deuxième ligne. Une réservation de 40px pour une barre qui fait 72px sur un écran de 360px créera toujours un décalage.
Les bannières de cookies doivent être hors du flux. Utilisez position: fixed pour que la bannière se superpose au contenu au lieu de le déplacer.
Contenus injectés par des applications
Les widgets d’avis, convertisseurs de devises, blocs d’upsell, guides des tailles et bulles de chat se chargent tous tardivement et s’étendent dans la mise en page.
Privilégiez les app blocks (blocs d’application) ajoutés via l’éditeur de thème. Ils sont rendus à l’intérieur de la section, dans le flux du document, lors du rendu côté serveur. Les anciens widgets injectés par script arrivent après le paint et décalent tout ce qui se trouve en dessous.
Si l’application injecte toujours ses éléments tardivement, réservez l’espace du conteneur :
.shopify-app-block {
min-height: 24px;
}
.product-reviews-placeholder {
min-height: 180px;
}
Les bulles de chat doivent être en position: fixed, jamais dans le flux. Pour un guide étape par étape sur la façon d’isoler l’application responsable, consultez notre guide sur la résolution des problèmes de mise en page après l’installation d’apps Shopify.
Contenu above the fold chargé en lazy-load
Ajouter loading="lazy" sur une image hero (bannière) est une erreur qui provoque un décalage auto-infligé. Le navigateur retarde la récupération, l’espace reste vide pendant la fenêtre critique, et le contenu se met en place en retard.
Assurez-vous que le premier viewport bénéficie d’un chargement eager et d’une priorité élevée :
<img src="hero.jpg" width="1600" height="900" loading="eager" fetchpriority="high" alt="Hero" />
Tous les détails se trouvent dans notre guide pour corriger le chargement eager de la bannière hero Shopify.
Le même principe s’applique aux sections révélées par JavaScript. Les animations de fade-in qui animent height, top ou margin sont comptabilisées comme des layout shifts à chaque image. Animez plutôt transform et opacity, que le compositeur (compositor) gère sans reflow.
Sticky headers
Un header qui passe en position: fixed au défilement quitte le flux du document, et la page en dessous remonte d’une valeur égale à la hauteur du header.
Utilisez plutôt position: sticky, ce qui garde l’espace de l’élément réservé. Si le design exige d’être en fixed, insérez un élément espaceur de la même hauteur au moment où la classe bascule, et gardez les deux synchronisés avec une propriété CSS personnalisée.
Changement de variante sur la page produit
Il y a ici deux sources de décalage.
Des dimensions d’image différentes. Remplacer une image carrée par une variante en format portrait modifie la hauteur du bloc média. Verrouillez le wrapper :
.product-media {
aspect-ratio: 1 / 1;
}
.product-media img {
width: 100%;
height: 100%;
object-fit: contain;
}
Remplacements via la Section Rendering API. Re-rendre le formulaire produit lors d’un changement de variante remplace le balisage par un balisage de hauteur différente, généralement parce qu’un badge promo, une alerte de stock ou un sélecteur d’abonnement s’affiche pour une variante et pas pour une autre. Réservez une min-height sur la zone qui va être remplacée.
Ces décalages surviennent dans les 500ms après un clic, ils sont donc souvent exclus du calcul du CLS. Mais ils nuisent toujours à l’ergonomie, et tout reflow en cascade qui arriverait plus tard n’est pas exclu du calcul.
Scroll infini sur les pages de collection
L’ajout de produits à la suite repousse le footer et tout ce qui est actuellement visible. Réservez cet espace avec des placeholders de type skeleton dimensionnés sur les vraies lignes de la grille, ou utilisez un bouton “Voir plus” (Load more) pour que le décalage soit initié par l’utilisateur.
Pour les longues grilles, contenir la taille (size containment) aide le navigateur à bien anticiper les lignes hors écran :
.product-grid__item {
content-visibility: auto;
contain-intrinsic-size: auto 420px;
}
CSS qui arrive en retard
Les feuilles de style des sections chargées de manière asynchrone redessinent un contenu qui a déjà été affiché. Chargez le CSS des sections above the fold normalement et différez uniquement ce qui se trouve en dessous. Notre guide sur les scripts bloquant le rendu sur Shopify vous indique où se situe la limite.
Comment Fudge intervient
La plupart des correctifs ci-dessus sont de petites modifications spécifiques du Liquid, du CSS et du JavaScript du thème. Fudge écrit ces changements sous forme de code natif dans le thème, plutôt que de superposer une couche de rendu supplémentaire par-dessus votre boutique.
Demandez une hauteur réservée sur la barre d’annonce ou un ratio verrouillé sur le conteneur du média produit, et la modification est directement intégrée aux fichiers du thème. Pas de script supplémentaire, pas de widget, ce qui a son importance quand la métrique que vous essayez d’améliorer est causée par des scripts et des widgets en trop. Il en va de même pour tout le reste construit avec l’éditeur de boutique Shopify : supprimez Fudge et le code reste.
Comment vérifier le correctif et quand le CrUX se met à jour
La vérification se fait en trois étapes, et la dernière est lente.
Immédiatement : réenregistrez. Tracez à nouveau la page dans l’onglet Performance avec la même limitation réseau. La ligne Layout Shifts devrait être vide ou presque. Répétez l’opération avec les Layout Shift Regions activées, en faisant défiler toute la page et en changeant les variantes.
Sous 24h : vérifiez en Lab. Testez l’URL en direct sur PageSpeed Insights. Le CLS Lab confirme que les décalages liés au temps de chargement ont disparu, mais ne dit rien sur les décalages d’interaction, donc ne vous arrêtez pas là.
Sur 28 jours : observez le terrain. CrUX est une moyenne mobile sur 28 jours, mise à jour quotidiennement vers 04:00 UTC.4 Le lendemain du déploiement de votre correctif, 27 des 28 jours contiennent toujours la page avec le problème. L’amélioration apparaît progressivement et n’atteint sa vraie valeur qu’environ quatre semaines après que le correctif est en ligne pour tout le trafic.
Deux points trompent souvent les gens ici. Le correctif doit être publié sur le thème en ligne (live), car CrUX n’observe que les vrais visiteurs. Et une URL a besoin d’assez de trafic pour être signalée, de sorte que les pages peu fréquentées peuvent ne montrer que des données au niveau de l’origine du site.
Une fois le CLS réglé, la même boucle s’applique au reste des Web Vitals. Notre guide expliquant comment accélérer un thème Shopify couvre l’aspect du temps de chargement.
FAQ
0,1 ou moins est un bon score, plus de 0,1 à 0,25 indique qu'il faut des améliorations, et au-delà de 0,25, c'est médiocre. Google évalue cela au 75e centile des chargements de page réels, segmentés séparément pour le mobile et l'ordinateur. Cela signifie qu'un quart de vos visiteurs peuvent dépasser le seuil et faire baisser votre note, c'est pourquoi le mobile est généralement le chiffre à prioriser.
Un test en labo (Lab data) correspond à un seul chargement synthétique sur un appareil émulé, sans scrolling ni interaction. Il ne voit donc que les décalages qui se produisent pendant ce test. Les données de terrain CrUX proviennent de vrais utilisateurs de Chrome sur tout le cycle de vie de la page, y compris les décalages déclenchés par le scroll, le changement de variante, et les scripts d'applications qui arrivent en retard sur des connexions lentes. Lorsque les deux scores ne sont pas d'accord, fiez-vous aux données de terrain et partez à la recherche des décalages causés par les interactions et le scroll.
Fréquemment. Les widgets d'avis, convertisseurs de devises, blocs d'upsell et bulles de chat injectent souvent du contenu après le premier affichage (paint) de la page, ce qui repousse vers le bas tout ce qui se trouve en dessous. Les app blocks ajoutés via l'éditeur de thème sont plus sûrs car ils sont rendus dans le flux du document lors du rendu côté serveur. Pour les widgets injectés par script, réservez l'espace avec un min-height sur le conteneur.
Enregistrez une trace dans le panneau Performance des Chrome DevTools avec le throttling activé, puis cliquez sur chaque décalage dans la piste Layout Shifts pour voir l'élément ainsi qu'une capture avant/après. Activer l'option Layout Shift Regions dans l'onglet Rendering fait clignoter en violet les zones qui bougent en temps réel. N'oubliez pas que l'élément qui a bougé est généralement la victime, et que le vrai coupable est l'élément apparu au-dessus.
CrUX est une moyenne mobile sur 28 jours mise à jour quotidiennement, l'amélioration apparaît donc progressivement et non du jour au lendemain. Attendez-vous à un délai d'environ quatre semaines après le déploiement de la correction sur le thème publié pour que le score de terrain (field data) reflète sa vraie valeur. Les outils de labo confirment le correctif immédiatement, mais ils ne couvrent que les décalages liés au chargement.
Oui, lorsqu'elle est injectée via JavaScript au lieu d'être rendue en Liquid, car elle apparaît au-dessus de tout le reste et repousse toute la page vers le bas. Rendre la barre côté serveur (via Liquid) dans le thème élimine totalement le décalage. Si elle doit rester dynamique, fixez un min-height sur son conteneur qui correspond à la hauteur rendue pour chaque breakpoint, y compris ceux où le texte passe sur deux lignes.
Google précise que les Core Web Vitals sont alignés avec les signaux de classement qu'ils récompensent, et fixe comme cible un CLS inférieur à 0,1. C'est un signal parmi beaucoup d'autres, et non un signal décisif. L'argument commercial principal reste souvent l'expérience utilisateur : une page qui bouge sous le doigt de l'acheteur génère des erreurs de clics, par exemple sur le bouton Ajouter au panier.
Footnotes
-
web.dev, “Cumulative Layout Shift (CLS)” - définition, formule fraction d’impact fois fraction de distance, la pause d’une seconde et la fenêtre de session maximale de cinq secondes, l’exclusion
hadRecentInputde 500 ms, et les seuils de 0,1 / 0,25 au 75e centile. https://web.dev/articles/cls ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Google Search Central, “Understanding Core Web Vitals and Google search results” - indique que l’expérience sur la page correspond à ce que les systèmes de classement récompensent, et donne 0,1 comme cible CLS. https://developers.google.com/search/docs/appearance/core-web-vitals ↩
-
Chrome for Developers, “Discover issues with rendering performance” - l’option Layout Shift Regions de l’onglet Rendering met brièvement en évidence les zones qui se décalent en violet. https://developer.chrome.com/docs/devtools/rendering/performance ↩
-
Chrome for Developers, “CrUX API” - le rapport Chrome UX est une moyenne mobile sur 28 jours de métriques agrégées, mise à jour quotidiennement autour de 04h00 UTC. https://developer.chrome.com/docs/crux/api ↩