Come le animazioni innescate dallo scroll causano il CLS
L'header che si nasconde allo scroll potrebbe far fallire il CLS silenziosamente. Vediamo perché succede e come risolverlo.
Quando mi chiedono di fare un audit del Cumulative Layout Shift, parto ovviamente dagli spostamenti durante la fase di caricamento. Controllo le immagini senza dimensioni, gli annunci iniettati e i font swap (FOUT).
Ma non tutto il CLS si verifica durante il caricamento della pagina. A volte è innescato da interazioni come lo scroll. Individuare questi problemi è difficile: Lighthouse non fa scroll e devi sapere esattamente dove guardare.
Ultima revisione a cura di Arjen Karel a marzo 2026
In un recente audit ho aggiunto i dati RUM di CoreDash e ho notato un pattern sospetto. Un elemento #header sembrava causare molto più layout shift del normale.

Quello che vediamo qui è un header fixed che si nasconde quando l'utente scorre verso il basso e riappare quando scorre verso l'alto. È un pattern che vedo spesso: se non è implementato a dovere, causa un layout shift a ogni cambio di direzione dello scroll.
Perché i layout shift causati dallo scroll sono diversi
Normalmente il CLS non si verifica dopo un'interazione. Il CLS misura gli spostamenti di layout imprevisti, mentre dopo un'interazione ci si aspetta che il layout cambi. Per questo la metrica CLS ha un periodo di grazia automatico di 500ms: gli spostamenti che avvengono entro 500 millisecondi da un'interazione dell'utente sono esclusi dal punteggio. Il browser gestisce questo meccanismo impostando e controllando il flag hadRecentInput su questi shift.
Il problema è che questo vale solo per eventi discreti come click, tap e pressione di tasti. Secondo la Layout Instability specification, lo scroll non è considerato un input di esclusione. Nemmeno i drag o i gesti di pinch-zoom lo sono. Solo gli eventi discreti beneficiano del periodo di grazia di 500ms.
Questo significa che ogni layout shift innescato da un evento di scroll viene conteggiato nel punteggio CLS.
Secondo il Web Almanac 2025, il 40% delle pagine mobile usa ancora animazioni non composite. Si tratta di animazioni su proprietà come top, height o margin che innescano il ricalcolo del layout a ogni frame. Se una di queste si attiva durante lo scroll, ogni singolo frame viene conteggiato nel CLS.
Il problema dell'header che si nasconde allo scroll
Partiamo dal problema che ho riscontrato oggi: l'header che si nasconde allo scroll. Un header fixed usa JavaScript per alternare la visibilità durante lo scroll. L'implementazione ha questo aspetto:
/* CSS */
#header {
position: fixed;
top: 0;
transition: top 0.3s ease;
}
/* JavaScript */
window.addEventListener('scroll', () => {
if (scrollingDown) {
header.style.top = '-80px'; // hide
} else {
header.style.top = '0px'; // show
}
}); A causare il layout shift è la combinazione di transition: top 0.3s ease e la modifica di header.style.top in JavaScript.
Il motivo: top è una proprietà di layout. Quando il browser elabora una modifica a top, esegue l'intera rendering pipeline: Style, Layout, Paint, Composite. A ogni frame di animazione durante quella transizione, il browser ricalcola il layout. E ogni ricalcolo produce una nuova voce di layout shift.
A ogni cambio di direzione dello scroll, il contatore del CLS continua a salire finché l'utente interagisce con la pagina.
Ho creato una demo che riproduce esattamente questo comportamento a partire da un sito reale che stavo analizzando e ho registrato un video. Dopo un po' di scroll, il CLS totale ha superato 0,1. Sia chiaro: un'attività di scroll così intensa capita raramente nella realtà, ma il meccanismo non cambia. E ho visto questo layout shift spingere siti oltre la soglia e far fallire il loro CLS totale.
Quali proprietà CSS possono causare layout shift se animate?
Perché un'animazione causi CLS, deve innescare la fase di Layout. Dopo il calcolo degli stili, la rendering pipeline del browser prevede tre fasi principali: Layout, Paint e Composite. Le proprietà che influiscono solo sulla fase di Composite non provocano mai layout shift. Per approfondire come le transizioni CSS causano layout shift, consulta l'articolo dedicato.
Proprietà che innescano il layout (e causano CLS se animate):
top, left, right, bottom, width, height, margin, padding, border-width, font-size
Tutte queste proprietà modificano la geometria o la posizione di un elemento. Il browser deve quindi ricalcolare la posizione dell'elemento (e spesso anche quella di altri elementi).
Proprietà che saltano del tutto il layout (e sono sicure per le animazioni):
transform e opacity vengono eseguite sul compositor thread. Bypassano sia il layout sia il paint. Il browser le gestisce sulla GPU (ecco perché queste animazioni funzionano anche mentre il main thread è occupato). Poiché non innescano mai il ricalcolo del layout, non generano mai voci di layout shift.
Sostituisci le proprietà di layout con i transform
Il fix per l'header che si nasconde allo scroll è semplice: sostituisci l'animazione di top con transform: translateY().
/* CSS - BEFORE (causes CLS) */
#header {
position: fixed;
top: 0;
transition: top 0.3s ease;
}
/* CSS - AFTER (no CLS) */
#header {
position: fixed;
top: 0;
transition: transform 0.3s ease;
}
/* JavaScript - BEFORE */
header.style.top = '-80px';
/* JavaScript - AFTER */
header.style.transform = 'translateY(-80px)'; Il risultato visivo è identico: l'header scorre verso l'alto e scompare. Ma transform: translateY() viene eseguito interamente sul compositor e non provoca mai un layout shift.
Lo stesso principio vale per qualsiasi animazione innescata dallo scroll:
- Pannelli scorrevoli: usa
transform: translateX()invece dileftoright - Elementi che si espandono: usa
transform: scaleY()invece diheight - Elementi in dissolvenza: usa
opacityinvece divisibilitycombinata a modifiche di altezza
Altri pattern innescati dallo scroll che causano CLS
L'header che si nasconde allo scroll è un caso frequente, ma ce ne sono altri. Qualsiasi codice JavaScript che modifica una proprietà di layout in risposta a eventi di scroll produrrà un layout shift. Ad esempio:
Effetti parallax con top o margin-top. Sostituiscili con transform: translateY() oppure usa l'approccio CSS con perspective e translateZ per un parallax in puro CSS.
Navigazione sticky che cambia altezza allo scroll. Una barra di navigazione che si riduce da 80px a 50px quando l'utente supera una certa soglia di scroll. Se la variazione di altezza è animata con transition: height, sostituiscila con transform: scaleY().
Barre di avanzamento legate allo scroll che usano width. Un indicatore di lettura che cresce da sinistra a destra animando width. Usa invece transform: scaleX() con transform-origin: left.
Alternativa moderna: le CSS Scroll-Driven Animations. L'API animation-timeline: scroll() consente di pilotare le animazioni in base all'avanzamento dello scroll senza ricorrere a JavaScript. Poiché queste animazioni usano transform e opacity per impostazione predefinita e vengono eseguite sul compositor, non causano mai layout shift. Il supporto dei browser copre Chrome 115+ ed Edge 115+, mentre per Safari è ancora in sviluppo. Per effetti legati allo scroll come parallax, barre di avanzamento e reveal animation, è la soluzione più pulita. Se vuoi eliminare del tutto lo scroll tramite JavaScript, l'API scroll-driven animations è la via da seguire.
Perché Lighthouse non rileva questo problema
Lighthouse simula il caricamento della pagina. Non fa scroll. Non interagisce con la pagina dopo il caricamento. Questo significa che i layout shift innescati dallo scroll sono del tutto invisibili nei lab data.
L'unico modo per individuare questi shift è il Real User Monitoring. I field data catturano l'intero ciclo di vita della pagina, compreso ogni layout shift che si verifica mentre l'utente scorre, fa click e naviga. Se il punteggio CLS di CrUX è peggiore di quello di Lighthouse, le animazioni innescate dallo scroll sono una delle prime cose da verificare. CoreDash traccia il CLS dei visitatori reali, compreso ogni spostamento causato dallo scroll, mostrandoti con precisione quali elementi ne sono responsabili.
Come individuare i layout shift innescati dallo scroll
Apri Chrome DevTools. Vai nel pannello Performance. Avvia la registrazione (l'icona circolare), poi scorri la pagina in alto e in basso più volte. Ferma la registrazione. Cerca i rombi viola nella traccia Layout Shifts. Se vedi un gruppo di shift che coincide con i movimenti di scroll, hai un problema di CLS innescato dallo scroll. Puoi anche usare l'estensione per Chrome Core Web Vitals Visualizer per osservare il CLS in tempo reale mentre fai scroll.

La regola pratica
Se si muove allo scroll, animalo con transform. Se sfuma allo scroll, animalo con opacity. Queste proprietà sono gestite esclusivamente dal compositor.
Per JavaScript: controlla gli handler degli eventi di scroll, i callback di IntersectionObserver e qualsiasi codice che modifichi gli stili degli elementi in base alla posizione dello scroll. Se usa top, left, margin, width, height o padding, sostituiscilo con il transform equivalente.

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.