Public cible
Débutants, étudiants et professionnels qui souhaitent acquérir une pratique structurée dans ce domaine.
Optimiser ce que les utilisateurs vivent réellement : mesurer les Core Web Vitals en données de terrain au 75e percentile, décomposer LCP en ses quatre sous-parties, traiter l'INP par les tâches longues, et régler le CLS en réservant la place des contenus. Une méthode de travail mesurée, correction par correction.
Débutants, étudiants et professionnels qui souhaitent acquérir une pratique structurée dans ce domaine.
Comprendre les notions essentielles et les appliquer dans un projet vérifiable.
Aucune connaissance préalable n’est strictement obligatoire ; les prérequis détaillés sont précisés dans les ateliers.
Tous niveaux
La validation repose sur les évaluations actives et les livrables pratiques du cours.
Un ordinateur, un navigateur et les outils indiqués dans le premier atelier sont suffisants pour commencer.
Relever les trois métriques, identifier la sous-partie défaillante et établir un plan de correction mesuré.
Obligatoire ConsulterAmener un groupe d'URL dans le seuil sur la métrique défaillante, avec journal de mesures avant/après.
Livrables :
Relever les trois métriques, identifier la sous-partie défaillante et établir un plan de correction mesuré.
Obligatoire ConsulterAmener un groupe d'URL dans le seuil sur la métrique défaillante, avec journal de mesures avant/après.
Livrables :
Relever les trois métriques, identifier la sous-partie défaillante et établir un plan de correction mesuré.
Obligatoire ConsulterAmener un groupe d'URL dans le seuil sur la métrique défaillante, avec journal de mesures avant/après.
Livrables :
Relever les trois métriques, identifier la sous-partie défaillante et établir un plan de correction mesuré.
Obligatoire ConsulterAmener un groupe d'URL dans le seuil sur la métrique défaillante, avec journal de mesures avant/après.
Livrables :
La distinction est la première chose à installer, parce qu'elle change tout le reste. Tout le monde prend le même chiffre et le prend pour la même chose.
Un environnement simulé, souvent sur un ordinateur puissant, avec un réseau dégradé artificiel. C'est un excellent outil de diagnostic : il montre quelles ressources sont lentes, quelles tâches bloquent, ce qui décale la mise en page. Il est reproductible, donc deux mesures successives sont comparables.
Ce qu'il ne dit pas : ce que vivent vos utilisateurs. Un score de 100 sur un simulateur peut correspondre à un INP médiocre sur un téléphone d'entrée de gamme en 4G.
Les rapports Core Web Vitals de Search Console agrègent des mesures réalisées par les navigateurs eux-mêmes, sur des appareils et des connexions réels, dans une fenêtre glissante de 28 jours. C'est la donnée que Google utilise, et la seule qui décide.
Une correction publiée hier n'apparaît pas dans les chiffres avant plusieurs semaines. Et une correction ancienne reste dans la fenêtre pendant un mois après être revenue en arrière. Conséquence pratique : une donnée de terrain ne se lit pas au jour le jour, et deux équipes qui regardent le même rapport le même jour peuvent en tirer des conclusions opposées selon ce qu'elles ont changé la veille.
Search Console n'affiche un groupe d'URL que s'il dispose d'un nombre suffisant de données. À défaut, le groupe est omis du rapport. Le piège : une page disproportionnellement populaire aura toujours des données, tandis qu'un template secondaire peut n'en avoir pas assez pour apparaître — et l'absence dans le rapport n'est pas un bon résultat, c'est une absence de mesure.
Google évalue une page au 75ᵉ percentile : la valeur telle qu'elle est vécue par 75 % des visites. Ce n'est pas un détail technique, c'est une décision de conception du produit, et elle change tout.
Il dit : « votre page est bonne pour 75 % de vos visiteurs ». Le quart restant vit une expérience médiocre. La question de gestion est alors : acceptez-vous de laisser un quart de vos utilisateurs subir une page lente ? Selon le produit, la réponse est non — et le P75 est précisément l'indicateur qui rend cette question visible.
Un site dont 80 % des visites sont immédiates et 20 % tardives peut avoir une moyenne flatteuse tout en étant catastrophique pour une part réelle de ses utilisateurs. Pire : sur les pages de catalogue, la moyenne est dominée par le trafic des robots, qui est rapide et n'a aucun problème. Les tableaux de performance « agrégés » sont donc souvent des mensonges — ils décrivent des robots, pas des gens.
Si votre propre outil d'analyse affiche une moyenne, il affiche autre chose que ce que Google utilise. Le module d'outil n'a pas à être remplacé — il a à être lu avec ses limites, et à être complété par le rapport officiel pour les trois métriques.
| Outil | Données | La question à laquelle il répond |
|---|---|---|
| Search Console | Terrain | Ce que vivent mes utilisateurs, par groupe d'URL et par appareil |
| PageSpeed Insights | Terrain si disponible, sinon laboratoire | Quel est l'état de cette URL précise, et le diagnostic associé |
| Lighthouse | Laboratoire | Qu'est-ce qui bloque sur cette page, maintenant |
| DevTools | Laboratoire, local | Quel élément est le LCP, quelles tâches bloquent, ce qui décale |
Lighthouse affiche un score global de performance. Ce score n'a aucune signification officielle : il n'entre dans aucun critère de classement, et il est calculé sur une simulation qui ne représente pas vos utilisateurs. Utiliser ce score comme objectif, c'est optimiser la mauvaise métrique — on peut obtenir 100 en laboratory tout en échouant en terrain.
La documentation le formule implicitement en ne retenant que les trois métriques comme signals de qualité d'expérience. Le reste du rapport est utile comme diagnostic.
Savoir que l'INP est à 400 ms ne sert à rien en soi. L'outil d'attribution indique quelle interaction est responsable, et à quel moment : pendant le chargement ou après, quel type (clic, frappe, toucher). C'est cette information qui transforme une mesure en décision.
Sans attribution, on optimise au hasard — et le résultat le plus fréquent est de corriger un problème qui n'était pas le pire.
Commencer par le laboratoire sans données de terrain est l'erreur de méthode la plus fréquente : on optimise ce qui est facile à mesurer plutôt que ce qui est subi.
Un budget de performance est un engagement écrit : « le JavaScript de ce gabarit ne dépassera pas X Ko », « le LCP de la page d'accueil restera sous 2,5 s au 75ᵉ percentile ». Sans cela, chaque livraison ajoute quelques kilo-octets et quelques dizaines de millisecondes, et personne ne s'en aperçoit.
Le budget ne tient que s'il est vérifié automatiquement et qu'il est visible au moment de la revue. Trois emplacements, du plus simple :
Le troisième est le plus utile et le plus rare : il mesure ce qui est réellement servi, y compris ce que personne ne croyait avoir ajouté.
Un budget de performance sans réponse à « qu'est-ce qu'on sacrifie si on le dépasse ? » n'est pas un budget, c'est un souhait. La réponse à cette question est presque toujours : « on décale une fonctionnalité ». C'est cette réponse qui rend la discussion possible au lieu de la rendre théorique.
LCP — Largest Contentful Paint — mesure le temps entre le début du chargement de la page et l'affichage du plus grand élément de contenu visible dans la fenêtre d'affichage. En pratique : une image, un bloc de texte, une vidéo.
Sur un site institutionnel, l'élément LCP est très souvent une image de bannière ou une illustration d'en-tête — pas le titre. Toute l'optimisation porte alors sur la bonne cible, mais pas sur celle qu'on imaginait. C'est aussi une question de conception : une image de bannière lourde est un mauvais choix, et le diagnostic performance et le diagnostic graphique arrivent à la même conclusion.
Si l'élément LCP est une image dont la source est injectée par JavaScript après le chargement, le moteur de recherche et l'utilisateur attendent tous les deux deux choses. Le lien vers le cours `seo` est fait ici : c'est la même cause qu'un problème d'indexation, et la même correction — mettre l'image dans le HTML initial.
Le temps LCP se décompose en quatre segments. Ce découpage est le cœur du module : il transforme « LCP est trop élevé » en « le délai de chargement de la ressource est de 1,8 seconde, voilà le problème ».
| Sous-partie | Part indicative | Nature |
|---|---|---|
| Temps de réponse du premier octet (TTFB) | ≈ 40 % | Réseau + serveur |
| Délai de chargement de la ressource | < 10 % | Attente évitable |
| Durée de chargement de la ressource | ≈ 40 % | Taille et distance |
| Délai de rendu de l'élément | < 10 % | Attente évitable |
Deux des quatre lignes contiennent le mot « délai ». C'est le signal : ce sont les deux segments qu'il faut ramener à zéro, parce qu'ils ne représentent aucun travail utile. Les deux autres sont du temps de travail réel — transfert réseau, calcul serveur — qui se réduit en optimisant la ressource ou l'infrastructure.
La documentation est explicite sur un point : ces proportions sont des repères, pas des lois. Si votre LCP est déjà sous 2,5 s de façon constante, la répartition importe peu. Si vous êtes à 4 s et que 2,5 secondes partent en délais, aucun travail sur la taille de l'image ne vous ramènera dans le seuil.
Si la réponse est « le délai de chargement de la ressource » ou « le délai de rendu », le problème est dans le code ou le gabarit, et il se corrige localement. Si la réponse est « le TTFB », le problème est côté serveur ou réseau — et il se corrige autrement, souvent par un cache ou une proximité géographique.
C'est le délai entre le début de la page et le moment où le navigateur commence à télécharger l'image LCP. Trois causes, trois corrections.
loading="lazy" — alors que c'est précisément l'image visible immédiatement. Le chargement différé ne doit jamais s'appliquer à l'élément LCP : le navigateur doit d'abord vérifier si l'image est dans la fenêtre, ce qui retarde le téléchargement.La ressource est chargée, mais l'élément n'est pas affiché. Quatre causes, de la plus fréquente à la plus rare.
Si l'image LCP est bien dans le HTML mais doit attendre
sa place dans la file de téléchargement, on lui donne la
priorité : fetchpriority="high" sur le
<img> ou le <link>.
L'attribut augmente sa priorité et elle reçoit plus de
bande passante pendant que les autres ressources
attendent.
La documentation est précise sur le rendement : parmi les pages éligibles, une minorité seulement utilisent cet attribut, alors que l'effort est faible et l'effet immédiat. C'est un des rares cas où le gain est élevé et le coût proche de zéro.
C'est le temps de transfert réel des octets. Quatre leviers, du plus rentable au plus lourd.
La documentation nuance honnêtement : la durée de chargement est rarement le goulot d'étranglement pour la plupart des sites. Il faut donc mesurer la répartition avant de s'y attaquer — sinon on réduit une image de 30 % alors que le problème est ailleurs.
Le TTFB est le délai avant que le premier octet de la réponse ne parvienne au navigateur. Il est indiqué en dernier dans la documentation parce que c'est souvent le levier dont le développeur Dispose le moins — mais il est aussi celui qui débloque tous les autres : rien ne peut se passer côté navigateur tant que le premier octet n'est pas arrivé.
Les causes et les corrections : cache serveur, proximité géographique, nombre de requêtes SQL, temps de génération des pages. Sur une application à rendu serveur, une requête qui prend 800 ms sur chaque page est un défaut de LCP déguisé.
no-store, ou un gestionnaire unload qui est d'ailleurs obsolète.INP évalue la réactivité globale d'une page en observant le délai de toutes les interactions qualifiantes sur la durée de la visite. La valeur finale est l'interaction la plus longue observée, hors valeurs aberrantes.
Parce que l'utilisateur ne se souvient pas de la moyenne. Il se souvient du clic qui n'a rien fait pendant une demi-seconde. INP retient donc le pire moment vécu — ce qui en fait une métrique de ressenti plutôt qu'une métrique de performance moyenne.
Clic, contact, touche, pointeur — les actions /volontaires de l'utilisateur qui déclenchent une réponse de la page. Les défilements, les survols et les chargements ne sont pas comptés. Une animation automatique non plus.
Le navigateur traite une tâche à la fois sur un fil principal. Toute interaction doit attendre que la tâche en cours se termine. Ce n'est pas perceptible en dessous d'un certain délai — l'utilisateur ne voit pas la file d'attente, il perçoit l'attente.
Un détail important et souvent oublié : chaque
iframe a son propre fil principal. INP est
rapporté au niveau de la page, interactions dans les iframes
comprises. Une publicité lente dans un cadre intégré dégrade la
métrique de la page qui l'héberge. Il faut donc identifier
dans quel cadre l'interaction a eu lieu avant d'aller
chercher le fil principal responsable.
Une interaction se décompose en trois temps, et chacun se corrige autrement. C'est la décomposition qui rend l'optimisation ciblée au lieu d'approximative.
Entre le geste de l'utilisateur et le moment où son gestionnaire d'événement commence à s'exécuter. C'est le temps d'attente, et il ne dépend pas du code du gestionnaire : il dépend de ce qui occupe le fil principal avant. Scripts qui se chargent et se compilent, traitements réseau, minuteries, interactions qui se chevauchent.
Pendant le chargement d'une page, c'est la cause dominante : le navigateur analyse et compile chaque script récupéré avant de pouvoir l'exécuter, et ces tâches longues retardent la réponse à toute interaction.
Le temps pendant lequel le gestionnaire s'exécute. Le conseil de la documentation est sans finesse : faire le moins de travail possible dans un gestionnaire d'événement. Si la logique est complexe, ce n'est pas qu'il faut l'optimiser sur place, c'est qu'il faut la découper.
Entre la fin du traitement et le prochain affichage. Il dépend du travail de rendu : mise en page, peinture, composition. C'est là que se paie le DOM volumineux — chaque élément supplémentaire rend le travail de rendu plus coûteux, à l'affichage initial comme après chaque interaction.
Un motif classique : mettre à jour un style, puis lire immédiatement une valeur de style en JavaScript. Le navigateur est forcé de faire un rendu synchrone qu'il aurait pu différer. Dans une boucle, ce motif transforme un rendu en un rendu par itération, et c'est l'une des causes principales d'INP élevé sans JavaScript visible.
Le remède est structurel : séparer les lectures et les écritures — lire toutes les valeurs, puis écrire tous les styles.
Une tâche est un morceau de travail que le navigateur traite d'un bloc : rendu, mise en page, analyse, compilation, exécution de script. Toute tâche de plus de 50 ms est une tâche longue, et son temps au-delà de 50 ms est ce que l'utilisateur perçoit comme un blocage.
C'est le seuil au-delà duquel l'attente devient perceptible. En dessous, l'utilisateur ne remarque rien ; au-dessus, l'interface semble figée. La limite est donc physiologique, pas technique — et c'est pourquoi le découpage fonctionne : ce n'est pas une réduction du travail total, c'est de la répartition.
La technique de base consiste à céder le fil principal au milieu d'une tâche : le navigateur peut alors traiter le rendu et les interactions en attente avant que le travail reprenne.
setTimeout(fn, 0) — simple, mais la continuation n'est pas priorisée : elle passe en fin de file d'attente.scheduler.yield() — l'API conçue pour cela. Elle rend la main et garantit que la suite de la fonction sera replacée en priorité. C'est la différence qui compte quand la page est chargée.scheduler.postTask() — planification par priorités explicites, pour les travaux à faible ou haute priorité.scheduler.yield() n'est pas encore disponible
partout : un repli sur setTimeout enveloppé dans
une promesse est nécessaire, et perd justement la
priorisation. Un polyfill qui retombe sur les autres
fonctions de planification est la solution robuste.
Rendre la main systématiquement coûte : chaque cession a un surcoût, et si les travaux sont très courts, on passe plus de temps à céder et reprendre qu'à exécuter. La pratique recommandée est de céder par lots, environ toutes les 50 ms, pas à chaque opération. Le module cite la recommandation telle quelle, car elle est contre-intuitive : l'instinct est de céder dès qu'on peut.
La documentation ne recommande plus
isInputPending(), qui consiste à ne céder que
si une entrée est en attente. Trois raisons données : il
peut renvoyer false à tort malgré une
interaction réelle ; l'entrée n'est pas le seul cas où il
faut céder, les animations et mises à jour régulières de
l'interface comptent aussi ; et des API plus complètes
existent désormais.
Un cours qui cite cette recommandation transmet une consigne abandonnée. C'est le genre d'erreur qui survit des années parce qu'elle est vraie à sa date de publication.
La cause racine la plus fréquente d'INP élevé : trop de JavaScript à analyser, compiler et exécuter. Chaque script récupéré est trois fois coûteux — transféré, analysé, compilé — et seul le transfert est visible dans le panneau réseau. Le poids total du JavaScript est le meilleur prédicteur de l'INP, davantage que la complexité logique.
Le gain immédiat : rendre la main permet au navigateur de traiter une interaction en attente au milieu d'un traitement de fond. La page ne fait pas moins de travail, elle devient réactive.
Une grande partie du JavaScript d'une page ne sert pas à l'affichage initial. Le différer — jusqu'au premier défilement, à une interaction, à l'inactivité — réduit à la fois le LCP et le délai d'entrée. Attention à ne pas décaler ce qui est nécessaire, sous peine de créer un défaut de contenu au moment où l'utilisateur en a besoin.
Deux cas où le volume du DOM se paie : au premier
rendu, et après chaque interaction. La propriété CSS
content-visibility permet de rendre
paresseusement les éléments qui approchent de la fenêtre
d'affichage. Elle demande de la pratique, et le module le
signale comme un piste plutôt que comme une correction
automatique.
Point de vigilance pour les applications à page unique : même sans changement d'URL, elles rendent du HTML via JavaScript à chaque interaction. C'est acceptable tant que le volume rendu reste raisonnable ; au-delà, c'est la cause principale du temps d'affichage suivant.
Avant toute correction, identifier le cadre dans lequel l'interaction a eu lieu. Sur une page qui embarque une carte, une publicité ou un lecteur vidéo tiers, la réponse est souvent : le fil principal du tiers. Et ce qu'on peut faire est alors borné — ce qui est une bonne raison de le savoir avant d'investir.
La documentation est honnête : corriger une interaction lente en fera souvent apparaître une autre, surtout sur un site très interactif. La persistance est la technique — le résultat vient d'une série de corrections mesurées, pas d'un réglage unique.
CLS — Cumulative Layout Shift — est la somme des scores de déplacement de chaque élément visible qui bouge sans raison pendant la visite. Le score est nul à l'identique et augmente avec chaque décalage.
Ce que CLS mesure n'est pas un détail esthétique. Un élément qui se déplace lorsque l'utilisateur vise un bouton fait manquer le clic : le curseur se trouvait sur le bouton, le bouton a glissé, le clic est parti ailleurs. Sur une liste de résultats, un décalage en pleine lecture fait perdre la ligne. Le CLS est la métrique qui traduit directement un problème d'accessibilité et de confiance en nombre.
Le lien avec le cours d'accessibilité est direct : les deux corrections sont les mêmes — réserver la place des contenus avant leur arrivée.
Contrairement au LCP et à l'INP, le CLS est la moins défaillante des trois : la documentation situe environ trois quarts des origines au vert. Le module ne s'en félicite pas — il en tire la conclusion utile : c'est la métrique où l'effort est le plus rentable, donc celle qu'il ne faut pas laisser se dégrader.
La cause numéro un de décalage : un contenu sans dimensions. Le navigateur ne peut pas lui réserver de place, il l'insère à hauteur nulle, puis le contenu arrive et pousse tout le reste vers le bas.
La documentation donne un chiffre : 66 % des pages contiennent au moins une image sans taille déclarée. Avec une hauteur initiale de 0 pixel, chaque image de ce type provoque un décalage quand elle se charge.
La correction tient en deux attributs par image :
width et height en attributs HTML, ce qui permet au navigateur de calculer le ratio avant le téléchargement ;aspect-ratio si la largeur est dynamique.Ce n'est pas un correctif de performance au sens strict : c'est une déclaration de dimensions. Le navigateur peut alors réserver la place exacte.
En CSS Grid ou Flexbox, la largeur peut dépendre du
conteneur, ce qui empêche d'écrire une largeur fixe en
pixels. La solution est aspect-ratio : on
donne le ratio, le navigateur calcule la hauteur.
Fonctionnellement disponible et applicable aux éléments
non-images également — un encart vidéo ou une carte peut
ainsi réserver sa place.
Le bfcache — back/forward cache — conserve une page en mémoire lorsqu'on la quitte, et la recharge instantanément au retour depuis l'historique. Ce n'est pas une optimisation de performance ordinaire : c'est une page qui ne se charge pas.
Deux effets, tous deux importants. D'abord, la page revenue est identique à celle quittée : aucun décalage n'est possible. Ensuite — et la documentation le dit explicitement — l'introduction du bfcache en 2022 a été la plus grosse amélioration de CLS observée cette année-là. Une part importante du « progrès » du CLS sur le web vient de là, pas d'un travail de mise en page.
no-store, très répandu pour des raisons de sécurité mal comprises.unload — l'événement est obsolète et son seul effet pratique est d'inéligibilité au bfcache.Le bfcache conserve la page en mémoire, sur l'appareil de l'utilisateur. La question de sécurité est donc réelle si la page contient des données sensibles après déconnexion. Le module ne tranche pas à la place du projet : il indique que l'arbitrage existe, que la raison la plus fréquente de l'exclure est une précaution de sécurité rarement justifiée par l'analyse réelle, et que la décision doit être prise par écrit.
DevTools dispose d'un testeur de bfcache, et l'API « Not Restored Reasons » permet de détecter les motifs d'inéligibilité en terrain — donc auprès des vrais utilisateurs, pas seulement en laboratoire. C'est la seule méthode qui donne le motif réel.
Une animation ne provoque pas forcément de décalage — mais une animation qui déplace un élément en provoquant une mise en page en provoque un à chaque image.
Les propriétés CSS se répartissent en trois catégories, et seules les deux dernières dégradient la mise en page ou la peinture :
transform, opacity. Ces deux propriétés sont traitées sur le GPU et n'obligent pas le navigateur à recalculer la position des autres éléments.color, box-shadow, background.width, height, top, margin, font-size. Animer celles-ci force le recalcul de la mise en page de la page entière, à chaque image.Une diapositive qui glisse avec left doit
utiliser transform: translateX(). Un
agrandissement doit passer par scale(). Un
fondu est déjà optimal. La différence de performance
est parfois d'un ordre de grandeur, et le gain
d'accessibilité suit : ces transformations
transform-only s'animent aussi selon la préférence de
mouvement réduit, alors qu'un déplacement par
left est plus difficile à neutraliser.
La règle prefers-reduced-motion désactive
ces animations pour les utilisateurs qui l'ont demandé —
cette préférence existe parce que les animations
provoquent des nausées et des vertiges réels chez
certaines personnes. C'est le même correctif qui sert la
performance, l'accessibilité et le confort.
Un composant instable par nature — un carrousel, une
barre de progression animée, un composant tiers — peut être
explicitement exclu du calcul du CLS. L'attribut
content-visibility ou la propriété dédiée
permettent de le déclarer. C'est un outil légitime pour
les éléments qui ne peuvent pas être stabilisés, à
condition de ne pas l'utiliser pour masquer une instabilité
évitable.
Optimiser une page réelle, mesurer avant et après, et prouver l’amélioration. Une optimisation sans mesure de terrain n’est pas une optimisation : c’est une impression.
Publié le 01/10/2026