Каскад, специфичность, наследование
Что такое CSS, как решается, какое правило побеждает, и что наследуется.
Обновлено
Что такое CSS
CSS (Cascading Style Sheets) описывает, как выглядит HTML. У CSS-правила есть селектор (кого) и объявления (что):
.card h2 { /* селектор */
color: navy; /* свойство: значение; */
margin-bottom: 1rem;
}Главная проблема CSS: на один элемент могут указывать несколько правил. Какое победит? Ответ — каскад.
Каскад — порядок принятия решения
Когда два объявления спорят за одно свойство, браузер сравнивает по порядку:
- Важность —
!importantпобеждает всё (избегайте его — это признак, что что-то не так). - Слой (
@layer) — побеждают слои, объявленные позже. Tailwind использует это внутри. - Специфичность селектора.
- Порядок в коде — при равенстве побеждает последнее правило.
Специфичность
Каждый селектор получает счёт вида (ID, классы, элементы). Сравнение идёт слева направо — один класс побеждает любое число элементов, один ID — любое число классов.
| Селектор | Счёт | Пояснение |
|---|---|---|
p |
(0, 0, 1) | один элемент |
.card |
(0, 1, 0) | один класс |
.card p |
(0, 1, 1) | класс + элемент |
nav a:hover |
(0, 1, 2) | псевдокласс считается как класс |
[type="email"] |
(0, 1, 0) | атрибут считается как класс |
#header .logo |
(1, 1, 0) | ID побеждает всё, что ниже |
style="..." |
выше всех | инлайн-стиль |
* |
(0, 0, 0) | универсальный, без счёта |
Особые случаи: у :where(...) специфичность 0 (удобно для базовых стилей, которые легко переопределить); :is(...) и :not(...) берут специфичность самого «сильного» аргумента.
Практическое правило: используйте классы, избегайте ID в CSS и !important. Тогда у всех правил похожий счёт, и порядок остаётся предсказуемым.
Наследование
Некоторые свойства автоматически передаются от родителя детям, другие — нет:
| Наследуются (обычно текст) | Не наследуются (обычно блок) |
|---|---|
color, font-*, line-height, text-align, visibility |
margin, padding, border, width, background, display |
Поэтому font-family на body применяется везде, а padding на body не появляется у каждого абзаца.
Можно задать явно: inherit (взять значение родителя), initial (значение по умолчанию), unset.
Виды селекторов
| Вид | Пример | Выбирает |
|---|---|---|
| элемент | p |
все абзацы |
| класс | .btn |
элементы с class="btn" |
| ID | #app |
элемент с id="app" |
| атрибут | [disabled], [href^="http"] |
по атрибутам |
| потомок | .card p |
p где угодно внутри .card |
| прямой потомок | .menu > li |
только прямых детей |
| соседний | h2 + p |
p сразу после h2 |
| псевдокласс | :hover, :focus-visible, :first-child, :nth-child(2n) |
состояние или позицию |
| псевдоэлемент | ::before, ::after, ::placeholder |
«виртуальную» часть элемента |
| реляционный | .card:has(img) |
родителя, который содержит что-то |
Частые ошибки
- Длинные селекторы (
.page .main .content .card .title) — их трудно переопределить. !important, чтобы «перебить» правило, — это скрывает настоящую проблему.- Стили на ID.
Коротко
- Побеждает:
!important→ слой → специфичность → порядок. - Специфичность = (ID, классы, элементы); используйте классы.
- Текст наследуется, блок — нет.