CLS optimalizálás 2026: a Cumulative Layout Shift csökkentése a gyakorlatban
A Cumulative Layout Shift (CLS) csökkentésének gyakorlati útmutatója 2026-ra: aspect-ratio, font-display: optional, content-visibility és RUM mérés web-vitals.js v4-gyel. Valós kódpéldák képekhez, fontokhoz és SSR streaminghez.
A CLS optimalizálás 2026 egy egyszerű alapelven nyugszik: minden vizuális elemnek előre lefoglalt helye kell legyen, mielőtt a böngésző megfesti. A Cumulative Layout Shift (CLS) azt méri, mennyit ugrál a látható tartalom a betöltés és a felhasználói interakciók során. A Google szerint a "jó" érték 0,1 alatt van, a p75 percentilis CrUX adatok alapján. Backend szemszögből nézve a CLS gyakran nem CSS-probléma, hanem cache- és streaming-stratégia kérdése. Ha a HTML először size hint nélkül érkezik, a kliens kénytelen találgatni, és találgatni mindig drága.
A CLS küszöbértékek 2026-ban változatlanok: jó < 0,1, javításra szorul 0,1–0,25, gyenge > 0,25 a session window aggregálással mérve.
A layout shift fő okai: méret nélküli képek és iframe-ek, késleltetett betöltésű webfontok (FOIT/FOUT), dinamikusan beszúrt bannerek és later érkező hirdetések.
Az aspect-ratio CSS-tulajdonság és a width/height HTML-attribútumok együtt megakadályozzák a képek miatti elmozdulást.
A font-display: optional és preloadolt WOFF2 fájlok minimalizálják a webfont okozta CLS-t mobilon.
Az új content-visibility: auto és contain-intrinsic-size kombináció off-screen elemeknél is stabilan tartja a layoutot.
RUM (web-vitals.js v4) használata kötelező, mert a labor mérés (Lighthouse) elrejti a felhasználói interakciók okozta shifteket.
Mi az a Cumulative Layout Shift?
A Cumulative Layout Shift egy felhasználó-központú Core Web Vital metrika, ami a vizuális stabilitást méri. Konkrétan azt összegzi, hogy egy oldalon a látható elemek mennyit mozdultak el váratlanul (azaz nem felhasználói interakció hatására) egy úgynevezett session window alatt. Egymást követő shiftek 1 másodperces különbséggel, max. 5 másodperces ablakban. 2020 óta a Google a legnagyobb ilyen ablak értékét veszi figyelembe, és ez a változás kifejezetten a single-page application-öket érintette, ahol egy hosszú scrollozási session korábban indokolatlanul felpumpálta a CLS-t.
A metrika értékét két tényező szorzata adja: az impact fraction (mekkora terület érintett a viewport-hoz képest) és a distance fraction (milyen messzire mozdult az elem). Egy 50%-os impact és 30%-os distance shift például 0,15 layout shift score-t generál, és ez egyetlen ilyen esemény, ami már átlépi a "jó" küszöböt. A web.dev hivatalos CLS dokumentációja részletesen tárgyalja a számítási logikát.
Saját tapasztalatom szerint a legtöbb csapat azzal a téves hittel kezdi a CLS-t javítani, hogy ez egy "Lighthouse-szám", amit egyszer letudunk. Valójában a CLS folyamatosan keletkezik a felhasználói munkamenet alatt: scrollozás közben tölthető tartalom, interakcióra megnyíló accordion, dinamikus ár- vagy stock-info. Mindezek hozzájárulnak. Ezért a CLS optimalizálás 2026-ban már nem build-time feladat, hanem RUM-monitorozott, folyamatosan finomított munkafolyamat.
Hogyan mérjük a CLS-t 2026-ban?
Háromféle mérési forrást használj kombinálva: Lighthouse (labor), CrUX (real-world p75) és saját RUM web-vitals.js-szel. A web-vitals.js v4 GitHub repository standard API-t biztosít minden Core Web Vital eseményhez, beleértve a CLS attributiont is, ami megmondja, hogy konkrétan melyik DOM-elem okozta a shiftet.
A laborhoz a Chrome DevTools Performance panel "Layout shifts" sora vizuálisan kiemeli a problémás elemeket: pirossal jelölve a "before" pozíciót és kékkel az "after"-t. Az olyan eszközök, mint a PageSpeed Insights vagy a WebPageTest CrUX-integrációval rendelkeznek, így a 28 napos p75 mediánt is láthatod. Egy minimalista RUM beépítés így néz ki:
A sendBeacon azért fontos, mert a CLS gyakran csak az oldal elhagyásakor véglegesedik, és az unload idején a hagyományos fetch elveszhet. A részletekért lásd a RUM és web-vitals.js gyakorlati útmutatónkat.
A layout shift leggyakoribb okai 2026-ban
Az elmúlt két évben több ezer oldal RUM-adatát átnézve a CLS-probléma 90%-a hét forrásra vezethető vissza. Ezeket fontos azonosítani, mielőtt elkezdesz javítani, különben fix-a-fix után kerget a többi forrás.
Méret nélküli képek és videók, vagyis hiányzó width/height attribútum, ami miatt a böngésző 0 magasságot feltételez.
Webfont swap (FOUT), ahol a fallback és a custom font különböző cap-height-tal rendelkezik.
Hirdetések és embedded widgetek: YouTube, Twitter, hirdetésrendszerek, amelyek dinamikus magasságot kapnak.
Animations layout property-vel, ahol top, left, width animálása helyett transform-ot kell használni.
SSR/CSR hidratációs eltérések, amikor a szerveren rendert markup eltér a kliens által várt struktúrától.
Skeleton screen váltások, amikor a placeholder és valós tartalom mérete nem egyezik.
Hogyan akadályozzuk meg a képek miatti layout shiftet?
A képek miatti CLS-t a width/height attribútumok és az aspect-ratio CSS-tulajdonság kombinációja oldja meg. A modern böngészők (Chrome 88+, Firefox 89+, Safari 14+) az HTML attribútumok alapján automatikusan kiszámolják az aspect-ratio-t, így a CSS oldalon csak width: 100%; height: auto; kell. A 2026-os baseline támogatás 96% feletti minden modern böngészőben.
<!-- HELYES: a width/height meghatározza az aspect-ratio-t -->
<img src="/hero.avif"
width="1200" height="675"
alt="Hero kép"
style="width: 100%; height: auto;"
fetchpriority="high"
decoding="async" />
<!-- HELYTELEN: méret nélkül a böngésző 0×0-val kezd, majd reflow -->
<img src="/hero.avif" alt="Hero kép" />
Reszponzív képeknél, ahol a srcset különböző méreteket szolgál ki, az aspect-ratio CSS-tulajdonság hatékonyabb:
Az iframe-ek (YouTube embed, Twitter, hirdetések) ugyanezt a logikát követik, de itt a "facade pattern" hozzáadása dupla előnyt jelent, mert nemcsak a CLS-t, hanem az LCP-t és INP-t is csökkenti. A részletekért nézd meg a képoptimalizálási útmutatónkat, ami az AVIF/WebP konverziót és a srcset stratégiát is tárgyalja. A MDN aspect-ratio referenciája a böngészőfüggő szegélyeseteket is dokumentálja.
Webfontok és a font-display stratégia
A webfont okozta CLS-t (gyakran "font swap shift"-nek hívják) a fallback és custom font közötti metrikabeli különbség okozza. Ha az Inter font cap-height-ja 1116 unit (UPM-ben), a system-ui pedig 1024, a swap pillanatában minden szöveg átrendeződik. Egy 600 pixeles bekezdésen ez 0,15+ CLS-t is okozhat. Én ezt utoljára egy e-kereskedelmi projekten futottam neki, ahol egyetlen Inter swap önállóan a teljes CLS budgetet elvitte.
2026-ban három technika kombinálandó a stabil CLS-hez.
1. font-display: optional
A font-display: optional érték a böngészőnek azt mondja: ha 100 ms-en belül nem érkezik meg a font, használja a fallbacket és többé ne cseréljen. Ez nullára csökkenti a swap shiftet, mert a swap soha nem történik meg az első nézetben.
A size-adjust, ascent-override, descent-override és line-gap-override tulajdonságok lehetővé teszik, hogy a fallback fontot szinte pixelpontosan illesszük a custom fonthoz. A Fontaine és a Capsize könyvtárak automatikusan generálják ezeket az értékeket egy tetszőleges WOFF2 fájl alapján.
A cookie banner, GDPR popup, "subscribe to newsletter" sáv és A/B teszt overlay-ek a leggyakoribb dinamikus CLS-források. Az alapelv: soha ne tolj le tartalmat fent megjelenő bannerrel. Helyette használj fixed pozíciójú elemet alul, vagy egy modal overlay-t, ami nem mozgatja a háttér tartalmát.
/* HELYES: a banner kívül esik a normal flow-n */
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
}
/* HELYTELEN: a body-ba beszúrva lenyomja a tartalmat */
.cookie-banner-bad {
position: relative;
margin-bottom: 20px;
}
Hirdetésekre és A/B teszt slot-okra reserve magasságot CSS-ben:
A content-visibility: auto egy 2024-ben általánossá vált CSS-tulajdonság, ami a viewport-on kívüli elemeket "kihagyja" a layout fázisból. Önmagában azonban a böngésző 0-ra becsüli a magasságot, ami scrolling közben CLS-t okoz, ahogy a tartalom látható lesz. A megoldás a contain-intrinsic-size:
article.blog-card {
content-visibility: auto;
contain-intrinsic-size: auto 400px;
}
/* "auto" kulcsszó: a böngésző memorizálja a tényleges méretet */
.faq-section {
content-visibility: auto;
contain-intrinsic-size: auto 800px;
}
Az auto kulcsszó kritikus, mert enélkül minden viewport-újraszámolásnál visszaáll a placeholder mérete. Ez különösen hosszú blogcikkeken és terméklisztákon 30–40% rendering-időt is megspórolhat, miközben a CLS stabil marad.
SSR streaming és CLS, a backend szemszöge
Full-stack perf vezetőként számtalanszor láttam, hogy a frontend csapat órákat tölt CSS-fixekkel, miközben az igazi probléma a streamingben rejlik. A modern keretrendszerek (Next.js App Router, Remix, SvelteKit) HTML-darabokat streamelnek, és ha a kritikus layout-information (pl. méretadatok inline style-ban) később érkezik, mint az érintett DOM-elem, a kliens kétszer is layout-ot számol.
A megoldás: a kritikus CSS-t és az inline méreteket az első chunk-ban kell elküldeni, lehetőleg <head>-be inline-olva. Next.js-ben a generateMetadata és a layout-szintű CSS biztosítja ezt:
A CSS animációk akkor okoznak CLS-t, ha layout-property-ket animálnak: top, left, width, height, margin, padding. Ezek mindegyike reflow-t indít, ami layout shift-ként rögzítődik. A megoldás a transform és az opacity használata. Ezek a compositor thread-en futnak és NEM számítanak layout shiftnek a böngésző számára.
A will-change hint segíti a böngészőt, hogy előre dedikált compositor layer-t hozzon létre, így az animáció akár 60+ FPS-en futhat anélkül, hogy main thread-et terhelne. Ne használd túl sok elemre, mert minden layer memóriát foglal.
Hogyan debugolod a CLS-t Chrome DevTools-szal?
A Chrome DevTools Performance paneljén négy lépésben azonosíthatod a CLS-források mindegyikét.
Nyisd meg a Performance panelt, kapcsold be a "Web Vitals" opciót.
Töltsd újra az oldalt, görgess végig a tartalmon, várj 5–10 másodpercet az interakciók után is.
A timeline tetején keresd a piros "Layout Shift" markereket a Web Vitals sávban.
Kattints rá: az "Experience" szekció megmutatja az érintett elemet, az előtti és utáni rect-et, valamint a score-t.
Az új Performance Insights AI-asszisztens (Chrome 128+) automatikusan ajánlatot tesz fix-re, pl. "Add explicit width/height to img#hero". A parancssoros workflowhoz a Lighthouse CI is ad cls auditot, ami a CI-ban bukja meg a build-et, ha a CLS > 0,1.
Ha kíváncsi vagy az INP és CLS együttes mérésére interakciókkal terhelt oldalakon, nézd meg az INP optimalizálási útmutatónkat is. Tapasztalatom szerint a két metrika gyakran ugyanattól a problémától romlik (long task által késleltetett render), de a fix-ek különböznek. Érdemes mindkettőt párhuzamosan figyelni a RUM dashboardon.
Gyakran ismételt kérdések
Mi a jó CLS érték 2026-ban?
A "jó" CLS küszöb 0,1 alatt van, a "javításra szorul" 0,1 és 0,25 között, és a "gyenge" 0,25 felett. Ezt a p75 percentilisre kell mérni a CrUX vagy saját RUM adataidban, nem az átlagra. A küszöbök 2020 óta változatlanok és 2026-ban sem terveznek módosítást.
Miért rossz a CLS-em, ha Lighthouse-ban 0?
A Lighthouse csak az oldal betöltését szimulálja headless módban, interakciók nélkül. A valódi CLS nagy része user interaction utáni dinamikus tartalomból (modal, accordion, "Load more" gomb) származik, amit csak RUM web-vitals.js-szel mérsz a valódi user session-ökön.
A lazy-loaded képek CLS-t okoznak?
Csak akkor, ha nincs explicit width/height vagy aspect-ratio megadva. A loading="lazy" önmagában nem ok, hanem a méretinformáció hiánya az. Mindig kombináld a lazy loadingot intrinsic size hint-tel.
Hogyan csökkenthetem a CLS-t React/Next.js-ben?
A Next.js <Image> komponens automatikusan generálja a width/height-ot és reservál layout helyet. Kerüld a kliens oldali fetch utáni renderelést hero szekcióban, és használj Server Componenseket. A font preloadot a next/font kezeli automatikusan, beleértve a metric override-okat is.
Számít-e a sütibanner a CLS-be?
Igen, ha az oldalbetöltés első 5 másodpercében jelenik meg és lenyomja a tartalmat. Megoldás: position: fixed bottom banner vagy modal overlay, ami nem mozgatja a háttér tartalmat. A felhasználói "Elfogadom" kattintás után 500 ms-on belüli változások már nem számítanak.
Gyakorlati LCP útmutató 2026-ra: a TTFB, a render-blokkoló erőforrások, a fetchpriority és az AVIF képoptimalizálás, valamint a Soft Navigation LCP mérése SPA-kban. Élesben tesztelt példák és kódrészletek.
Hogyan állíts be saját RUM-ot web-vitals.js v5-tel: p75 percentilek, eszközosztály-szegmentáció, CrUX BigQuery lekérdezések és attribution build a valódi felhasználói teljesítmény méréséhez.
A Speculation Rules API JSON szabályokkal jelzi Chrome 121+ böngészőnek, mely URL-eket renderelje elő vagy töltse le előre. Tanuld meg a prerender, prefetch és eagerness beállításait gyakorlati kódpéldákkal a Core Web Vitals javításához.