CLS на сайте: почему скачет вёрстка при загрузке и как исправить
Cumulative Layout Shift простыми словами: почему контент прыгает при загрузке и как удержать CLS меньше 0.1 на WordPress, Tilda и Битрикс.
Содержание
CLS растёт, когда контент подгружается после первой отрисовки и сдвигает то, что уже видно: куки-баннер сверху, картинка без размеров, шрифт, подменяющий запасной. Зелёная зона до 0,1. Семь причин дают примерно 90 процентов проблемы на обычном сайте.
Открываете сайт, начинаете читать заголовок, и в этот момент сверху падает баннер с куки-уведомлением, ниже подгружается рекламный блок, текст скачет вниз на 200 пикселей, и палец промахивается мимо нужной кнопки. Это и есть CLS. Метрика, которую Google и Яндекс ввели в Core Web Vitals не из любви к красивым цифрам, а ровно за это раздражающее поведение, на котором отваливаются клиенты.
CLS даёт меньшую долю в общей оценке Core Web Vitals по сравнению с LCP, но именно его проще всего починить за один заход. И именно он отвечает за чувство, что сайт собран криво, даже если он быстрый.

Что такое CLS и как он считается
CLS это Cumulative Layout Shift. Сумма сдвигов вёрстки, которые произошли неожиданно для пользователя за всё время сессии на странице. Каждый сдвиг весит как доля видимой области, помноженная на расстояние смещения. Чем больше прыгает контент в видимом окне, тем выше CLS.
Цель: CLS меньше 0.1. Зелёная зона. От 0.1 до 0.25 жёлтая, всё что выше красная. На красном Google ощутимо понижает позиции в мобильной выдаче, и Яндекс начинает учитывать поведенческие, как будто люди уходят раньше, что, как правило, и происходит.
Считаются только неожиданные сдвиги. Если пользователь нажал кнопку и в результате этого открылось меню или загрузилось ещё контента (это user-initiated), такой сдвиг в CLS не идёт первые 500 миллисекунд после взаимодействия. Поэтому раскрытие FAQ-аккордеонов обычно безопасно. Прыгающий хедер при первой загрузке нет.
Замер ведётся не только при первой загрузке. Это ключевой момент, который многие пропускают. Если ваш слайдер на главной перерисовывается каждые 5 секунд и каждый раз чуть смещает соседний блок, CLS копится за всё время визита, не только за первые секунды.
Где смотреть честные цифры
Несколько источников, по приоритету.
PageSpeed Insights. На главной странице pagespeed.web.dev. Внизу есть секция Core Web Vitals Assessment, и в ней есть CLS как field (по реальным данным Chrome User Experience Report) и как lab (по эмуляции).
Field-данные ценнее. Это что Google реально намерял у живых пользователей за последние 28 дней. Если у вас лабораторно CLS 0.05, а в поле 0.2, у вас проблемы, которые видят посетители, но не видит синтетический тест.
Lighthouse в Chrome DevTools. Открываете DevTools, вкладка Performance или Lighthouse, прогоняете аудит. Удобно для отдельных страниц.
Chrome Web Vitals Extension. Маленькое расширение, показывает метрики прямо в углу при прокрутке. Незаменимо при ручном тестировании.
Яндекс Метрика, раздел Технологии > Время загрузки. Показывает CLS среди прочих метрик, в реальных условиях ваших пользователей. Точность на больших сайтах, на маленьких выборка тонкая.
WebPageTest, бесплатный тариф. Прогоняет полную сессию загрузки, показывает waterfall и явно подсвечивает источники сдвигов с привязкой к конкретным элементам. Лучший инструмент для серьёзной диагностики.
Главные причины CLS на типичном сайте
Я регулярно делаю аудит мелких сайтов, и одни и те же 7 причин дают 90 процентов CLS. По убыванию частоты:
1. Картинки без width и height
Самая массовая причина. Браузер пока не загрузил картинку, не знает, какого она размера, и резервирует под неё ноль пикселей. Когда картинка пришла, она расталкивает соседний контент.
Проверка: в HTML атрибуты width и height обязательны для всех <img>. Не в стилях, а в атрибутах.
Лечение для разных движков: — Astro astro:assets сам ставит размеры из исходного файла. Используем <Image src={img} alt="..." />, а не голый <img>. — WordPress по умолчанию ставит размеры. Если у вас тема пишет <img> руками без атрибутов, переписывайте. — Tilda обычно ставит размеры, но в Zero Block иногда забывает. Проверяйте инспектором.
Дополнительно полезно ставить aspect-ratio в CSS:
img {
aspect-ratio: attr(width) / attr(height);
height: auto;
max-width: 100%;
}
Это удерживает соотношение даже при респонсиве и спасает от сдвига при медленной сети.
2. Шрифты, подменяющиеся на середине загрузки
Браузер сначала рендерит текст системным шрифтом, потом загружает кастомный, и всё перерисовывается. Если у системного и кастомного шрифта разные ширины символов и высоты, текст смещается, иногда сильно.
Лечение комплексное:
font-display: optional для самых критичных шрифтов. Браузер ждёт 100 миллисекунд, и если шрифт не пришёл, рендерит фоллбеком и больше не подменяет в эту сессию.
font-display: swap это хуже, потому что подмена всё равно происходит, и CLS набегает.
Лучший вариант: размер-метрики через size-adjust и ascent-override в @font-face. Подбираем фоллбек так, чтобы символы занимали ровно ту же ширину, что кастомный шрифт. Тогда подмена визуально незаметна:
@font-face {
font-family: 'Manrope Variable';
src: url('/fonts/Manrope.woff2') format('woff2');
font-display: swap;
size-adjust: 102%;
ascent-override: 92%;
descent-override: 26%;
line-gap-override: 0%;
}
Метрики подбираются под конкретный шрифт. Для популярных есть утилита fontaine (npm), которая считает их автоматически.
3. Реклама и счётчики, врезающиеся в контент
Рекламный блок отрендерился через 2 секунды и подвинул статью вниз. Классика.
Лечение: всегда резервируйте место под рекламу заранее. Если у вас баннер 300×250, делайте обёртку с фиксированной высотой 250 пикселей до загрузки скрипта:
<div class="ad-slot" style="min-height: 250px; max-width: 300px;">
<!-- сюда придёт реклама -->
</div>
Тот же приём для всех виджетов: чат JivoSite, формы обратного звонка, виджет VK, отзывы Яндекса.
4. Слайдеры и карусели
Самая частая ошибка с слайдером это пока не загрузился JS, слайды лежат стопкой друг под другом. Когда JS пришёл, он схлопывает их в один слайд, и страница становится короче на 1500 пикселей.
Лечение: в стилях задавайте контейнер слайдера фиксированной высотой, а слайды кроме первого скрывайте через display: none или position: absolute сразу до JS.
.slider {
height: 480px;
overflow: hidden;
position: relative;
}
.slider .slide {
position: absolute;
inset: 0;
}
.slider .slide:not(:first-child) {
display: none;
}
Когда JS загрузится, он управляет слайдами уже в готовом контейнере. CLS не растёт.
5. Куки-баннер сверху и снизу
Куки-баннер показывается всем впервые зашедшим. Если он появляется через секунду после загрузки и сдвигает контент, CLS прыгает у каждого нового пользователя.
Лечение: рендерить баннер так, чтобы он был фиксированно поверх контента (position: fixed), и не двигал его. Если очень хочется, чтобы баннер был в потоке (например, требование 152-ФЗ показать перед использованием), резервируйте под него высоту в верхней или нижней полосе сразу при первой отрисовке.
Я обычно делаю баннер position: sticky снизу с прозрачным фоном, тогда он не закрывает критичный контент и не сдвигает страницу.
6. Динамические формы и валидация
Поле формы показало ошибку под собой и расширилось на 30 пикселей. Кнопка Submit опустилась, пользователь нажал в пустоту. Знакомо.
Лечение: место под сообщения об ошибках резервируется заранее минимальной высотой, даже если ошибки нет.
.form-row .error {
min-height: 18px;
font-size: 13px;
}
Тот же принцип для подсказок, для счётчиков символов, для индикаторов прогресса.
7. Embedded видео и iframes без размеров
YouTube embed, видео из VK, виджет Яндекс Карт без явных размеров занимают ноль пикселей до загрузки и потом разворачиваются. Особенно неприятно с картами, потому что они часто в середине страницы.
Лечение: контейнер с aspect-ratio: 16 / 9 или фиксированной высотой, iframe внутри position: absolute или width: 100%; height: 100%.
<div class="embed-16-9">
<iframe src="..."></iframe>
</div>
.embed-16-9 {
aspect-ratio: 16 / 9;
position: relative;
}
.embed-16-9 iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
}

