Avez-vous déjà commencé à lire un article d’actualité passionnant, puis perdu votre ligne parce que tout le texte s’est déplacé vers le bas ? Cela m’arrive souvent, surtout à cause des publicités qui se chargent au-dessus du contenu.
Ce type d’expérience utilisateur peut être frustrant, mais jusqu’à présent, nous avons eu du mal à la mesurer quantitativement. Le Cumulative Layout Shift relève ce défi.
Lorsque vous voyez une page web, votre cerveau décompose l’information visuelle en éléments. L’agencement de la page vous donne des informations sur leurs relations les uns par rapport aux autres. C’est comme ça que vous détectez le contenu pertinent. Si ce contenu se déplace tout seul, ou si un autre élément est injecté dans la page et déplace les autres, alors vous risquez de le perdre de vue.
Chargement de la page d’accueil de LeMonde.fr. Les contenus insérés et en mouvement sont identifiés par des zones bleu clair. Le contenu d’environ 2/3 de l’écran se déplace pendant le chargement de la page.
Pire encore, vous pouvez penser que la mise en page est stable et essayer d’interagir avec la page. Par exemple en cliquant sur un bouton (image de gauche). Si un autre contenu apparaît juste avant le clic et pousse l’autre contenu vers le bas (image de droite), vous risquez d’interagir avec le mauvais élément.
Plan
Dans cet article, nous introduirons l’API Layout Instability et verrons comment elle est utilisée pour calculer le Cumulative Layout Shift (CLS). Nous verrons comment visualiser le CLS dans Chrome et comment optimiser son site pour avoir un bon CLS. Enfin, nous aborderons les étranges caractéristiques du CLS et nous conclurons.
Une API dédiée dans le navigateur…
Depuis plusieurs années, les équipes Front tentent d’identifier les éléments qui se déplacent pendant le chargement et l’utilisation d’une page. Il y a dix ans, il fallait un moteur de rendu sur-mesure pour afficher tous les repaints (dans l’exemple ci-dessous, Gecko, le moteur de rendu de Firefox).
ATTENTION
Cette vidéo contient beaucoup de clignotements. Ne la regardez pas si vous êtes photosensible ou si vous souffrez de troubles vestibulaires.
Pour déterminer le moment où une page est chargée/stable, vous pouviez utiliser l’événement onload
, mais c’est loin d’être parfait. L’API Layout Instability de Chrome répond à cette limite. L’idée ici est de s’intéresser à d’autres signaux. Plus particulièrement, à la stabilité de la mise en page, ou « l’absence de sursauts d’interface ». Comme indiqué dans le résumé de l’API, elle « offre aux auteurs de pages web un aperçu de la stabilité de leurs pages, à partir des mouvements des éléments de la page ». Pour ce faire, l’API surveille les nœuds du DOM qui sont visibles dans le viewport. Si la représentation visuelle d’un nœud se décale, entre deux frames, de 3 pixels ou plus dans le sens horizontal ou vertical, l’API enregistre un layout-shif (un décalage) et lui attribue un score.
… utilisé pour pénaliser les saccades
Revenons à notre exemple :
Comment l’API Layout Instability attribue-t-elle un score à ce décalage ? Pour calculer le score de décalage (layout shift score), l’algorithme évalue d’abord la zone affectée par le décalage (impact fraction), et la multiplie par la longueur du décalage (distance fraction).
La zone affectée par le layout shift, ou impact fraction (fraction d’impact), est la zone totale occupée par le nœud qui se déplace, de sa première position à sa position finale. Ici, 78 % + 32 % = 110 %. Comme vous pouvez le voir, la fraction d’impact peut être supérieure à 100 % (la taille du viewport).
La longueur du décalage, ou distance fraction, est mesurée en pourcentage du plus grand côté du viewport. Dans cet exemple, nous allons donc considérer la hauteur. L’élément existant se déplace d’une hauteur équivalente à 34 % de la hauteur du viewport.
Pour ce Layout Shift en particulier, le score de Layout Shift est donc :
score de Layout Shift = 110 % (impact fraction) × 34 % (distance fraction) = 0,374
Le Cumulative Layout Shift (CLS) est la somme des scores de (presque) tous les Layout Shifts.
La fenêtre de 500 millisecondes
Pour éviter que les sites web ne soient pénalisés pour les transitions ou les animations qui résultent de l’interaction de l’utilisateur avec l’interface, l’algorithme du CLS ignore les Layout Shifts pendant 500 ms après chaque interaction active de l’utilisateur avec le document (par exemple, un clic, une frappe de touche ou un tapotement), ou après tout événement qui modifie directement la taille du viewport.
D’après Google, pour offrir une bonne expérience, les sites devraient s’efforcer d’avoir un CLS inférieur à 0,1 pour la plupart (75 %) des utilisateurs. C’est une mauvaise nouvelle pour les développeurs de l’exemple ci-dessus !
N’oubliez pas que la fraction d’impact et la fraction de distance sont dépendantes des caractéristiques du viewport. Une modification des dimensions ou de l’orientation du viewport peuvent modifier la mise en page (en provoquant une réadaptation du contenu à l’espace disponible). Si cela se produit, pour un même Layout Shift, la fraction d’impact et la fraction de distance, et donc le Cumulative Layout Shift, n’auront plus les mêmes valeurs.
Comment visualiser les Layout Shifts dans Chrome
Les Layout Shifts peuvent être affichés dans Chrome :
- Dans les DevTools (les outils de développement de Chrome), ouvrez le menu de commande en appuyant sur Control + Shift + P ou Command + Shift + P (macOS)
- Commencez à taper « Rendering »
- Exécutez la commande « Show Rendering » qui vous est proposée
- Activez la case Layout Shift Regions. Quand vous interagissez avec la page, les Layout Shifts apparaissent désormais en bleu.
Attention
Cette fonctionnalité peut provoquer des clignotements. Si vous êtes photosensible ou si vous souffrez de troubles vestibulaires, faites attention.
Depuis Chrome 84, vous pouvez également visualiser les Layout Shifts et obtenir des informations sur leur attribution (allant même jusqu’à la liste des éléments qui ont été décalés) dans l’onglet Performance, sur la ligne « Experience ».
Comment optimiser votre Cumulative Layout Shift
Une page accumule des Layout Shifts tout au long de son utilisation. À chaque fois que de nouveaux éléments sont insérés visuellement sans que l’espace nécessaire à leur affichage n’ait été réservé en amont par le navigateur. Le plus souvent, il s’agira d’images, mais vous devez également faire attention aux publicités, aux polices de caractères web, aux contenus intégrés et aux contenus injectés suite à une interaction qui génèrent des temps d’attente de plus de 500 ms.
Images
Quand il reçoit le code HTML de votre page, le navigateur ne sait pas grand choses sur les images qui y sont utilisées. S’il ne dispose que de l’URL de l’image, le navigateur ne peut pas connaître sa taille. Il ne peut donc pas allouer l’espace nécessaire à l’affichage de cette image. Cette situation est devenue encore plus complexe depuis l’avènement du Responsive Web Design puisque désormais, la taille d’affichage d’une image dépend souvent du contexte.
Pour donner le plus d’informations possible au navigateur (afin qu’il puisse faire son travail au mieux) définissez toujours les valeurs des attributs width
et height
sur vos éléments <img>
(exprimées en pixels). Même si ce ne sont pas les dimensions réelles de l’affichage (qui peuvent dépendre des règles de style), le navigateur les utilisera pour déduire le rapport largeur / hauteur (appelé aspect-ratio
) de l’image, et ainsi calculer la hauteur à réserver en fonction de la largeur d’affichage définie par le CSS.
Le saviez-vous ?
Edge, Firefox et Chrome ont une représentation interne du aspect-ratio
des images sur lesquelles les attributs width
et height
sont définis. Une expérience est en cours sur Chrome pour ouvrir la fonctionnalité aux équipes de développement web.
.el {
aspect-ratio: 16 / 9;
}
/* internal rule for img, using the --webkit prefix (in Chrome)
img[width][height] {
--webkit-aspect-ratio: attr(width) / attr(height);
}*/
Pour en savoir plus sur aspect-ratio
lisez cet article de Chris Coyier sur CSS Tricks
Et bien sûr, n’oubliez pas de servir la bonne taille de fichier en fonction du contexte. Utilisez srcset
pour aider le navigateur à sélectionner la meilleure image de votre jeu d’images avec le même rapport hauteur/largeur (suivant la largeur du viewport). Utilisez picture
et source
pour gérer différents rapports hauteur/largeur ou différents formats d’image en fonction du contexte et des types supportés par le navigateur.
Publicités, intégrations, iframes et contenus créés dynamiquement
Comme pour les images, le navigateur doit allouer l’espace nécessaire pour afficher le contenu final. Mais vous ne pouvez pas donner beaucoup d’informations au navigateur, car souvent, vous ne disposez vous-même que de peu d’informations sur le futur contenu. Dans ce qui suit, je ne parlerai que du cas des publicités, car elles constituent un problème récurrent. Mais l’approche est la même pour les autres types de contenus injectés.
Dans la suite, nous examinerons l’impact des injections de contenu sur le contenu d’une page. Notez que le contenu des iframes n’est pas concerné : si une iframe produit de Layout Shifts, ceux-ci n’auront pas d’impact sur la page principale.
Le plus souvent, l’espace publicitaire est attribué à un service d’enchères qui injecte un conteneur publicitaire dans un nœud dédié du DOM. Ensuite, cet espace est « loué » au plus offrant, attribué à une régie qui injecte enfin une publicité. L’espace peut être redimensionné plusieurs fois avant que la publicité finale ne soit affichée, et dans le cas de publicités temporisées, des injections supplémentaires peuvent se produire plusieurs fois pendant la session de l’utilisateur.
Comment éviter que la mise en page soit modifiée ?
Définissez explicitement l’espace que vous souhaitez allouer au conteneur publicitaire. Prévoyez un habillage pour prendre en compte le cas dans lequel l’annonce serait plus petite que l’espace pré-réservé. Si l’annonce est plus grande que l’espace réservé, il faudra accepter qu’une partie de l’annonce ne soit pas affichée.
Il arrive que l’espace reste vide, si aucune publicité n’a été trouvée. Prévoyez des visuels correspondant à la taille de l’espace publicitaire afin de ne pas avoir à réduire l’espace publicitaire et à provoquer un changement de disposition. Si votre régie vous donne les moyens (par le biais d’un callback JS, par exemple) d’injecter du contenu utile en cas d’absence de publicité, bien sûr, n’hésitez pas.
Une autre façon de résoudre ce problème consiste à positionner les publicités dans l’espace négatif laissé par le contenu du site, en dehors du flux de pages. Si votre publicité a une position figée dans le gabarit de page, et que cette position n’interfère pas avec l’agencement du reste du contenu, alors l’insertion de la publicité ne provoque aucun décalage.
Enfin, vous pouvez décider de déplacer vos espaces publicitaires plus bas sur vos pages. Cela évitera que la fraction d’impact du Layout Shift ne soit trop importante, mais cela réduira probablement vos revenus.
Police d’écriture web
Lorsqu’un navigateur doit afficher du texte avec une police d’écriture web, il détermine si la police est nécessaire et disponible (via le système d’exploitation ou dans le cache navigateur). Si la police est nécessaire mais non disponible, le navigateur la récupère. Pendant la phase de récupération, le navigateur suit les instructions font-display
(ou leur absence) pour déterminer s’il doit afficher le texte avec une police de substitution disponible ou allouer l’espace avec du texte invisible. Une fois la police chargée, ou si le temps d’attente est trop long, le navigateur procède alors au rendu final.
En d’autres termes, chaque fois que le navigateur doit récupérer une police web, il attribue à la zone un texte avec une police par défaut, visible ou non. Et lorsque la police devient disponible, le navigateur effectue le rendu final de la zone de texte.
Problème : la police temporaire utilisée pour occuper l’espace n’a pas nécessairement pas nécessairement les mêmes caractéristiques que la police finale. Les lignes de base, la taille des points typographiques et les kernings peuvent être différents et le texte final peut apparaître plus court ou plus long que le texte temporaire, ce qui peut déplacer les contenus qui suivent dans le flux de page, et entraîner des Layout Shifts.
Chaque fois que j’ai besoin de me rappeler comment fonctionne la gestion de l’affichage des polices, je retourne voir le the font-display playground, de Monica Dinculescu.
Pour éviter ces Layout Shifts, vous pouvez accélérer le chargement des polices. ou choisir des polices web ayant des caractéristiques très proches des polices système. Vous pouvez également considérer la police web comme optionnelle lors du premier chargement de la page. Elle ne sera alors pas utilisée sur cette page, même si elle est téléchargée et mise en cache. En revanche, elle sera disponible pour la page suivante. À ce sujet, vous pouvez lire « eBay’s Font Loading Strategy » de Senthil Padmanabhan.
Contenu injecté plus de 500 ms après une interaction
L’algorithme du CLS ignore les Layout Shifts pendant 500 ms après chaque interaction active de l’utilisateur avec le document ou modification de la taille du viewport. Grâce à ce mécanisme, la plupart des interactions utilisateur ne provoquent pas de Layout Shifts comptabilisés dans le Cumulative Layout Shift.
Assurez-vous donc que votre interface réagira à l’interaction utilisateur dans la fenêtre de 500 ms. Si votre interface utilisateur repose sur des requêtes réseau ou un traitement javascript lourd, des décalages peuvent se produire après 500 ms et seront donc pris en compte dans la valeur du Cumulative Layout Shift.
Pour optimiser votre CLS :
- Assurez-vous que vos transitions et animations qui suivent une interaction utilisateur durent moins de 500 ms.
- Mesurez et optimisez les temps de réponse des vos API.
- Anticipez le résultat de l’appel (ou des appels) ou du long traitement en réservant l’espace associé au(x) résultat(s) durant les 500 ms qui suivent l’interaction, quitte à ce que cet espace soit rempli ultérieurement, à la manière de l’optimistic UI.
- Implémentez un paradigme d’interface utilisateur spécifique aux longs traitements et un appel réseau (mise en file d’attente et notification d’achèvement, affichage des résultats dans des pop-ins, etc.)
- N’animez que des propriétés CSS qui ne déclenchent pas de changements de mise en page dans Blink.
Bien sûr, testez toujours vos optimisations. En menant quelques expériences pour écrire cet article, j’ai découvert que lorsque la durée d’une transition translate
est de 0, des Layout Shifts apparaissent (un problème désormais résolu). Des cas-limites comme celui-ci existeront toujours. N’hésitez pas à les signaler à l’équipe Chromium.
Astuce: un traitement en deux étapes
Prenez l’exemple d’un bouton « Charger 20 articles supplémentaires » en fin de page. Si le traitement (appel réseau + rendu) prend moins de 500 ms, c’est parfait. Mais s’il prend plus de 500 ms, le rendu peut provoquer un décalage du pied de page et augmenter votre Cumulative Layout Shift.
Pour éviter ça, surveillez le temps écoulé et divisez la tâche en deux parties si nécessaire. Au lieu de faire un rendu systématique après l’appel réseau, vous pourrez plutôt changer le libellé du bouton en « Afficher les 20 éléments récupérés ». Cela obligera les utilisateurs à interagir à nouveau avec la page et vous accordera 500 millisecondes supplémentaires pour le rendu du contenu.
Bien sûr, n’hésitez pas à tester le ressensi des utilisateurs pour vérifier que l’utilisation de la fonctionnalité correspond mieux à leurs attentes. Optimiser le CLS ne doit pas se faire au détriment de l’UX.
Comportements contre-intuitifs
Augmentation continue dans le cas des Single-Page Applications (SPAs)
La plupart des algorithmes de calcul des indicateurs de performance web s’arrêtent à la fin du chargement de la page. L’évaluation du Cumulative Layout Shift, elle, se poursuit tout au long de l’utilisation de la page.
Beaucoup de sites web sont construits sur un paradigme de navigation de page à page, d’URL à URL. Pour eux, le CLS est réinitialisé à chaque changement de page. Certains sites, en revanche, sont construits autour d’une page unique dont le contenu évolue au fil des interactions. C’est ce qu’on appelle des Single-Page Applications (SPA).
Sur les SPA (y compris les applications qui utilisent des bibliothèques d’amélioration progressives comme turbolinks), le Cumulative Layout Shift augmente en continu pendant la session d’un utilisateur, puisque la navigation entre les contenus ne se fait pas en naviguant entre des pages. Le Cumulative Layout Shift n’est donc collecté que lorsque l’utilisateur quitte la page. Si la valeur du Cumulative Layout Shift que vous observez dans la Search Console (rubrique « Signaux Web essentiels ») vous semble élevée, c’est peut-être une explication.
Note
Ce comportement spécifique du Cumulative Layout Shift est particulièrement important. Pour les équipes développant des SPAs, l’objectif ne doit pas être de réduire le nombre de Layout Shifts, mais de concevoir des interfaces qui ne produisent aucun Layout Shifts. Dans le cas contraire, plus leurs interfaces sont populaires, plus les durées des session de leurs utilisateurs et utilisatrices seront longues, plus le CLS mesuré sera élevé.
Le problème des barres de défilement
Bien que cela puisse paraître contre-intuitif, les barres de défilement horizontales et verticales font partie du viewport. Elles sont notamment incluses dans les dimensions de pourcentage du viewport vw
et vh
. L’apparition ou la disparition des barres de défilement ne modifie donc pas le viewport, et ne sont donc pas éligibles aux « 500 ms sans accumulation de CLS ». Pourtant, l’apparition et la disparition des barres de défilement modifient la zone disponible pour les contenus, qui peuvent donc se déplacer pour mieux l’occuper, provoquant un Layout Shift.
Sur certains systèmes d’exploitation, comme Windows, les barres de défilement sont cachées jusqu’à ce qu’elles soient nécessaires. Si le chargement de votre site est très progressif (avec par exemple une page vide au début puis des requêtes JavaScript qui, petit-à-petit, créent le contenu), alors les barres de défilement peuvent être absentes lors du First Paint, puis apparaître au cours du chargement de la page et provoquer des Layout Shifts.
Pour éviter ce comportement, vous pouvez ajouter html { overflow-y: scroll }
afin de forcer l’apparition d’une barre de défilement au First Paint, ou attendre l’implémentation du module CSS Overflow de niveau 4 et la nouvelle propriété scrollbar-gutter
.
Cumulative Layout Shift #CLS tuning tip
— Tim Vereecke (@TimVereecke) March 16, 2020
Add html{overflow-y:scroll} to enforce a scrollbar at first paint.
Pages with a layout shift drastically reduced as soon as the above CSS rule was added. #WebPerf #AimForZero pic.twitter.com/k4KCUnmzPo
Impact de la timing-function
Dans l’exemple suivant, nous comparons deux mises en page réalisées avec flexbox
. Dans les deux cas, le survol de l’élément central double sa surface. Sur la première ligne, la timing-function
est ease
. Sur la seconde, c’est linear
.
Comme vous pouvez le voir, le deuxième exemple génère beaucoup moins de Cumulative Layout Shift. Pour l’expliquer, nous devons revenir à la spécification de l’API Layout Shift, et plus précisément à ce que l’on appelle les « nœuds instables » (« Unstable nodes »).
A Node N has shifted in a coordinate space C if the starting point of N in C differs from the previous frame starting point of N in C by 3 or more pixel units in either the horizontal or vertical direction.
qu’on peut traduire par :
Un nœud N s’est déplacé dans un espace de coordonnées C si le point de départ de N en C diffère du point de départ de la trame précédente de N en C de 3 unités de pixels ou plus dans le sens horizontal ou vertical.
Cette règle des « trois pixels entre deux frames » introduit un critère de vitesse dans la mesure de l’instabilité. Tous les éléments se déplaçant en dessous de cette vitesse ne sont pas considérés comme instables, et ces animations ne produisent pas de Layout Shifts.
Malheureusement, cela signifie que pour réduire le CLS, certains propriétaires de sites pourraient être tentés d’utiliser des animations linéaires, qui ont une vitesse constante, plutôt que des animations plus intuitives pour l’homme, telles que ease
, qui accélère au début de l’animation et ralentit à la fin.
Autres comportements étranges de l’indicateur
- Les éléments animés qui créent des Layout Shifts peuvent continuer à le faire, même s’ils ne sont pas visibles pour l’utilisateur (par exemple, s’ils ont une couleur qui se fond avec l’arrière-plan). Mais si vous utilisez
visibility: hidden
, ce ne sera pas le cas. - La page web.dev du CLS (traduite en français par Sarah Salis pour Fasterize) recommande d’utiliser
transform
ettranslate
pour créer des animations qui ne produisent pas de Layout Shifts. Chrome créer des Layout Shifts lorsque le temps de transition est de 0, jusqu’à Chrome 86.
Conclusion
Le Cumulative Layout Shift est un indicateur qui mesure les instabilités de la mise en page d’une page web. Pour l’optimiser et améliorer l’expérience utilisateur, vous devez éviter d’injecter du contenu pour lequel le navigateur n’a pas alloué la bonne quantité d’espace. Il existe plusieurs techniques pour les images, mais cela rendra toutefois la gestion de la publicité beaucoup plus complexe.
Le mode de calcul du CLS lui confère des caractéristiques inhabituelles. L’algorithme actuel du CLS n’est probablement pas adapté aux Single Page Apps, car l’attribution des résultats au niveau de la page n’est pas correctement gérée. Par conséquent, l’utilisation des Core Web Vitals (Signaux Web Essentiels) comme critère de classement pourrait être particulièrement difficile dans de tels cas.
N’oubliez pas que le CLS est une nouvelle mesure. Nous manquons encore de retours d’expérience à grande échelle. En cas de doute, concentrez-vous toujours sur l’amélioration de l’expérience de l’utilisateur plutôt que sur l’optimisation de l’indicateur lui-même. Certaines optimisations du CLS peuvent aller à l’encontre de l’UX et doivent être évitées.
Support Navigateurs
En octobre 2018, Steve Kobes a annoncé que Blink (le moteur de rendu Chrome) avait l’intention d’implémenter une API faisant émerger cette information. Le travail sur le projet d’API Layout Instability a commencé en mai 2019, et l’API a été publiée dans Chrome 77 en septembre 2019. Daniel Holbert a créé un ticket pour Firefox en juillet 2020. Nous n’avons trouvé aucune mention de l’API Layout Instability parmi les tickets publics de Safari.
Resources
- Mes propres expérimentations sur le Cumulative Layout Shift
- « Layout Instability API specification », par Steve Kobes, Nicolás Peña Moreno et Emily Hanley
- « Cumulative Layout Shift », par Philip Walton et Milica Mihajlija
- « Optimize Cumulative Layout Shift »
et « Infinite Scroll without Layout Shifts », par Addy Osmani - « Web Vitals - A New Hope in Web Performance Measurement », par Dave Smart
Merci à Damien Jubeau et Matt Hobbs pour leurs contributions.