content-visibility: auto — Salta il Rendering fuori Schermo per Velocizzare Pagine Lunghe nel 2026
content-visibility: auto e la proprieta CSS che salta il rendering degli elementi fuori viewport. Guida 2026 con esempi pratici, supporto browser aggiornato e trappole comuni da evitare.
content-visibility: auto è una proprietà CSS che dice al browser di saltare completamente layout e paint per gli elementi fuori dal viewport, riducendo il tempo di rendering iniziale fino al 7× su pagine lunghe. In pratica, ogni sezione contrassegnata diventa un'unità di rendering pigra: il browser ne conosce la dimensione (grazie a contain-intrinsic-size) ma non ne calcola stili interni, layout né pixel finché non si avvicina alla viewport. Nel 2026 è supportata in Chrome, Edge, Safari 18 e Firefox 125, quindi finalmente utilizzabile in produzione senza fallback.
content-visibility: auto salta layout, style e paint per gli elementi fuori dal viewport, applicando implicitamente contain: layout style paint.
Va sempre accompagnata da contain-intrinsic-size (o contain-intrinsic-block-size: auto <fallback>) per evitare CLS e scrollbar instabili.
Nel 2026 è cross-browser: Chrome/Edge dal 2020, Safari 18 (settembre 2024), Firefox 125 (aprile 2024).
Su articoli lunghi e griglie di prodotti riduce il tempo di rendering iniziale del 40–85% in test reali su Moto G Power.
Non usarla above-the-fold, su elementi piccoli in gran numero o dentro contenitori con overflow scroll: l'overhead di tracking supera il beneficio.
Il contenuto skipped è ancora nel DOM, indicizzabile da Google e presente nell'accessibility tree (non è display:none mascherato).
Cosa fa content-visibility: auto
La proprietà content-visibility è arrivata insieme al modulo CSS Containment Level 2 e funziona come una scorciatoia dichiarativa per il rendering pigro. Applicata a un elemento, dice al browser: «se non sei visibile e non stai per diventarlo, salta layout, style calculation e paint del sottoalbero». Il risultato è che il browser tratta la sezione come una scatola nera: ne mantiene la posizione nel flusso del documento usando la dimensione dichiarata via contain-intrinsic-size, ma non spende tempo a calcolare i figli.
Sotto il cofano content-visibility: auto attiva automaticamente contain: layout style paint. I tre containment isolano l'elemento in modo che i suoi cambiamenti interni non possano influenzare il layout esterno, ed è il prerequisito per poterne skippare in sicurezza il rendering. Quando l'elemento entra nella «near viewport area» (di solito il doppio dell'altezza dello schermo, gestito dall'algoritmo IntersectionObserver interno del browser), il browser materializza layout e paint just-in-time. Chi ha lavorato su virtualizzazione manuale con React-Window o TanStack Virtual riconoscerà subito il pattern: qui però non serve JavaScript, non serve conoscere l'altezza esatta di ogni riga, e non c'è re-mount al re-render.
Rispetto a soluzioni come loading="lazy" (che agisce solo su immagini e iframe) o alle librerie di virtualizzazione, content-visibility lavora su qualsiasi elemento blocco e non richiede refactoring del markup. È l'unica primitiva del browser che dice esplicitamente al motore di rendering: puoi ignorare questa parte finché non serve.
Perché serve contain-intrinsic-size (e come evitare CLS)
Il problema centrale di skippare il layout di un elemento è: quanto è grande? Se il browser non calcola i figli, non sa quanto spazio occupare, e finisce per assegnare all'elemento un'altezza di 0. Il risultato è uno scroll totalmente rotto e un CLS catastrofico quando ogni sezione viene renderizzata scorrendo. Ci sono incappato la prima volta su una landing di 40 sezioni, e ricordo ancora la faccia del PM quando gli ho mostrato la pagina che «saltava» a ogni pixel di scroll.
La soluzione è contain-intrinsic-size, che fornisce una dimensione «fittizia» che il browser usa mentre il vero layout è skippato:
.article-section {
content-visibility: auto;
/* Riserva 800px di altezza per ogni sezione non renderizzata */
contain-intrinsic-size: auto 800px;
}
La parola chiave auto davanti al valore è cruciale: dice al browser di ricordare la dimensione reale una volta che l'elemento è stato renderizzato la prima volta, così tornando indietro nello scroll usa il valore corretto anziché il fallback. Prima del supporto di auto (Chrome 98+, Safari 18, Firefox 125) era comune vedere pagine con scrollbar che «respirava» durante lo scroll perché ogni ritorno resettava all'altezza fissa.
Per contenuti di dimensione molto variabile puoi separare gli assi:
Il supporto è finalmente stabile su tutti i motori principali. Ecco la matrice aggiornata a settembre 2026:
Browser
content-visibility: auto
contain-intrinsic-size: auto <fallback>
Note
Chrome / Edge
85 (agosto 2020)
98 (febbraio 2022)
Implementazione di riferimento; flag DevTools per debug
Safari
18.0 (settembre 2024)
18.0
Ha atteso 4 anni; oggi allineato allo spec
Firefox
125 (aprile 2024)
125
Include supporto ai container queries dipendenti
Samsung Internet
14.0
19.0
Segue Chromium
WebView Android
85+
98+
Cordova / Capacitor compatibili
Secondo caniuse.com la copertura globale ha superato il 94% degli utenti attivi a metà 2026. Onestamente, non serve più un fallback tramite @supports, ma se vuoi essere paranoico puoi wrappare la dichiarazione:
Quando usare content-visibility: auto (e quando evitarla)
La regola pratica che uso quando faccio audit ai miei clienti DTC è semplice: usa content-visibility su elementi grandi, ripetitivi, non above-the-fold, di dimensione prevedibile. Il beneficio scala con il numero di elementi che il browser può saltare e con il costo di rendering di ciascuno.
Casi d'uso ideali
Articoli lunghi: sezioni <section> del corpo di un long-form. In un progetto recente ho misurato un miglioramento del FCP da 2.1s a 1.4s su un articolo di 6000 parole con 40 sezioni.
Griglie di prodotti con 50+ card. Ogni card ha immagine, prezzo, badge, stelle: il rendering di ciascuna costa 3–8ms su mobile.
Thread di commenti o chat con centinaia di messaggi caricati simultaneamente.
Dashboard con tab non attive: qui content-visibility: hidden è ancora più aggressivo.
Log viewer e tabelle con migliaia di righe rese in HTML.
Casi da evitare
Elementi minuscoli e numerosi (es. ogni <li> di una lista di 500 voci): il costo di tracking IntersectionObserver-like supera il vantaggio.
Contenitori con overflow: scroll interni: il containment può interferire con la scrollbar.
Elementi con altezza estremamente variabile e nessuna stima affidabile: rischi CLS ripetuto.
Elementi contenenti position: sticky o ancoraggi che devono attraversare il boundary di containment.
Esempio completo: un articolo lungo ottimizzato
Ecco il pattern che uso in produzione per un blog Next.js. Ogni sezione dell'articolo è un blocco autonomo con dimensione ragionevolmente prevedibile:
/* CSS */
.post-section {
content-visibility: auto;
contain-intrinsic-size: auto 700px;
/* Nessun'altra regola serve: il browser fa il resto */
}
/* Prima sezione dopo l'hero: rendering immediato per LCP pulito */
.post-section:first-of-type {
content-visibility: visible;
}
Il trucco è escludere l'hero e la prima sezione: sono nel primo viewport e devono contribuire al Largest Contentful Paint senza latenza aggiunta. Su una landing di e-commerce con 60 card prodotto ho misurato:
Oltre ad auto, la proprietà accetta visible (il default) e hidden. La modalità hidden è più aggressiva: skippa sempre il rendering, non solo fuori dal viewport. È l'ideale per pannelli, tab e accordion nascosti, dove display: none distruggerebbe lo stato interno del sottoalbero.
.tab-panel[aria-hidden="true"] {
content-visibility: hidden;
/* Il DOM è preservato, lo stato React/Vue rimane, il paint è skippato */
}
Combinato con l'attributo HTML hidden="until-found" (supportato in Chromium dal 2022, in Firefox 131 dal 2024) permette al browser di rivelare automaticamente sezioni collassate quando l'utente usa Ctrl+F. È particolarmente utile per FAQ e documentazione: il contenuto rimane trovabile ma non pesa sul rendering iniziale.
<details hidden="until-found">
<summary>Sezione FAQ</summary>
<p>Contenuto skippato ma indicizzabile e cercabile...</p>
</details>
Accessibilità, SEO e come Google la vede
Una domanda che ricevo spesso: «se il contenuto non è renderizzato, Googlebot lo vede?». Sì. content-visibility: auto è puramente una direttiva di rendering visuale: il DOM è completo, l'accessibility tree è popolato, e i crawler processano tutto normalmente. Google ha confermato più volte via chrome.dev che la proprietà non ha effetti negativi sull'indicizzazione.
Per gli screen reader vale lo stesso: NVDA, JAWS e VoiceOver vedono il testo di elementi con content-visibility: auto perché il contenuto è nel DOM. L'unica cosa che manca è il rendering pixel, e agli utenti di screen reader non serve.
Con content-visibility: hidden invece l'elemento viene rimosso dall'accessibility tree, quindi è equivalente a display: none dal punto di vista dell'assistive tech. Usalo consapevolmente: pannelli nascosti sì, sezioni «legittime» no. Per approfondire, la documentazione MDN su content-visibility spiega bene tutte le implicazioni sull'accessibility tree.
Come misurare l'impatto reale di content-visibility
Il tuo Lighthouse score potrebbe non mostrare l'intero beneficio: Lighthouse gira su Moto G4 emulato, ma i benefici crescono con la dimensione del viewport e con la complessità del layout. Ecco la strategia di misurazione che uso di solito.
1. Chrome DevTools Performance Insights
Apri DevTools, poi Performance, poi Record. Dopo lo stop, cerca l'evento Layout nella timeline. Con content-visibility: auto ben applicato, il primo Layout dovrebbe elaborare solo i nodi above-the-fold. La differenza è visualmente ovvia nel flame chart.
2. Rendering flag «Show content-visibility»
In DevTools, sotto More tools → Rendering, attiva «Show content-visibility». Chrome disegna un overlay giallo sugli elementi attualmente skippati. È il modo più veloce per capire se le tue regole sono efficaci mentre scorri la pagina.
3. Long Animation Frames API in produzione
Usa la Long Animation Frames API per catturare regressioni reali. Se dopo aver rimosso content-visibility vedi picchi di scripting/rendering che non c'erano prima, hai un segnale forte.
// Snippet RUM per misurare rendering pesante
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.renderStart - entry.startTime > 50) {
// Rendering task lungo: candidato per content-visibility
navigator.sendBeacon('/rum', JSON.stringify({
name: 'long-render',
duration: entry.duration,
url: location.pathname,
}));
}
}
}).observe({ type: 'long-animation-frame', buffered: true });
Anti-pattern e trappole comuni
Negli ultimi due anni ho visto ripetere gli stessi errori. Ecco i cinque più costosi.
1. Dimenticare contain-intrinsic-size
È la trappola numero uno. Senza contain-intrinsic-size, tutti gli elementi non renderizzati collassano a 0, l'altezza totale della pagina precipita, e la scrollbar sparisce e riappare mentre scorri. Il CLS esplode. Se non conosci la dimensione, usa una stima ragionevole: meglio 500px sbagliati di 0.
2. Applicarla a elementi troppo piccoli
Applicare content-visibility: auto a ogni <p> di un articolo non aiuta, e può anche danneggiare. Il browser deve tracciare la visibilità di ciascuno: se sono cento paragrafi da 40px, l'overhead di tracking eccede il beneficio del rendering pigro. Limita il pattern a blocchi grandi (sezioni, card, wrapper).
3. Combinarla male con overflow scrollabile
Se applichi content-visibility: auto a un elemento dentro un contenitore con overflow: auto/scroll, il browser deve decidere se «visibile» significa nel viewport della pagina o nel viewport del contenitore. Le implementazioni divergono. Testa sempre in Chrome, Safari e Firefox prima di deployare.
4. Sicurezza con position: fixed/sticky interni
Il containment applicato da content-visibility crea un nuovo contenimento di layout. Un elemento position: sticky dentro un blocco skippato smetterà di funzionare come previsto. Se la tua pagina ha una TOC laterale sticky dentro una sezione con containment, sposta la TOC fuori.
5. Ignorare il caso «print»
Quando l'utente stampa la pagina, tutto è «nel viewport» e tutto viene renderizzato. Se stimavi male contain-intrinsic-size, la pagina di stampa può sembrare corrotta durante il calcolo iniziale. Usa una media query di stampa per disabilitare la proprietà:
content-visibility: auto funziona su Safari nel 2026?
Sì. Safari 18, uscito a settembre 2024, ha aggiunto il supporto completo a content-visibility: auto e alla forma moderna di contain-intrinsic-size: auto <fallback>. Nel 2026 la proprietà è cross-browser e non necessita più di fallback.
Qual è la differenza tra content-visibility: auto e loading="lazy"?
loading="lazy" ritarda solo il caricamento della risorsa (immagine o iframe). content-visibility: auto ritarda invece il layout e il paint di qualsiasi blocco HTML, indipendentemente da cosa contiene. Si combinano bene: puoi avere una card con immagine loading="lazy" dentro una sezione con content-visibility: auto.
content-visibility peggiora la SEO?
No. Googlebot processa il DOM completo indipendentemente da content-visibility, che è una direttiva puramente di rendering visuale. Google stessa raccomanda la proprietà per migliorare le Core Web Vitals, che sono un fattore di ranking.
Posso usare content-visibility al posto della virtualizzazione JavaScript?
In molti casi sì, e con meno complessità. Le librerie di virtualizzazione (React-Window, TanStack Virtual) rimangono superiori per liste con migliaia di elementi molto piccoli, ma per griglie di 50–500 card content-visibility: auto offre benefici simili senza JavaScript. Testa entrambi gli approcci con il tuo dataset reale.
Perché la scrollbar «respira» con content-visibility?
Perché stai stimando male contain-intrinsic-size. Ogni volta che un elemento entra nel viewport e viene renderizzato per la prima volta, la sua altezza reale sostituisce la stima, alterando l'altezza totale del documento. Usa contain-intrinsic-size: auto <fallback>: la parola chiave auto memorizza la dimensione reale dopo il primo layout ed evita il problema al secondo passaggio.
Marcus has spent the last 9 years on Core Web Vitals, mostly on the browser side. He worked at Cloudflare on the Workers team, shipping early-hints and 103 response support for the Pages product, and before that did two years at Vercel debugging Next.js hydration regressions across enterprise customers. He still maintains a small open-source library for measuring CLS on client-side route transitions, which he refuses to rewrite in TypeScript on principle.
His current obsession is third-party script governance: the embedded chat widgets, A/B testing tags, and CDP snippets that quietly destroy TBT on real devices. He consults part-time for two DTC brands and writes here about lab-vs-field discrepancies, the actual cost of a 200KB JS bundle on a Moto G Power, and why your synthetic Lighthouse score is lying to you.
Come costruire un RUM custom con PerformanceObserver per misurare LCP, INP e CLS sugli utenti reali: codice pratico, sendBeacon e attribuzione con LoAF.
Guida pratica alla View Transitions API cross-document per siti multi-pagina nel 2026: come attivarla con una regola CSS, personalizzarla con pageswap/pagereveal e combinarla con Speculation Rules senza impattare Core Web Vitals.
Configura stale-while-revalidate e stale-if-error per servire contenuti istantanei con TTFB vicino allo zero. Include esempi Cloudflare, Fastly, Vercel e Next.js 15.