CLS после первого экрана: про что забывают
Сейчас расскажу про вещь, которую часто пропускают. CLS считается за всю сессию, не только за загрузку. Если у вас на странице есть элемент, который перерисовывается раз в несколько секунд (бегущая строка с акциями, обновляющийся курс валют, тикер новостей), он каждый раз чуть-чуть двигает соседний контент. Этот сдвиг тоже идёт в CLS.
Лечение: фиксируйте размер любого регулярно меняющегося контейнера. Курс валют пусть всегда занимает 80 пикселей по ширине, даже если число короче. Бегущая строка пусть всегда 32 пикселя по высоте, даже если в ней одна короткая фраза.
Особенный случай: бесконечная подгрузка ленты (типа Pinterest или Хабра). Тут CLS возникает, если новые карточки приходят и расталкивают существующие. Лечение: подгружать только вниз, никогда не вставлять между уже отрисованными карточками.
Чек-лист для аудита своей страницы
Прогоните страницу по этому списку, внимание тут окупается.
- Все
<img>имеют атрибутыwidthиheight. - Все
<iframe>обёрнуты в контейнер с зарезервированной высотой илиaspect-ratio. - Подключаемые шрифты используют
font-display: optionalдля критичных илиfont-display: swapс подобраннымsize-adjust. - Под все рекламные блоки и виджеты зарезервирована минимальная высота через
min-height. - Слайдеры и карусели стартуют в правильном контейнере, до загрузки JS.
- Куки-баннер не сдвигает основной контент.
- Поля формы резервируют место под сообщения об ошибках.
- Элементы, которые обновляются по таймеру, имеют фиксированные размеры.
- Динамическая подгрузка идёт только в конец списка, не в середину.
Если все 9 пунктов выполнены, CLS меньше 0.1 практически гарантирован.
Особые случаи по CMS
WordPress
Самые частые источники CLS в WP-сайтах:
- Темы, которые рисуют
<img>через свои функции и теряют размеры в процессе. Лечится правкойfunctions.phpили сменой темы. - Плагины кеширования с lazy-load, которые подменяют
<img>на пустые<div>до загрузки. Хороший lazy-load делает это с резервом высоты, плохой нет. Проверяйте перед использованием. - Слайдер Slider Revolution со стандартными настройками может давать большой CLS на мобильных. Решается через жёсткий
min-heightдля контейнера слайдера. - Виджеты тёмной темы или подключения внешних шрифтов в footer, которые догружаются при скролле и сдвигают контент.
Tilda
В Tilda по умолчанию вёрстка на абсолютном позиционировании, и часть проблем с CLS не возникает технически, потому что блоки сидят жёстко. Но:
- Zero Block с галереями или каруселями часто страдает теми же проблемами, что и WP.
- Подключение внешних виджетов через
T123блок ломает CLS, если виджет догружается с задержкой. - Шрифты Tilda по умолчанию даёт через свой CDN, и подмена шрифта при загрузке регулярно даёт CLS на мобильных.
В большинстве случаев Tilda попадает в зелёную зону без вмешательства, но если показатель красный, начинайте именно с этих трёх причин.
Битрикс
Стандартные шаблоны Битрикса последних лет CLS-приличные, проблема обычно в:
- Кастомных компонентах, которые наследовались годами и не помнят про размеры картинок.
- Большом количестве баннеров и информеров на главной, которые подгружаются по скрипту.
- Слайдере главной страницы из стандартного шаблона, у которого минимальная высота не задана.
Здесь обычно требуется аудит шаблона, разовый, после которого CLS нормализуется на год вперёд.

