Risolvi Layout shift culprits su Lighthouse e PageSpeed
Cosa si è spostato durante il caricamento della pagina, come verificare l'elemento che Lighthouse incolpa e come risolvere gli spostamenti causati da immagini, font, annunci, barre e CSS in ritardo.
Perché Layout shift culprits è rosso su PageSpeed Insights?
Layout shift culprits diventa rosso quando i layout shift durante il caricamento sommati danno un CLS pari o superiore a 0,1: "I layout shift si verificano quando gli elementi si muovono in assenza di interazione dell'utente". In precedenza Lighthouse lo chiamava Avoid large layout shifts. Elenca cosa si è spostato e ne ipotizza la causa. Riserva spazio per ciò che arriva in ritardo: width e height sulle immagini, un'altezza per gli slot pubblicitari.
Vuoi subito la soluzione? Vai a immagini, web font, annunci ed embed o banner.
Come si legge la tabella Layout shift culprits?
Lighthouse registra un singolo caricamento della pagina su uno schermo formato smartphone ed elenca i layout shift divisi in burst (spostamenti che avvengono a meno di un secondo di distanza l'uno dall'altro), con una tabella per ciascun burst. La segnalazione è rossa quando il burst peggiore registra 0,1 o più, e grigia al di sotto. Punta a 0 anche quando è grigia: uno spostamento inferiore a 0,1 muove comunque il testo che il visitatore sta leggendo.
Su una pagina di ricette creata come un food blog WordPress — con un titolo in web font, un plugin per la newsletter, un annuncio sotto l'header e un plugin per la scheda ricetta — la segnalazione era rossa con un singolo burst di 0,241.

La segnalazione della pagina di ricette in Lighthouse 13.5, mobile, con throttling simulato come su PageSpeed Insights.
- Total è il punteggio del burst; il Total del burst peggiore corrisponde al CLS riportato da Lighthouse.
- Element è l'elemento che si è spostato di più in quello shift, spesso un contenitore ampio come
<main>. Ciò che lo ha spinto si trova di solito più in alto. - Layout shift score è il contributo di quel singolo spostamento al Total.
- La riga sotto ogni voce è l'ipotesi di Lighthouse sulla causa: "Unsized image element" con l'immagine, o "Web font" con il file del font. Una riga può mostrare entrambe le cause, o nessuna delle due.
Nella pagina di ricette Lighthouse ha indicato l'immagine sbagliata sotto tre dei quattro spostamenti: verifica quale elemento si è mosso davvero prima di modificare qualsiasi cosa.
Perché la pagina salta durante il caricamento?
Il visitatore comincia a leggere e il testo scivola via, oppure tocca un link e finisce su ciò che ne ha preso il posto.
Il browser esegue il paint della pagina con ciò che ha a disposizione. Tutto ciò che arriva dopo senza uno spazio riservato spinge via il contenuto sottostante: un'immagine senza dimensioni parte da un'altezza pari a zero, un web font rende il titolo più largo rispetto al fallback. Ogni movimento è un layout shift, calcolato in base a quanto schermo si muove e di quanto. Gli spostamenti distanti meno di un secondo formano un singolo burst, e il CLS è il punteggio del burst peggiore: piccoli spostamenti ravvicinati finiscono così per sommarsi.
Nella pagina di ricette ogni singola causa resta sotto 0,1, e tutte e quattro si verificano in un unico burst:
| Causa nella pagina di ricette | CLS da sola | Dopo il fix |
|---|---|---|
| Web font del titolo | 0,035 | 0 |
| Foto della ricetta senza width e height | 0,036 | 0 |
| Barra della newsletter sopra l'header | 0,058 | 0 |
| Annuncio in uno slot vuoto | 0,054 | 0 |
| Tutte e quattro insieme | 0,241 | 0 |
Insieme, le quattro cause ottengono un punteggio superiore alla somma delle singole righe, perché nella pagina completa il font e la foto spostano ciascuno una porzione maggiore dello schermo. Risolvere tutte e quattro ha portato il punteggio di Performance mobile da 87 a 100, con FCP, LCP e Total Blocking Time invariati. Il CLS pesa per un quarto di quel punteggio, ma Google posiziona la pagina in base al CLS ottenuto dai visitatori reali.

