HTML & CSS

Rendering Pipeline і CSS Performance

Як браузер перетворює CSS у пікселі: Critical Rendering Path, Layout → Paint → Composite, властивості що спричиняють reflow та repaint, GPU-прискорення через transform та opacity, will-change, content-visibility та CSS Containment.

Rendering Pipeline і CSS Performance

Між кодом і пікселями

Ви написали CSS. Браузер read DOM та CSSOM. Але між вашим кодом і тим, що бачить користувач на екрані, — складний конвеєр із десятків кроків. Розуміння цього конвеєру — це різниця між інтерфейсом на 60fps і тим, що «чомусь підгальмовує».

Chrome Timeline, Firefox Performance Panel, Safari Web Inspector — все це інструменти для аналізу rendering pipeline. Але щоб читати їх результати, потрібно розуміти, що відбувається всередині.

Loading diagram...
flowchart LR
    A["HTML"] --> B["DOM"]
    C["CSS"] --> D["CSSOM"]
    B & D --> E["Render Tree"]
    E --> F["Layout\n(Reflow)"]
    F --> G["Paint\n(Repaint)"]
    G --> H["Composite\n(GPU Layer)"]
    H --> I["🖥️ Екран"]

    style F fill:#ef4444,color:#fff
    style G fill:#f59e0b,color:#fff
    style H fill:#10b981,color:#fff
    style I fill:#6366f1,color:#fff

Critical Rendering Path

Critical Rendering Path — це послідовність кроків від HTML-файлу до першого відмальованого кадру:

  1. Parse HTML → DOM (Document Object Model)
  2. Parse CSS → CSSOM (CSS Object Model)
  3. Merge DOM + CSSOM → Render Tree (лише видимі елементи)
  4. Layout (Reflow) — обчислення точних позицій та розмірів
  5. Paint (Repaint) — малювання пікселів для кожного шару
  6. Composite — об'єднання шарів і відправка на GPU

Ключова ідея: CSS блокує rendering — браузер чекає завантаження та парсингу CSS перед тим як малювати щось. Тому <link rel="stylesheet"> у <head> і <script defer> — стандартні практики crítikal path.


Три типи rendering операцій

Layout (Reflow) — найдорожча операція

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

CSS-властивості, що завжди спричиняють Layout (не використовуйте в анімаціях):

/* ❌ Ці властивості trigger layout при кожній анімації */
.bad-animation {
  transition: width 0.3s;      /* reflow! */
  transition: height 0.3s;     /* reflow! */
  transition: top 0.3s;        /* reflow! */
  transition: left 0.3s;       /* reflow! */
  transition: margin 0.3s;     /* reflow! */
  transition: padding 0.3s;    /* reflow! */
  transition: font-size 0.3s;  /* reflow! */
}
🔒localhost:3000

Paint (Repaint) — дорожче ніж Composite

Paint відбувається коли змінюються візуальні властивості, що не впливають на геометрію:

/* ⚠️ Ці властивості спричиняють paint (але НЕ layout) */
.medium-cost {
  transition: background-color 0.3s; /* repaint */
  transition: color 0.3s;            /* repaint */
  transition: box-shadow 0.3s;       /* repaint */
  transition: border-color 0.3s;     /* repaint */
  transition: border-radius 0.3s;    /* repaint (в деяких браузерах) */
}

Composite — тільки GPU

Composite — найдешевша операція. Браузер лише переміщує або трансформує вже намальований шар, не перераховуючи геометрію і не перемальовуючи:

/* ✅ Тільки composite — анімуйте лише це! */
.cheap-animation {
  /* transform + opacity — БЕЗКОШТОВНО у compositor thread */
  transform: translateX(100px);   /* composite */
  transform: translateY(-50%);    /* composite */
  transform: scale(1.1);          /* composite */
  transform: rotate(45deg);       /* composite */
  opacity: 0.5;                   /* composite */
  filter: blur(4px);              /* composite (у більшості браузерів) */
}

GPU шари та will-change

Як створити GPU шар

Браузер автоматично переносить деякі елементи на окремий GPU шар (composite layer):

  • Елементи з position: fixed/sticky
  • <video>, <canvas>, <iframe>
  • Елементи з CSS animations/transitions на transform/opacity
  • Елементи з will-change
🔒localhost:3000

