# Core Web Vitals

## Що це таке

Документація Google на web.dev виокремлює з ширшої програми Web Vitals три
метрики й називає їх основними (core): Largest Contentful Paint для
завантаження, Interaction to Next Paint для чутливості до дій і Cumulative
Layout Shift для візуальної стабільності. Кожна з них описує те, що помічає
відвідувач, стосується будь-якої сторінки й піддається вимірюванню в полі, тобто
в браузерах справжніх відвідувачів.

Largest Contentful Paint (LCP) — це час від початку навігації до моменту, коли
відмальовано найбільше зображення, текстовий блок чи відео, видиме у вікні
перегляду. Добрим вважається значення до 2,5 с, поганим — понад 4 с.

Interaction to Next Paint (INP) стежить за кожним кліком, дотиком і натисканням
клавіші впродовж відвідин і бере все очікування, яке бачить відвідувач: затримку
до запуску обробників подій, час їхньої роботи й час до відмальовування
наступного кадру. INP сторінки — це найдовше з цих очікувань, причому на кожні
50 взаємодій одна найбільша відкидається. Прокручування, наведення курсора й
масштабування не враховуються. Добре — до 200 мс, погано — понад 500 мс. INP
замінила First Input Delay 12 березня 2024 року, після того як у травні 2022
року її запровадили як експериментальну метрику; First Input Delay вимірювала
лише затримку до початку обробки першої взаємодії.

Cumulative Layout Shift (CLS) оцінює видимі елементи, що зсуваються, хоча ніхто
цього не просив. Кожен зсув оцінюється як частка вікна перегляду, яку займають
зсунуті елементи, помножена на відстань, на яку вони зсунулися. Зсуви з
проміжками менш ніж секунду, загалом не довше п'яти секунд, утворюють вікно
сесії, і CLS — це сума найгіршого вікна. Зсуви протягом 500 мс після дотику,
кліку чи натискання клавіші не враховуються; прокручування й перетягування
нічого не виправдовують. Добре — до 0,1, погано — понад 0,25.

Вердикт зчитують на 75-му процентилі завантажень сторінки, окремо для мобільних
пристроїв і для десктопів, і сторінка проходить лише тоді, коли там добрі всі
три метрики. Межу «добре» приймали лише за умови, що її вже досягала щонайменше
десята частина вебджерел (origins).

В основі лежать браузерні інтерфейси з власними специфікаціями, API Largest
Contentful Paint, Layout Instability та Event Timing, і кількох рядків досить,
щоб побачити сировину для CLS на будь-якій сторінці:

```js
new PerformanceObserver((list) => {
    for (const shift of list.getEntries()) {
        if (!shift.hadRecentInput) console.log(shift.value, shift.sources);
    }
}).observe({ type: "layout-shift", buffered: true });
```

Цей код виводить у консоль окремі зсуви, а не саму метрику; щоб виміряти всі три
так, як їх подають інструменти Google, web.dev радить бібліотеку `web-vitals`.

Термін легко сплутати з оцінкою продуктивності Lighthouse, яку отримують з
одного змодельованого завантаження в лабораторії, де відвідувача немає; оцінка
Core Web Vitals описує реальні відвідини на 75-му процентилі, і сторінка може
мати добрий результат в одній і поганий в іншій.

## Чому це важливо

Визначення не кажуть, звідки беруться числа, і саме це сайтові доводиться
вирішувати: орієнтуватися на лабораторні замінники чи платити за польові дані.

