Оптимізація CLS (Cumulative Layout Shift) для 1С-Бітрікс
CLS вимірює візуальну нестабільність сторінки: наскільки елементи зміщуються під час завантаження. Поріг Google — CLS < 0,1. Зміщення, яке змушує користувача промахнутися повз кнопку або втратити місце читання, — погана UX та фактор ранжування.
На сайтах Бітрікс CLS зазвичай становить 0,2–0,6. Винуватці передбачувані та усуваються систематично.
Як розраховується CLS
CLS = сума (дріб впливу × дріб дистанції) для кожного зміщення. Дріб впливу — частина екрана, на яку вплинуло зміщення. Дріб дистанції — на яку частину екрана переместився елемент. Зміщення зображення висотою 30% екрана вниз на 20% екрана дає 0,06 за один інцидент.
Вимір: Chrome DevTools → Performance → запис + прокрутка сторінки. Розділ Layout Shift показує кожне зміщення з вказанням елемента.
Зображення без ширини та висоти
Найпоширеніша причина CLS у Бітрікс. Коли браузер аналізує <img src="..."> без розмірів, він не знає, скільки місця займати. Контент під зображенням відмальовується без відступу, потім при завантаженні зображення все зміщується вниз.
Стандартні шаблони компонентів Бітрікс часто виводять зображення без атрибутів розміру. Виправлення в шаблоні компонента:
// Отримати розміри зображення при відмалюванні
$imgFile = \CFile::GetByID($arItem['PREVIEW_PICTURE'])->Fetch();
$width = $imgFile['WIDTH'] ?: 300;
$height = $imgFile['HEIGHT'] ?: 300;
echo '<img src="' . \CFile::GetPath($arItem['PREVIEW_PICTURE']) . '"'
. ' width="' . (int)$width . '"'
. ' height="' . (int)$height . '"'
. ' loading="lazy"'
. ' alt="' . htmlspecialchars($arItem['NAME']) . '">';
CSS aspect-ratio як альтернатива — коли розміри заздалегідь відомі та фіксовані:
.product-card__image-wrapper {
aspect-ratio: 4 / 3;
overflow: hidden;
}
.product-card__image-wrapper img {
width: 100%;
height: 100%;
object-fit: cover;
}
Тоді місце резервується через CSS, а конкретні розміри в HTML необов'язкові.
Динамічні блоки: кошик, лічильники, банери
Сайти Бітрікс часто мають динамічні блоки у шапці: кількість товарів у кошику, сума, кількість сповіщень. Ці дані завантажуються AJAX після відмалювання. Якщо під кошиком є контент — при завантаженні даних кошика він зміщується.
Резервування місця через CSS:
.header__cart-count {
min-width: 24px;
min-height: 24px;
display: inline-flex;
align-items: center;
justify-content: center;
}
Встановіть фіксовані розміри для всіх динамічних елементів шапки та сайдбара. Порожній елемент займає місце, після завантаження даних — розмір не змінюється.
Skeleton-завантаження замість порожнього блока: показує сірі плашки потрібного розміру до завантаження даних. Не усуває CLS, але поліпшує сприйняття.
Рекламні та банерні блоки
Рекламні блоки — класичне джерело CLS. Блок підвантажується через JS, вставляється в DOM та розздвигає контент. Рішення — завжди резервуйте місце під рекламу:
.ad-block {
min-height: 90px; /* стандартна висота банера */
width: 100%;
}
Якщо розмір банера непередбачуваний — використовуйте контейнер з фіксованим розміром та overflow: hidden.
Шрифти та FOIT/FOUT
Без настройки font-display веб-шрифти викликають два ефекти: FOIT (Flash of Invisible Text) — текст приховано до завантаження шрифту, або FOUT (Flash of Unstyled Text) — текст спочатку системним шрифтом, потім перемальовується користувацьким.
FOUT викликає CLS, якщо системний та користувацький шрифти мають різні метрики (розмір символу, висота рядка). Рішення:
@font-face {
font-family: 'SiteFont';
src: url('/bitrix/fonts/sitefont.woff2') format('woff2');
font-display: swap; /* FOUT замість FOIT */
font-style: normal;
font-weight: 400;
}
Для мінімізації CLS від FOUT — font-display: optional (шрифт використовується лише якщо вже в кеші) або підбір системного шрифту-fallback з подібними метриками через CSS size-adjust:
@font-face {
font-family: 'SiteFontFallback';
src: local('Arial');
size-adjust: 105%;
ascent-override: 90%;
}
Анімації та трансформації
Анімації через top, left, margin змінюють геометрію та викликають reflow — браузер перераховує розташування елементів, що фіксується як CLS. Використовуйте тільки transform та opacity для анімацій:
/* Викликає CLS — змінює геометрію */
.banner { transition: top 0.3s; }
/* Не викликає CLS — лише compositing */
.banner { transition: transform 0.3s; }
.banner.hidden { transform: translateY(-100%); }
Віджети зворотного зв'язку та чат-боти
Віджети типу JivoSite, CallTouch, Бітрікс24-чат підвантажують JS асинхронно та вставляють кнопку в DOM. Якщо кнопка з'являється поверх контенту — CLS. Варіанти:
- Зарезервувати місце під віджет через CSS placeholder
- Використовувати
position: fixedзbottom: 20px; right: 20px— fixed-елементи не викликають CLS - Відкласти завантаження віджету на
idleчерезrequestIdleCallback
Часовий графік оптимізації CLS
| Завдання | Час | Вплив |
|---|---|---|
| Додавання width/height до зображень у шаблонах компонентів | 1–3 дні | CLS −0,1–0,3 |
| Резервування місця для динамічних блоків | 1 день | CLS −0,05–0,15 |
| Настройка font-display | 0,5 дня | CLS −0,02–0,1 |
| Фіксація рекламних блоків | 0,5 дня | CLS −0,05–0,2 |
| Переведення анімацій на transform | 1–2 дні | CLS → 0 від анімацій |
Після виправлень — перевірте PageSpeed Insights за реальними даними. CrUX оновлюється з затримкою 28 днів — зміни метрики в реальному трафіку видно через місяць.