Prima, al first paint

Prima, a 2 secondi

Dopo, al first paint

Dopo, a 2 secondi
La pagina di ricette alle dimensioni smartphone di Lighthouse. Il riquadro tratteggiato indica la posizione della foto al first paint.
Lighthouse ha incolpato l'elemento giusto?
Lighthouse ipotizza la causa in base alle tempistiche: un web font che termina il download appena prima dello spostamento, un'immagine su cui il browser esegue il paint subito dopo. Di conseguenza, un'immagine che è stata solo spinta può finire elencata come "Unsized image element". L'immagine in evidenza della pagina di ricette ha width="1200" height="675", eppure Lighthouse la indica sotto gli spostamenti del font, della barra della newsletter e dell'annuncio. Se sotto una riga non compare alcuna spiegazione, parti dal punto 2.
- L'immagine indicata ha una dimensione? "Image elements do not have explicit width and height", nella sezione Diagnostics dello stesso report, elenca le immagini che ne sono prive. In DevTools, la scheda Computed dell'immagine mostra una proporzione per aspect-ratio (auto 1200 / 675) se lo spazio è riservato, e solo auto quando non lo è. Un'immagine dimensionata non ha causato lo spostamento, a meno che il tema non imposti width: auto (Unsized image element più sotto): controlla l'altra riga sotto la voce, spesso "Web font", oppure passa al punto 2.
- Cosa è apparso in quel momento? Registra un ricaricamento nel pannello Performance. Ogni spostamento è un rombo viola nella traccia Layout shifts; passaci sopra con il mouse per vedere il punteggio e uno screenshot di quel momento con l'area spostata evidenziata. Nella pagina di ricette lo spostamento da 0,0583 mostra la barra verde della newsletter appena apparsa in alto. Per gli spostamenti durante lo scroll, clicca su Record, scorri e clicca su Stop.
- Blocca l'elemento sospetto e ricarica. Nel pannello Network, fai clic destro sulla request sospetta, come il file del font o il tag dell'annuncio, scegli Block requests, poi Block request URL e registra di nuovo. Se un rombo scompare, apparteneva a quella request: bloccando il font della pagina di ricette è sparito uno spostamento, lasciando gli altri tre. Al termine, deseleziona il blocco nel drawer Request conditions.

Punto 2 sulla pagina di ricette: Chrome 155, Slow 4G e rallentamento CPU 4×.
Senza DevTools, questo controllo lo fa cRUM in CoreDash: affianca un trace di Chrome della pagina ai dati dei visitatori reali, indica l'elemento che si è mosso e mostra cosa ha fatto il visitatore subito prima. Lo screenshot qui sotto proviene da un'altra pagina: parti da Chrome trace e Real users.

CoreDash cRUM per una pagina su acme-pagespeed.com, mobile.
Quali elementi si spostano per i visitatori reali?
Lighthouse carica la pagina una sola volta, come visitatore al primo accesso, e non esegue mai scroll o tap. I field data raccolgono ogni visita reale, inclusi l'annuncio a metà articolo che si carica quando il visitatore ci arriva con lo scroll e l'header che si rimpicciolisce durante lo scroll. Quando Lighthouse non rileva problemi o è grigio e Search Console segnala che il CLS è scarso, di solito gli spostamenti avvengono dopo il caricamento.
In CoreDash, imposta il dispositivo su mobile e raggruppa il CLS per Attribution Element (CLS) per vedere quali elementi si sono mossi; Last interaction (CLS), sotto More dimensions, indica se lo spostamento è avvenuto dopo uno scroll. Per scoprire cosa ha spinto quegli elementi, registra la pagina nel pannello Performance mentre scorri (punto 2 del controllo qui sopra).