Польові дані — це те, для чого написано межі. [Chrome User Experience Report](https://developer.chrome.com/docs/crux)
збирає знеособлені вимірювання від реальних користувачів Chrome і передає їх у
PageSpeed Insights та звіт Core Web Vitals у Search Console, і сайтові не треба
нічого додавати, але тільки тоді, коли сторінка чи вебджерело має достатньо
відвідувачів. Альтернатива — скрипт у браузері відвідувача, що надсилає кожне
вимірювання на сервер: це рішення про приватність і витрата на кожному перегляді
сторінки.

Лабораторні дані не коштують ні того, ні іншого. Lighthouse завантажує сторінку
на змодельованому пристрої та в змодельованій мережі, ще до того, як зміна
потрапить до відвідувачів, і стільки разів, скільки треба. Вимірює він
відвідини, яких ніхто не здійснював. Він бачить LCP і CLS під час завантаження,
але коли ніхто нічого не натискає, INP просто немає; web.dev пропонує Total
Blocking Time як лабораторний замінник і застерігає, що повноцінною заміною INP
він не є.

Найпрактичніше — мати обидва джерела, але тримати їх окремо: лабораторні числа
як бюджети, що ловлять погіршення, а польові, де вони є, як єдиний вирок тому,
що отримують відвідувачі.

## Як це виглядає на практиці

Запис у плані показує, що завдання з Core Web Vitals розглянули й відхилили:
Chrome UX Report потребує ключа API і достатнього трафіку для звіту, а сторінки
й так тримаються в межах бюджетів розміру у воротах якості. Власного збору
польових даних сайт теж не веде: сторінка подяк каже, що на ньому
[виконується один скрипт, близько кілобайта, і немає аналітики](/uk/credits/), і
цей скрипт, фільтр списку, — єдиний, що міг би затримати клік чи натискання
клавіші. Та сама сторінка радить читачам PageSpeed Insights, де видно
вимірювання з реальних відвідин, щойно сторінка має їх достатньо.

Натомість сайт вимірює лабораторію. Його конфігурація Lighthouse завантажує
фіксований список сторінок по три рази під змодельованим мобільним обмеженням
швидкості й повідомляє про провал, коли будь-яка з англійських чи данських
сторінок перетинає одну з цих меж: LCP понад 4 200 мс, First Contentful Paint
понад 1 600 мс, CLS понад 0,02 і Total Blocking Time понад 100 мс, а для
українських сторінок межі двох метрик відмальовування трохи ширші. Це бюджети, а
не польові межі, і їх записали після того, як виміряли розкид між запусками: 299
мс у найгіршому випадку й 80 мс у найкращому, бо межа всередині цього розкиду
спрацьовує навмання. Нотатки проєкту пояснюють більшу частину LCP затримкою
рендерингу через одну таблицю стилів, що блокує рендеринг. Вагу, яка надходить
раніше за будь-який вміст, уже було зменшено:
[пакет стилів важив 87 КБ із base64-шрифтом усередині й 31 КБ без нього](/uk/portfolio/cut-the-stylesheet-bundle-from-87kb-to-31kb-139/).
Запуски тривають хвилини, тож їх виконують на вимогу, поза воротами якості.

Бюджет CLS, уп'ятеро суворіший за межу 0,1, зловив випадок, який варто зберегти.
Одна сторінка категорії провалилася з результатом 0,107. Власниця відкрила її,
не побачила нічого поганого й так і сказала. Lighthouse назвав винуватцем
паралаксні шестерні, і перший письмовий діагноз йому повірив. Контрольований
тест зі справжнім прокручуванням коліщатком миші дав 0,2477 незалежно від того,
рухалися шестерні чи ні, а заголовок і останній абзац за весь час не зсунулися
ні на піксель. Причиною виявився футер: він закріплений відносно вікна перегляду
й змінює висоту, коли розгортається його колофон, а метрика оцінює цей блок
відносно вікна перегляду, хоча закріплений елемент нічого не може зсунути.
Бюджет лишили як є; якщо колись це число таки вирішать приглушити, план —
виняток для однієї цієї адреси.

## Де виникають проблеми

**Лабораторний звіт оминає INP, і сторінку вважають чутливою.** Lighthouse
показує LCP, CLS і низький Total Blocking Time, а повільні кліки на робочому
сайті стають несподіванкою. У змодельованому завантаженні ніхто не взаємодіє зі
сторінкою, а Total Blocking Time рахує лише блокування головного потоку під час
цього завантаження. Сприймайте його як сигнальну лампу, вимірюйте справжні
взаємодії вручну в інструментах продуктивності браузера й оцінюйте INP за
польовими даними, де вони є.

**Елемент, на який указує інструмент, виявляється невинним.** Звіт називає
вузол, відповідальний за зсув, зміна цього вузла не змінює оцінку ні на йоту, а
текст на екрані стоїть на місці. Атрибуція називає вузли, а не причини; зсув
може належати закріпленому шару, який змінює висоту, а це вважається рухом і не
виправдовується прокручуванням. Вимикайте підозрюваних по одному, перевіряйте,
чи рухається вміст, і анімуйте розмір через `transform`, який радить web.dev, бо
він не спричиняє зсуву макета.

**Вміст стрибає вниз, поки завантажуються зображення й пізні елементи.** Читачі
губляться в тексті, а польовий CLS піднімається понад 0,1. web.dev називає
типові причини: зображення й відео невідомого розміру, вебшрифти, що
відмальовуються більшими чи меншими за запасний шрифт, і віджети, які самі
змінюють свій розмір. Давайте кожному зображенню атрибути `width` і `height`,
щоб браузер зарезервував місце, як це роблять partial-шаблони зображень цього
сайту, і резервуйте місце для всього, що вставляється пізніше.

**Гарне середнє значення приховує провалену оцінку.** Панель моніторингу показує
середній LCP значно нижче 2,5 с, а сторінку все одно оцінено як таку, що
потребує покращення. Оцінка спирається на 75-й процентиль для кожного типу
пристроїв, і повільна чверть мобільних відвідин зникає в середньому, до якого
входять і швидкі десктопи. Звітуйте й ставте цілі за 75-м процентилем, окремо
для кожного типу пристроїв.

## Що почитати далі

- [Web Vitals](https://web.dev/articles/vitals) — три метрики, їхні межі й
  інструменти, які про них звітують.
- [Defining the Core Web Vitals metrics thresholds](https://web.dev/articles/defining-core-web-vitals-thresholds)
  — чому саме 75-й процентиль і як перевіряли кожну межу.
- [Interaction to Next Paint](https://web.dev/articles/inp) — що вважається
  взаємодією і чим INP відрізняється від First Input Delay.
- [Largest Contentful Paint API](https://wicg.github.io/largest-contentful-paint/)
  — які елементи стають кандидатами і коли фіксується їхній час.
- [Layout Instability API](https://wicg.github.io/layout-instability/) — оцінка
  зсуву макета, нещодавнє введення і джерела кожного зсуву.
- [Event Timing API](https://w3c.github.io/event-timing/) — як затримка
  взаємодій стає доступною для скриптів.

<https://engineer.company/uk/glossary/core-web-vitals/>
