Що таке каскад у CSS
Каскад у CSS - це алгоритм, який браузер використовує для вирішення конфліктів: коли кілька CSS-правил застосовуються до одного елемента, каскад вибирає переможне значення для кожної властивості окремо.
Теорія
TL;DR
- Каскад вибирає одне значення для кожної властивості з усіх відповідних правил, а не одне "переможне" правило повністю.
- Порядок: Походження > Важливість > Специфічність > Порядок у коді.
!importantзмінює пріоритет усередині кожного рівня походження, але не скасовує специфічність повністю.- Пізніше правило виграє лише при рівній специфічності.
- Правило може програти по
colorі при цьому виграти поfont-sizeна тому самому елементі.
Швидкий приклад
<style>
p { color: blue; } /* програє: нижча специфічність */
.highlight { color: red; } /* виграє: клас перемагає тег */
</style>
<p class="highlight">Цей текст червоний.</p> <!-- red -->
<p>Цей текст синій.</p> <!-- blue -->Обидва правила підходять для першого <p>. Каскад вибирає .highlight для color, бо клас (специфічність 0,0,1,0) перемагає тег (0,0,0,1). Конфлікту по інших властивостях немає, тому вони не змінюються.
Як каскад вирішує конфлікти
Браузер сортує всі відповідні декларації за таким порядком:
-
Походження і важливість. Стилі браузера (user-agent) мають найнижчий пріоритет. Твої авторські стилі стоять вище. Стилі користувача з розширень браузера або налаштувань ОС - ще вище.
!importantперевертає кожен рівень: author!importantперемагає звичайний author, user!importantперемагає майже все. -
Специфічність (specificity). Рахується як чотиричастинний кортеж: inline (1,0,0,0), ID (0,1,0,0), класи і атрибути і псевдокласи (0,0,1,0), теги і псевдоелементи (0,0,0,1). Порівнюй зліва направо.
#header a(0,1,0,1) перемагає.menu a(0,0,1,1), бо ID-колонка вирішує одразу. -
Порядок у коді. Якщо все решта рівне, перемагає декларація, яка стоїть останньою в документі.
Ключова ідея, яку часто не розуміють
Каскад не вибирає одне правило і не ігнорує решту. Він вибирає переможне значення для кожної властивості окремо. Правило з color: red; font-size: 16px може програти по color іншому правилу і при цьому внести font-size у підсумковий стиль. Саме на цьому злитті більшість розробників плутаються, бо очікують що одне правило "захопить" елемент повністю.
Коли що використовувати
- Простий сайт без конфліктів - тримай специфічність низькою і покладайся на порядок у коді.
- Конфлікти з бібліотекою компонентів - підніми специфічність через BEM-селектори типу
.card__button--primaryзамість!important. - Боротьба з чужим CSS -
!importantяк крайній засіб, не як перший хід. Inline-стилі в такому разі кращий короткостроковий варіант. - Система тем - шаруй таблиці стилів: базова, потім тема, потім налаштування користувача. CSS
@layer(підтримується у всіх основних браузерах з 2022 року) робить цей підхід явним. - Відлагодження дивних стилів - відкрий панель "Styles" у DevTools. Там видно кожну декларацію, яка перемогла, і чому решта перекреслені.
Як браузер обробляє це всередині
Blink (движок Chrome) парсить усі таблиці стилів у дерево каскаду, відсортоване за походженням. Під час layout браузер знаходить відповідні селектори для кожного елемента, рахує специфічність і проходить дерево. Найвища комбінація походження/важливості/специфічності/порядку виграє для кожної властивості. Значення без конфліктів зливаються в об'єкт підсумкового стилю. Саме цей крок злиття описаний у специфікації, але більшість туторіалів його пропускають.
Типові помилки
-
Вважати, що пізніше правило завжди перемагає
cssp { color: red; } /* стоїть першим, але виграє */ div p { color: blue; } /* стоїть пізніше, але програє */div pмає специфічність 0,0,0,2, аp- лише 0,0,0,1. Специфічність перемагає порядок. -
Використовувати
!importantяк швидкий фіксЯ бачив команди, які місяцями воювали з
!important, бо один розробник додав його до спільного компонента і тепер ніхто не міг нічого перевизначити нормально.css.btn { color: black !important; } /* тема тепер не може це змінити */Фікс: підніми специфічність.
.theme-dark .btn { color: white; }вирішить питання без отруєння всього далі по ланцюжку. -
Думати, що inline-стилі завжди виграють
html<p style="color: blue" class="override">Текст</p> <style>.override { color: red !important; }</style> <!-- Результат: red -->!importantперемагає inline-стилі. Лишеstyle="color: blue !important"поверне inline-значення. -
Ігнорувати стилі користувача
Розширення браузера вставляють CSS з рівня user-origin, який автоматично перевизначає твої авторські стилі. Якщо щось виглядає зламаним у браузері користувача, але у тебе все нормально - перевір розширення. DevTools дозволяє емулювати user stylesheet для тестування.
Де зустрічається у реальних проектах
- Tailwind CSS - утилітарні класи типу
bg-red-500перемагають базові стилі за специфічністю або порядком. Властивості без конфліктів з базових стилів при цьому застосовуються. - Bootstrap -
.btn-primary(клас плюс тег) перевизначає загальний.btn. - Styled Components / Emotion - генерує ізольовані класи, але каскад все одно працює між різними бібліотеками на одній сторінці.
- Material UI - CSS custom properties разом із каскадом обробляють перевизначення теми на рівні окремих властивостей.
Питання на співбесіді
Q: Який повний порядок у каскаді?
A: Спочатку походження і важливість (user-agent < user < author, !important перевертає кожен рівень), потім специфічність (inline > ID > клас/атрибут/псевдоклас > тег), потім порядок у коді.
Q: Як рахується специфічність?
A: Чотиричастинний кортеж (A, B, C, D): A дорівнює 1 для inline, B рахує ID, C рахує класи і атрибути і псевдокласи, D рахує теги і псевдоелементи. Порівнюй зліва направо. Комбінатори типу > і + нічого не додають.
Q: Що відбувається при рівній специфічності?
A: Для звичайних правил перемагає останнє в документі. Для !important навпаки: перемагає перше.
Q: Чи працює каскад так само всередині Shadow DOM?
A: Ні. Shadow DOM інкапсулює власний каскад. Конструйовані таблиці стилів (adopted sheets, Chrome 101+) зливаються в тіньовий каскад без впливу на light DOM. Inline-стилі на host-елементі при цьому перемагають !important у тіньових стилях, що часто дивує розробників Web Components.
Q: Як CSS Custom Properties взаємодіють із каскадом?
A: Кастомні властивості каскадують як будь-яка інша декларація. --color: red виграє за звичайними правилами каскаду. Підстановка color: var(--color) відбувається після того, як каскад вирішив значення змінної, а не до.
Приклади
Конфлікт специфічності зі злиттям властивостей
<!DOCTYPE html>
<html>
<head>
<style>
p { color: blue; font-size: 14px; } /* тег, специфічність 0,0,0,1 */
.highlight { color: red; } /* клас, специфічність 0,0,1,0 */
</style>
</head>
<body>
<!-- color: red від .highlight, font-size: 14px від p (конфлікту немає) -->
<p class="highlight">Червоний текст, 14px.</p>
</body>
</html>.highlight виграє color. Але font-size з p все одно застосовується, бо жодне інше правило його не оголошує. Обидва правила вносять свій внесок у підсумковий стиль. Ось що означає злиття на практиці.
Каскад у React + Tailwind
/* App.css */
.button { background: gray; padding: 8px; border-radius: 4px; }
/* Tailwind генерує: */
/* .bg-blue-500 { background-color: rgb(59 130 246); } */
function Button({ primary }) {
return (
<button className={`button ${primary ? 'bg-blue-500' : 'bg-gray-500'}`}>
Натисни
</button>
);
}
/* Результат для primary-кнопки:
background: rgb(59 130 246) від Tailwind (рівна специфічність, підключається пізніше)
padding: 8px від .button (конфлікту немає, зливається)
border-radius: 4px від .button (конфлікту немає, зливається) */Обидва .button і .bg-blue-500 - класові селектори з однаковою специфічністю. Tailwind виграє background за порядком у коді: його таблиця стилів підключається після твоєї. Padding і border-radius з .button зливаються, бо Tailwind їх не оголошує. Це стандартна поведінка Tailwind у Next.js-проектах.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.