CLS optimalizálás 2026: Cumulative Layout Shift csökkentése és Core Web Vitals javítása

Gyakorlati útmutató a Cumulative Layout Shift csökkentéséhez 2026-ban: kép- és font-méretezés, ad slot reservation, View Transitions, PerformanceObserver attribution és teljes audit munkafolyamat.

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

  • : 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-frame performance 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

  1. Futtass Lighthouse audit-ot mobil profillal (a CLS mobilon szigorúbb — ne felejtsd el).
  2. Telepítsd a fenti PerformanceObserver snippetet RUM endpointtal.
  3. Vizsgáld a CrUX (Chrome User Experience Report) p75 értékét a Search Console Core Web Vitals jelentésében.
  4. A top 3 shiftforrást javítsd: kép méretek, font override-ok, ad slot reservation.
  5. Mérj újra 28 napos CrUX ablakkal — a változás itt jelenik meg (türelem kell).
  6. A regresszió elkerüléséhez állíts be CI gátat (pl. @lhci/cli assertion cumulative-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.

A Szerzőről Editorial Team

Our team of expert writers and editors.