CoreDash su mobile, p75: parti dal CLS più alto.
Come risolvere Layout shift culprits?
Parti dallo spostamento più grande nella tabella con il Total più alto, dopo che il controllo qui sopra ne ha confermato la causa.
Unsized image element
Un'immagine senza dimensioni parte da un'altezza pari a zero e, quando si carica, spinge verso il basso tutto ciò che si trova sotto. WordPress aggiunge width e height alle immagini della libreria media; quelle generate direttamente da un tema, page builder, slider o plugin spesso ne sono prive, come la foto della scheda ricetta. Se non puoi modificare il tema, WP Rocket aggiunge le dimensioni mancanti (Media, Add missing image dimensions); in un template puoi scriverle direttamente:
<img src="shakshuka.jpg" width="800" height="600" alt="Shakshuka in the pan">
img { max-width: 100%; height: auto; } Il browser converte i due attributi in una proporzione e riserva l'altezza corretta a qualsiasi larghezza dello schermo prima ancora che il file arrivi. Un width: auto nel CSS del tema annulla questa proporzione (auto-sizing delle immagini). Su Shopify il filtro image_tag aggiunge le dimensioni; un tema o un'app che genera il proprio <img> richiede width="{{ image.width }}" height="{{ image.height }}". In Next.js, next/image richiede width e height, oppure fill all'interno di un genitore con dimensioni proprie.
Web font
Il titolo appare prima con il font di fallback e poi con il web font, che ha una larghezza diversa: il testo va a capo su parole differenti e tutto ciò che sta sotto si sposta. Su WordPress (CSS aggiuntivo) e Shopify (il foglio di stile del tema in Modifica codice; un aggiornamento del tema può sovrascriverlo), si aggiunge una font face di fallback che ridimensiona un font locale sulle misure del web font. In Next.js, next/font aggiunge un fallback corrispondente. Il font del titolo della pagina di ricette:
@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 del 122% calcolato con un tool lasciava comunque un CLS di 0,029; il 131% lo ha azzerato. L'articolo su font display include la regola per Android, come ricavare i propri valori e font-display: optional, che elimina lo spostamento mostrando il fallback a chi visita il sito per la prima volta.
Annunci, video e altri embed
Lo script pubblicitario inserisce il proprio iframe nello slot al termine dell'asta; se lo slot era vuoto, tutto ciò che si trova sotto slitta verso il basso dell'altezza dell'annuncio. Secondo lo State of Web Vitals, l'89% dei siti senza reti pubblicitarie supera il CLS su smartphone, contro il 73% dei siti con due o più network. È una correlazione.
Nel tema o nel plugin pubblicitario, assegna al contenitore dell'annuncio l'altezza del formato mostrato più spesso e rendi l'iframe un elemento block:
.ad-slot { min-height: 50px; }
.ad-slot iframe { display: block; } Di default un iframe si posiziona sulla linea di testo: lo slot riservato nella pagina di ricette cresceva comunque di 8 pixel finché l'iframe non è diventato un elemento block. Mantieni lo slot aperto anche se non c'è alcun annuncio: chiuderlo sposta la pagina tanto quanto riempirlo. Gli annunci automatici di AdSense si posizionano dove decide Google: escludi le aree nella prima schermata (Impostazioni degli annunci, Aree escluse) e inserisci al loro posto un'unità pubblicitaria manuale in uno slot dimensionato.
Anche YouTube, mappe e altri embed sono iframe, e su un iframe gli attributi width e height non impostano alcuna proporzione: con height: auto si riduce a circa 150 pixel. Assegna all'iframe un aspect-ratio, oppure usa una facade: un'anteprima dimensionata che si trasforma in iframe al clic (YouTube, Google Maps):
.video-embed iframe { width: 100%; height: auto; aspect-ratio: 16 / 9; } Barre e banner inseriti da uno script
Una barra per la newsletter o una striscia per la spedizione gratuita inserita da uno script sopra l'header spinge l'intera pagina verso il basso.
Controlla prima di tutto le impostazioni di layout del plugin o dell'app: una barra in basso, un box o un popup fluttuano sopra la pagina senza spostare nulla. I tool di consenso come OneTrust e CookieYes offrono gli stessi layout; Lighthouse visualizza il loro banner a ogni scansione perché parte senza cookie. Se la barra deve necessariamente stare sopra l'header, riserva il suo spazio nel tema, all'altezza mostrata nella scheda Computed alla larghezza dello smartphone:
<div id="promo-slot" style="min-height: 48px"></div> Per una barra che compare solo in alcune visite, falla fluttuare sopra la pagina oppure renderizzala nell'HTML lato server. Il contenuto visibile iniettato da un tag di Google Tag Manager arriva sempre dopo il first paint: spostalo nel template. Su Shopify, un'app che inietta un widget (come le stelle delle recensioni o una barra degli annunci) richiede un'altezza definita sul contenitore in cui si inserisce il suo app block.
CSS caricato dopo il first paint
Un foglio di stile caricato con il trucco di media="print", oppure ritardato o caricato in modo asincrono da un plugin di ottimizzazione, arriva quando la pagina è già sullo schermo e ridimensiona ciò che è già presente: un header che si espande spinge verso il basso tutto il resto. Nel plugin, escludilo dall'opzione che ritarda il CSS o lo carica in modo asincrono; nel tema, caricalo con un normale <link rel="stylesheet"> nell'head.
Animazioni, slider e blocchi renderizzati da JavaScript
Di solito derivano dal tema, da un plugin o da un'app: cerca un'impostazione lì. Se non c'è, passa a uno sviluppatore l'elemento indicato nel report insieme al codice qui sotto.
Un'animazione su top, left, height o margin modifica il layout a ogni frame. Lighthouse la elenca come una serie di piccole righe senza indicazione della causa, e la voce "Avoid non-composited animations" in Diagnostics segnala l'elemento. Anima invece transform: muove i pixel dell'elemento senza toccare ciò che lo circonda, quindi il browser non registra alcun layout shift.
.promo { position: fixed; top: 0; left: 0; right: 0;
transform: translateY(-100%); transition: transform .3s; }
.promo.is-open { transform: translateY(0); } Uno slider mostra le slide impilate finché il relativo script non viene eseguito; mantienilo sulla prima slide finché lo script non aggiunge la classe ready (usa quella specifica dello slider):
.slider:not(.is-ready) > :not(:first-child) { display: none; } Un blocco che JavaScript renderizza dopo il caricamento, come recensioni o prodotti correlati, necessita di un contenitore vuoto con l'altezza finale già definita, oppure del rendering lato server. Per le animazioni allo scroll, vedi animazioni attivate dallo scroll e transizioni CSS.
Spostamenti causati da codice non modificabile
Non puoi modificare ciò che viene inserito da una creatività pubblicitaria, da un'app priva di impostazioni di layout o dallo script di un fornitore. Riserva l'altezza che occupa di solito, posizionalo sotto la prima schermata o sostituiscilo con uno strumento che rispetta i propri spazi.
Le modifiche hanno portato il CLS a 0?
Verifica il fix in Lighthouse su mobile, dopo aver svuotato il plugin di cache e la CDN. Eliminato ogni spostamento, Layout shift culprits risulterà superato e il CLS sarà pari a 0. Una registrazione nel pannello Performance, eseguita una volta con un ricaricamento e una durante lo scroll, mostrerà la traccia Layout shifts completamente vuota. CoreDash mostrerà il nuovo CLS per elemento già dalle visite successive; Search Console richiede fino a 28 giorni.

Prima

Dopo
Prima e dopo i quattro fix: Lighthouse 13.5, mobile, throttling simulato.
Alcuni spostamenti hanno origine in punti a cui non hai accesso: uno slot pubblicitario gestito dal partner pubblicitario, un'app che inietta il proprio widget, uno spostamento visibile solo ai visitatori reali dopo aver fatto scroll. In una Core Web Vitals power hour li analizziamo insieme nei field data e in una registrazione della pagina, per decidere quale altezza assegnare a ciascun contenitore.

Ti serve un sito più veloce?
Sono Arjen e da 17 anni sistemo siti lenti. Se hai problemi che non riesci a risolvere da solo, mandami il tuo URL e ne parliamo.
Mandami il tuo URL
Perché il fix non funziona?
A volte la CDN serve ancora il vecchio HTML, oppure il fix è finito nel template desktop e il mobile ne usa un altro. Mandami il tuo URL e il problema, e ti rispondo con qualche orario. Prima analizzo i tuoi field data, poi dedichiamo un'ora al tuo sito, ed entro un giorno hai le mie note per iscritto. 350 € IVA esclusa.
Mandami il tuo URLNon hai uno sviluppatore per il fix? Faccio io il lavoro, su una copia del tuo sito.