A Cumulative Layout Shift (CLS) a Core Web Vitals egyik legalattomosabb metrikája. Tudod, az a klasszikus helyzet: épp olvasod a cikk második bekezdését, amikor egy késve betöltődő banner lenyomja a tartalmat, és véletlenül rákattintasz egy gombra, amelyikre egyáltalán nem akartál. Idegesítő, és sajnos mérhető — a Google ugyanis pontosan ezt méri.
2026-ban a küszöb változatlanul 0,1 alatt van, viszont a session windowing mérési modell és a friss Long Animation Frames API integráció miatt a szám manipulálása már tényleg nem opció. Őszintén? Ez jó hír: végre azt mérjük, amit a felhasználó is érez.
Ez az útmutató azt mutatja meg, hogyan diagnosztizálj, javíts és előzz meg layout shift problémákat valós produkciós oldalakon — elmélet helyett konkrét kóddal, a 2026-ban elérhető legfrissebb böngészőfunkciókkal.
Mi pontosan a CLS, és hogyan számítja a böngésző 2026-ban?
A CLS azt méri, hogy a látható tartalom mennyit "ugrál" az oldal élettartama során. A számítás két komponens szorzata:
- Impact fraction: a viewport hány százalékát érinti az elmozdult elem (az eredeti és új pozíció uniója).
- Distance fraction: az elmozdulás távolsága osztva a viewport hosszabb dimenziójával.
A 2021-ben bevezetett session window modell csoportosítja az 5 másodpercen belüli, egymástól legfeljebb 1 másodperces szünettel elválasztott shifteket. A teljes oldal CLS-e a legrosszabb session window összértéke — nem az összes shift összege. Ez azért lényeges, mert egy SPA navigáció után új session window indul (erre később még visszatérünk).
2026-os küszöbértékek
- Jó: CLS < 0,1
- Javítandó: 0,1 – 0,25
- Gyenge: > 0,25
A Chrome 128+ óta a LayoutShift performance entry tartalmazza a sources mezőt, amely megnevezi az elmozdult DOM elemeket. Ez — nem túlzás — forradalmasította a debug munkafolyamatot. Emlékszem, pár éve még brute force módszerrel, konzol logokkal kellett kideríteni, melyik blokk csúszik el; ma ez egy API hívás.
A 7 leggyakoribb CLS-okozó és pontos javításuk
1. Méret nélküli képek
A klasszikus eset: a <img> tag betöltődik, és a tartalom alá tolja a többit. A megoldás minden képhez explicit width és height attribútum, amiből a böngésző az aspect ratio alapján helyet foglal le már a betöltés előtt.
<img
src="/hero.avif"
width="1280"
height="720"
alt="Hero kép"
fetchpriority="high"
>
Reszponzív képeknél CSS-ben add meg az aspect-ratio-t:
img {
height: auto;
max-width: 100%;
aspect-ratio: attr(width) / attr(height);
}
Jó hír: 2026-ban az attr() függvény már nem-string típusokkal is működik a Chromium és Safari legújabb verzióiban. Firefoxban még fallback kell — de ez hamarosan változik.
2. Hirdetések és iframe-ek dinamikus mérete
A hirdetésszerverek gyakran nem ismerik előre a kiszolgált kreatív méretét (tipikus probléma). Foglalj le minimum magasságot a konténerben:
.ad-slot {
min-height: 250px;
contain: layout size;
content-visibility: auto;
contain-intrinsic-size: 300px 250px;
}
A contain-intrinsic-size arra is jó, hogy a content-visibility: auto off-screen elemeknél ne okozzon shiftet, amikor scrollolsz és aktiválódik a renderelés.
3. Webfontok és FOIT/FOUT
A betűtípus-csere kétféle layout shiftet okoz: amikor a fallback fontról a webfontra vált, a karakterek szélessége változik. 2026-ban a size-adjust, ascent-override, descent-override és line-gap-override kombinációja gyakorlatilag eltünteti ezt.
@font-face {
font-family: "Inter";
src: url("/fonts/inter.woff2") format("woff2");
font-display: swap;
size-adjust: 107%;
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}
@font-face {
font-family: "Inter Fallback";
src: local("Arial");
size-adjust: 107%;
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}
body {
font-family: "Inter", "Inter Fallback", sans-serif;
}
A pontos override értékeket a Capsize vagy a Google Fonts CSS API automatikusan generálja — ne számold fejben, kár az időért.
4. Késleltetett tartalom DOM-ba szúrása scroll fölött
Cookie banner, GDPR notice, A/B teszt variáns — mind klasszikus offender. A megoldás: helyfoglalás, vagy beillesztés a viewport alá. Ha mindenképp felül kell megjeleníteni, használj position: fixed vagy position: sticky overlay-t, amely nem tolja el a flow-t.
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 9999;
/* nem érinti a többi elem layoutját */
}
5. Animációk, amelyek nem transformot használnak
Ha a top, left, width vagy height tulajdonságot animálod, az layout shiftet okoz. Helyette mindig transform:
/* ROSSZ */
.element {
transition: top 0.3s;
}
/* JÓ */
.element {
transition: transform 0.3s;
will-change: transform;
}
6. Skeleton screenek hibás méretezése
Ha a skeleton placeholder magassága kisebb, mint a tényleges tartalom, a betöltéskor shift történik. Mérd meg a tipikus végleges magasságot, és állítsd be a placeholderre min-height-tal vagy aspect-ratio-val. Egyszerű, mégis sokan elrontják.
7. Late-loading komponensek lazy hidratációval
A 2026-ban népszerű Islands architecture (Astro, Qwik, Fresh) komponensei interakcióra hidratálódnak. Ha a komponens DOM-ja a hidratáció során változik (pl. új feliratokat ad hozzá), CLS-t generál. Mindig szerver oldalon renderelt, statikus markup legyen az alap, és a kliens csak event handlereket csatoljon.
Diagnosztika: a layout shift forrásainak pontos azonosítása
PerformanceObserver alapú in-production logging
Telepíts egy könnyű observert, amely a tényleges felhasználói shifteket küldi az analitika rendszerbe a problémás elem CSS selectorával együtt. Ezt a snippetet több projektemen is használom — egyszerű, és a RUM adat így beszélni kezd:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.hadRecentInput) continue;
const sources = entry.sources?.map((s) => ({
node: s.node ? cssPath(s.node) : 'unknown',
previousRect: s.previousRect.toJSON(),
currentRect: s.currentRect.toJSON(),
}));
navigator.sendBeacon('/rum/cls', JSON.stringify({
value: entry.value,
startTime: entry.startTime,
sources,
url: location.pathname,
}));
}
});
observer.observe({ type: 'layout-shift', buffered: true });
function cssPath(el) {
if (!(el instanceof Element)) return '';
const path = [];
while (el.nodeType === Node.ELEMENT_NODE) {
let selector = el.nodeName.toLowerCase();
if (el.id) { selector += '#' + el.id; path.unshift(selector); break; }
let sib = el, nth = 1;
while ((sib = sib.previousElementSibling)) nth++;
selector += `:nth-child(${nth})`;
path.unshift(selector);
el = el.parentNode;
}
return path.join(' > ');
}
Chrome DevTools Performance panel
A Chrome 130+ Performance paneljében az "Experience" track piros négyzettel jelzi a shifteket, és kattintásra megmutatja a moved from / moved to régiókat overlay-ként a viewportban. A "Layout Shift Culprits" insight pedig automatikusan rangsorolja a forrásokat — innen érdemes indulni.
web-vitals.js v4 integráció
import { onCLS } from 'web-vitals/attribution';
onCLS((metric) => {
console.log('CLS:', metric.value);
console.log('Largest shift target:',
metric.attribution.largestShiftTarget);
console.log('Largest shift time:',
metric.attribution.largestShiftTime);
console.log('Loaded state:', metric.attribution.loadState);
}, { reportAllChanges: true });
Az attribution mód megadja a legnagyobb shift forrását. A legtöbb esetben egyetlen sor kódot kell megváltoztatnod — és ami egy hétig vándorolt a backlogban, hirtelen tíz perces ticket lesz.
Speciális esetek: SPA-k, BFCache és View Transitions
Single Page Application navigáció
Az SPA-knál a route váltás nem indít új page load-ot, így a CLS akkumulálódhat. A web-vitals v4 a soft navigation támogatással külön session window-t hoz létre route váltás után — engedélyezd:
onCLS(callback, {
reportAllChanges: true,
reportSoftNavs: true,
});
View Transitions API
A natív View Transitions a régi és új DOM állapot közti átmenetet animálja, de nem számít layout shiftnek, mert a böngésző tudja, hogy ez tervezett:
document.startViewTransition(() => {
updateDOM();
});
A cross-document View Transitions 2026-ban már stabil Chromiumban — így MPA oldalakon is használható, egyszerű CSS-szel, a @view-transition at-rule-lal.
Back/Forward Cache
BFCache visszatöltés esetén a CLS-t a böngésző újraszámolja. Ha az oldal pageshow eseményen DOM-ot módosít, az shiftet okoz — kerüld, vagy halaszd el a viewport alá.
2026-os újdonságok: amit érdemes tudnod
- Long Animation Frames API: a
long-animation-frameperformance entry korrelálja a layout shifteket a hosszú frame-ekkel, így könnyebb azonosítani, melyik script okozta a problémát. - CSS
contain-intrinsic-size: auto: a böngésző megjegyzi a tényleges méretet, és visszatérő látogatáskor pontos placeholdert használ. - Priority Hints (
fetchpriority): a high-priority képek hamarabb töltődnek, csökkentve az LCP-shift kombinált kockázatot. - Speculation Rules prerender: prerendered oldalon a CLS már a felhasználó látogatása előtt "kiég", így a tényleges navigációkor nulla shift.
Lépésről lépésre: CLS audit munkafolyamat
- Futtass Lighthouse audit-ot mobil profillal (a CLS mobilon szigorúbb — ne felejtsd el).
- Telepítsd a fenti PerformanceObserver snippetet RUM endpointtal.
- Vizsgáld a CrUX (Chrome User Experience Report) p75 értékét a Search Console Core Web Vitals jelentésében.
- A top 3 shiftforrást javítsd: kép méretek, font override-ok, ad slot reservation.
- Mérj újra 28 napos CrUX ablakkal — a változás itt jelenik meg (türelem kell).
- A regresszió elkerüléséhez állíts be CI gátat (pl.
@lhci/cliassertioncumulative-layout-shift < 0.1).
Gyakori kérdések (FAQ)
Mennyi idő alatt változik a CLS érték a Search Console-ban a javítás után?
A CrUX adatok 28 napos gördülő ablakot használnak, így a teljes hatás körülbelül egy hónap után látszik. A field data 24-48 órán belül kezd javulni, viszont a p75 érték csak akkor stabilizálódik, amikor az új mérések teljesen kitöltik az ablakot.
Számít-e CLS-nek, ha a felhasználó kattintása után jelenik meg új tartalom?
Nem. A hadRecentInput flag 500 ms-on belül kizárja a shifteket, ha azokat felhasználói interakció előzte meg. Épp ezért fontos, hogy a saját PerformanceObserveredben is kihagyd ezeket az entry-ket.
Hogyan kezeljem a CLS-t végtelen scrollnál?
A felül beszúrt új tartalom shiftet okoz, az alulra fűzött viszont nem. Ha mindenképp felülre kell beszúrni (pl. új üzenetek egy chat appban), használj overflow-anchor: auto-t a konténerre — a böngésző automatikusan kompenzálja a scroll pozíciót.
Mi a különbség a layout shift és a content shift között?
A "content shift" laza terminológia bármilyen vizuális változásra. A "layout shift" konkrét specifikációval rendelkezik: a Layout Instability API csak akkor regisztrálja, ha egy látható elem előző és új pozíciója eltér, és nem tervezett animáció eredménye. A transform-alapú animációk és View Transitions nem számítanak.
Befolyásolja a CLS közvetlenül a Google rangsorolást 2026-ban?
Igen, a Core Web Vitals továbbra is rangsorjelzők. Mobilon a CLS a leginkább problémás metrika a legtöbb oldalon, és a "page experience" jelzés gyenge CLS esetén lefelé módosíthatja a pozíciót — különösen szoros versenyben lévő keresési kifejezéseknél.
Összefoglalás
A CLS javítása 2026-ban már nem jelent vakon böngésző DevTools-t. Az új sources mező, a Long Animation Frames API és a web-vitals attribution mód együtt percek alatt megmutatják a problémás elemeket.
A leggyakoribb okok kilencven százaléka négy kategóriába esik: méretezetlen képek és iframe-ek, betűtípus-csere, késleltetett DOM-beszúrás, valamint nem-transform animációk. A fenti munkafolyamatot követve a 0,1 alatti CLS reális, fenntartható cél — még ad-heavy, third-party-tartalommal teli oldalakon is.