Как тестировать после изменений
Простая методика, которой я придерживаюсь.
- До изменений снимаете три замера: PageSpeed Insights
field, Chrome Web Vitals Extension при ручном проходе, WebPageTest полный прогон. - Делаете изменения.
- После изменений снимаете те же три замера.
- Field-данные обновятся не сразу, дайте Google неделю на пересчёт.
- Lab-данные и WebPageTest показывают результат сразу.
Если lab улучшился, а field неделю стоит на месте, надо смотреть сегменты пользователей: возможно, мобильный трафик идёт со старых устройств с медленной сетью, и там проблема глубже, чем у вас в лабе. PageSpeed показывает разрезы по mobile и desktop в Core Web Vitals Assessment.
Когда CLS не лечится без переделки
Иногда страница построена так, что CLS красный системно: например, шаблон рендерит контент потоком, а сверху падают рекламные блоки разной высоты, в зависимости от ставок. Тут чинить точечно бесполезно, нужна переработка раскладки. Это уже не доработка, а проектная задача.
Признаки, что вам нужна именно переработка, а не правка:
- CLS больше 0.4 в
field-данных. - Источников сдвига в WebPageTest 5 и больше, и они на разных уровнях DOM.
- Шаблон сверстан до 2018 года и в нём нет CSS Grid/flex, всё на float.
- В коде шаблона десятки
setTimeoutиinnerHTML +=в JS.
В таких случаях я обычно ставлю клиенту вариант: точечно срезать CLS до 0.25 за пару дней, чтобы не падал в выдаче, и параллельно планировать переделку секции на горизонте месяца.
Что делать дальше
Если хотите делегировать аудит и правки CLS на маленьком сайте, типичный объём работ занимает от 4 до 12 часов, в зависимости от того, насколько широко надо лезть в шаблон. Это входит в любой из тарифов на /#pricing и часто решается в первый месяц обслуживания. Если хотите только разовый замер и список правок, опишите ситуацию через /#contact.
Связанные материалы про скорость собраны в /blog/category/speed/, там есть отдельные статьи про LCP, INP, шрифты и lazy-load. Если CLS у вас в порядке, а LCP большой, начинайте с раздела про оптимизацию первой отрисовки.
Частые вопросы
Что такое CLS простыми словами
Это мера того, насколько сильно прыгает вёрстка во время загрузки. Читаете заголовок, сверху падает баннер, текст уезжает вниз, палец промахивается мимо кнопки. Каждый такой сдвиг попадает в метрику.
Из-за чего скачет вёрстка
Картинки и видео без указанных размеров, баннеры и уведомления, которые вставляются сверху, реклама с плавающей высотой, подмена шрифта после загрузки и блоки, подгружаемые скриптом.
Какое значение CLS считается нормальным
До 0,1 зелёная зона, от 0,1 до 0,25 требует внимания, выше уже плохо. Смотреть надо на данные реальных посетителей за последние 28 дней, а не на разовый замер.
Как починить CLS без переделки сайта
Проставить ширину и высоту у картинок, зарезервировать место под баннеры и рекламные блоки, подключить шрифты так, чтобы подмена не меняла метрику строки. Это закрывает большую часть случаев.