will-change — обережно!

/* ✅ Правильне використання: задаємо при hover, знімаємо після */
.card {
  transition: transform 0.3s;
}

.card:hover {
  will-change: transform;
  transform: translateY(-4px);
}

/* ❌ Неправильно: ніколи не знімається — пожирає GPU пам'ять */
/* Не ставте will-change на ВСІХ елементах "про запас" */
* { will-change: transform; } /* Це ПОГАНО */

/* ✅ Правильно: лише на елементах, що БУДУТЬ анімовані */
.animated-card { will-change: transform, opacity; }
will-change — турбонаддув: корисний в точних місцях, але невиправдано великий — вбиває. Кожен will-change споживає GPU пам'ять. На мобільних пристроях із 2–4 ГБ RAM це критично.

CSS Containment

CSS Containment дозволяє розробнику сказати браузеру: «цей елемент незалежний від решти сторінки». Браузер може оптимізувати рендеринг, не перераховуючи решту документа при зміні всередині контейнера.

/* contain: layout — зміни геометрії не впливають на зовнішній layout */
.widget {
  contain: layout;
}

/* contain: paint — вміст не виходить за межі елемента */
.card {
  contain: paint; /* неявний overflow: hidden, ізоляція стеків */
}

/* contain: style — стилі не протікають назовні (обмежені сценарії) */
.isolated {
  contain: style;
}

/* contain: size — розмір не залежить від вмісту */
.fixed-box {
  contain: size;
  width: 200px;
  height: 300px;
}

/* Скорочення */
.optimized { contain: layout paint; }
.strict    { contain: strict; /* = layout paint size style */ }
.content   { contain: content; /* = layout paint style */ }

content-visibility — ліниве рендеринг

content-visibility — одна з найефективніших оптимізацій для довгих сторінок. Браузер пропускає rendering елементів поза viewport:

/* Скіпає layout, paint, composite для off-screen елементів */
.article {
  content-visibility: auto;
  contain-intrinsic-size: 0 300px; /* placeholder розмір до рендерингу */
}
🔒localhost:3000

Оптимізація: практичні правила

🔒localhost:3000

Практика

Рівень 1 — Базовий: аналіз CSS

Визначте, яку rendering операцію спричиняє кожна властивість:

/* Класифікуйте: Layout / Paint / Composite */
.a { animation: all 0.3s; transition: width 0.3s; }    /* ? */
.b { animation: all 0.3s; transition: color 0.3s; }    /* ? */
.c { transition: transform 0.3s, opacity 0.3s; }        /* ? */
.d { transition: box-shadow 0.3s, border-radius 0.3s; } /* ? */
.e { transition: top 0.3s, left 0.3s; }                 /* ? */

Рівень 2 — Оптимізація hover-картки

Беремо "погано" написаний компонент картки з hover-ефектом через top/left. Перепишіть його, використовуючи лише transform та opacity, додайте will-change правильно.

Рівень 3 — Performance-ready список

Побудуйте компонент «стрічки новин» з 100+ елементів:

  • content-visibility: auto + contain-intrinsic-size для кожного елемента
  • contain: content на кожній card
  • Усі hover-ефекти — лише через transform та opacity
  • Перевірте Performance Timeline у DevTools — FPS має бути ≥ 60

Підсумок

🔄 Layout (Reflow)

Спричиняється width, height, top, margin, padding, font-size. Найдорожча операція — зачіпає весь документ. Уникайте в анімаціях.

🖌️ Paint (Repaint)

background-color, color, box-shadow, border-color. Дорожча за Composite, але не перераховує геометрію. Мінімізуйте в хот-шляхах.

⚡ Composite (GPU)

transform, opacity, filter. Лише переміщення вже намальованих шарів на GPU. Єдина правильна ціль для 60fps анімацій.

🎯 will-change

Просить браузер підготувати GPU шар заздалегідь. Використовуйте точково — лише там, де анімація БУДЕ. Надмірне застосування вбиває GPU пам'ять.

📦 contain

contain: layout paint ізолює рендеринг компонента. Browserможе пропустити recalculation зовнішнього layout при зміні всередині. Ідеально для widgets.

👁️ content-visibility

content-visibility: auto — браузер пропускає rendering off-screen елементів. На сторінках з 1000+ елементів дає 4–5x прискорення first render.