CLS (Cumulative Layout Shift) — величина самой большой вспышки неожиданных сдвигов вёрстки за жизнь страницы. Сдвиг — видимый элемент сменил стартовую позицию между двумя кадрами. Это не LCP (когда появился основной блок) и не отчёт CWV целиком. Метрика загрузки — соседний LCP. Как Search Console клеит группы и красит статус по худшей из трёх — Core Web Vitals. Хаб — техническое SEO.
Порог Search Central / CrUX на 75-м процентиле, мобильные отдельно от компьютера: ≤0,1 — «хорошо», ≤0,25 — «нужно увеличить скорость», >0,25 — «плохо». Ноль идеален, 0,1 выбран как достижимый: web.dev прямо пишет, что 0,05 режет эмбеды, высоту которых не знают до загрузки. INP — следующий H1. Яндекс CLS в кабинете не считает.
Что суммируют, а что выкидывают
С июня 2021 CLS — не сумма всех сдвигов за сессию. Для бесконечной ленты и SPA старое определение росло без потолка. Сейчас: session window — сдвиги с промежутком меньше 1 с, окно не длиннее 5 с; в метрику идёт окно с максимальной суммой. 70% origin на 75-м процентиле не должны были увидеть изменение, остальные — улучшение (оценка Chrome Speed Metrics на момент смены).
| Событие | Входит в CLS? | Почему |
|---|---|---|
| Картинка без width/height, снизу уехал текст | Да | У текста сменилась стартовая позиция |
| Новый блок вставили в DOM, соседи на месте | Нет | Сдвиг только если уже видимые элементы поехали |
| Элемент сам вырос, start position тот же | Нет | Нестабильный — тот, у кого сменились top/left (в обычном письме) |
| Клик/тап/клавиша, сдвиг ≤500 мс | Нет | Флаг hadRecentInput; скролл, драг и pinch этим флагом не считаются |
Анимация через transform: translate/scale |
Нет | Не трогает layout; top/height — трогает |
| Рекламный iframe вырос после загрузки | Да | Типичный эмбед без зарезервированной высоты |
Оценка одного кадра: impact fraction × distance fraction. Impact — доля вьюпорта, которую заняли нестабильные элементы в этом и предыдущем кадре вместе. Distance — наибольший сдвиг по горизонтали или вертикали, делённый на большую сторону вьюпорта. Пример из web.dev: блок на половину экрана уехал вниз на 25% высоты → 0,75 × 0,25 = 0,1875. Невидимая часть за краем экрана в impact не входит. Раньше считали только impact — крупные блоки с малым ходом штрафовали слишком сильно, поэтому добавили distance.
Где ловят в поле, а лаборатория молчит
Лаборатория (Lighthouse, PSI Diagnose) видит сдвиги на загрузке. Живой CLS длится всю жизнь вкладки: баннер согласия, подгрузка ленты, шрифт, который перерисовал fallback крупнее, виджет чата на 30-й секунде. Поэтому поле почти всегда не ниже лаборатории. Смотрят CrUX / отчёт Core Web Vitals за ~28 дней. Свой RUM — onCLS из web-vitals: сырой layout-shift не группирует окна, врёт на bfcache и не видит сдвиги в iframe, которые метрика учитывает.
Типичный шаблон витрины. Герой без размеров бьёт и CLS, и поздний LCP. Шрифт без size-adjust / близкого fallback сдвигает абзац после swap. Cookie-баннер вставляют в body сверху без резерва — вспышка на первом экране. Сторонний эмбед: резервируют min-height под финальный кадр, иначе 0,05–0,1 на одном виджете. После клика «показать ещё» место под ответ лучше открыть сразу (скелетон), а не ждать API: иначе это уже не hadRecentInput, если ответ пришёл позже 500 мс.
CLS в норме, когда 75% полевых визитов шаблона держат вспышку ≤0,1. Нулевая лаборатория на пустом кэше этого не доказывает.
Связка с первым HTML: если баннер и герой уже в ответе сервера с явными размерами, сдвигать нечего. Пустая оболочка CSR, в которую потом падают блоки, копит окна сдвигов на каждом fetch — соседний CSR и SSR. Page Experience как широкий сигнал — Page Experience. Если в Console группа красная по CLS на всём каталоге, правят шаблон, не один URL: постановка SEO-аудита сайта.
Ограничение. Нет доли сайтов РФ с CLS >0,25 — не выдумываем. Нет формулы «минус N позиций за 0,26». Не складывайте индекс скорости Яндекса (открытия в Яндекс Браузере с мобильной выдачи) с 0,1 Google. Устаревшие отчёты, которые суммируют все сдвиги без окна 5 с, с текущим кабинетом не сравнивают.
Частые вопросы
Почему лаборатория показывает 0, а в Search Console CLS красный?
Lighthouse видит загрузку. Полевой CLS копит вспышки за всю жизнь страницы: баннер, лента, шрифт, эмбед, чат. Смотрят 75-й процентиль CrUX по группе, не разовый прогон.
Сдвиг после клика пользователя тоже портит CLS?
Если вёрстка поехала в пределах 500 мс после тапа, клика или клавиши — нет, стоит hadRecentInput. Скролл и жест масштаба этим флагом не считаются. Ответ API через секунду после клика — уже неожиданный сдвиг, место под него резервируют сразу.
Новый блок внизу страницы увеличивает CLS?
Сам по себе нет. В метрику попадают уже видимые элементы, у которых сменилась стартовая позиция. Вставка «в конец», которая никого не двигает во вьюпорте, нулевая. Вставка «над» списком, из‑за которой карточки прыгают — да.
CLS и LCP лечат одним сжатием картинки?
Нет. Сжатие бьёт по времени загрузки LCP. CLS с картинки лечат зарезервированным местом: width/height или aspect-ratio в первом HTML. Можно починить сдвиг и оставить поздний герой — это разные метрики.