Каскад: почему стиль «не работает»
Как браузер выбирает между спорящими правилами: важность, вес селектора, порядок. Плюс наследование и честное место !important.
Что ты научишься делать
- Посчитаешь вес любого селектора в уме
- Найдёшь причину, по которой твоё правило проигрывает чужому
- Переопределишь чужой стиль, не прибегая к !important
#Буква C в слове CSS
Cascading — «каскадные». Каскад — это правила, по которым браузер решает спор, когда два объявления задают одному элементу разное значение одного свойства.
Спор решается в три шага, строго по порядку. Как только на очередном шаге определился победитель, дальше браузер не идёт.
Важность
Объявление с
!importantпобеждает любое обычное — независимо от селектора и порядка.Специфичность
Побеждает правило с более «тяжёлым» селектором. Как считается вес — прямо ниже.
Порядок
Если важность и вес одинаковые, побеждает то правило, которое написано позже.
#Специфичность: три числа
Вес селектора — это три числа, которые сравниваются слева направо, как версии программы.
- Первое — количество
idв селекторе. - Второе — количество классов, атрибутов и псевдоклассов.
- Третье — количество тегов и псевдоэлементов.
Сравнение идёт по первому различию. Один id перевесит хоть двадцать классов: 1-0-0 больше, чем 0-20-0.
| Селектор | Вес | Почему |
|---|---|---|
* | 0-0-0 | Универсальный селектор веса не имеет |
p | 0-0-1 | Один тег |
.card | 0-1-0 | Один класс |
.card p | 0-1-1 | Класс и тег |
a:hover | 0-1-1 | Псевдокласс считается за класс |
input[type="text"] | 0-1-1 | Атрибут тоже считается за класс |
#hero | 1-0-0 | Один идентификатор |
#hero .card p | 1-1-1 | Всё вместе |
p::before | 0-0-2 | Псевдоэлемент считается за тег |
#Смотрим, как решается спор
#Наследование
Часть свойств передаётся детям автоматически. Задал color у body — его получили все абзацы, ссылки (нет, ссылки не получили, у них свой цвет), заголовки и списки внутри.
Наследуются в основном свойства, связанные с текстом: color, font-family, font-size, line-height, text-align, letter-spacing, visibility.
Не наследуются свойства, связанные с коробкой и оформлением: margin, padding, border, background, width, display. Иначе рамка у body появилась бы вокруг каждого абзаца.
Наследованием можно управлять вручную. Значение inherit заставляет свойство взять значение родителя, даже если оно обычно не наследуется. initial возвращает значение по умолчанию, unset — наследует, если свойство наследуемое, и сбрасывает, если нет.
/* Кнопка не наследует шрифт автоматически — вернём принудительно */button { font: inherit;}/* Ссылка в меню должна быть цвета текста, а не синей */.menu a { color: inherit;}#!important — и почему его не любят
!important выигрывает у всего. Именно поэтому он опасен: перебить его можно только другим !important с более тяжёлым селектором. Начинается гонка вооружений, и через месяц половина файла состоит из восклицательных знаков.
Есть ровно два случая, когда он уместен: перекрыть стили сторонней библиотеки, к которой нет доступа, и служебные правила вроде отключения анимаций для людей, которым движение мешает.
Так не надо
.card .title {
color: red !important;
}Проблема решена ударом кувалды. Следующий стиль для этого заголовка перебить уже не получится — только ещё одним !important.
Так надо
.card .title {
color: red;
}
/* или, если надо перебить чужое правило: */
.page .card .title {
color: red;
}Достаточно добавить один класс — вес вырос, правило победило, и его по-прежнему можно перекрыть в будущем.
#Практический вывод
Чтобы каскад не превращался в борьбу, держи вес селекторов низким и примерно одинаковым.
- Стили — на классы. Один класс на правило, если получается.
idв CSS не использовать: 1-0-0 против всего остального — это сразу неравный бой.- Цепочки — не длиннее двух уровней.
- Файл читается сверху вниз: сначала общее (сброс, типографика), потом компоненты, потом мелкие правки.
Где спотыкаются почти все
«Я написал правило, а оно не работает»
Почему Почти всегда его перебивает более весомое правило. Иногда — опечатка в селекторе или незакрытая скобка выше по файлу.
Как надо Открой DevTools и посмотри на элемент. Зачёркнутое свойство и есть ответ: рядом написано, какое правило победило.
Стили на id, потому что «так короче»
Почему Вес 1-0-0 нельзя перебить классами. Любая правка потребует ещё одного id или !important.
Как надо В CSS цепляйся за классы. id оставь якорям и label.
Селектор скопирован из DevTools целиком
Почему Браузер выдаёт что-то вроде body > div:nth-child(3) > ul > li:nth-child(2). Такое правило сломается при первой же правке разметки.
Как надо Дай элементу класс и напиши селектор сам.
!important как первое средство
Почему Это не решение, а откладывание проблемы. Через месяц перекрыть такой стиль будет нечем.
Как надо Сначала попробуй добавить класс или переставить правило ниже.
- Каскад cascade
- Порядок разрешения споров между правилами: важность, специфичность, порядок.
- Специфичность specificity
- Вес селектора в виде трёх чисел: id — класс — тег.
- Наследование inheritance
- Передача значения свойства от родителя к потомкам.
- inherit / initial / unset global values
- Ручное управление наследованием: взять у родителя, вернуть по умолчанию, или то и другое по ситуации.
- !important important
- Пометка, выигрывающая у обычных объявлений. Крайнее средство.
Проверь себя
Один класс и один тег: ноль идентификаторов, один класс, один тег.
Веса 1-0-1 против 0-2-1. Сравнение идёт по первому числу: один идентификатор перевешивает любые классы.
Наследуется в основном текстовое оформление. Отступы, рамки и фоны — нет, иначе они дублировались бы на каждом вложенном элементе.
DevTools покажет зачёркнутое свойство и правило-победителя. Дальше решение обычно очевидно.
<code>:where()</code> обнуляет специфичность. Такое правило легко перекрыть чем угодно — это его смысл.
Задание
Победить чужой стиль без !important
В файле уже есть «чужое» правило, которое красит заголовок карточки в серый и задаёт ему маленький размер. Трогать его нельзя — представь, что оно пришло из подключённой библиотеки.
Добавь ниже свои правила так, чтобы заголовок стал не меньше 24px и приобрёл другой цвет. Использовать !important и id запрещено.
Подсказка 1
Посчитай вес чужого правила: .card .card__title — это 0-2-0. Твоё должно быть не меньше и стоять ниже.
Подсказка 2
Добавь в цепочку класс родителя: .page .card .card__title { … } — получится 0-3-0.
Показать решение
<div class="page"> <article class="card"> <h2 class="card__title">Раф на кокосовом</h2> <p class="card__text">Мягкий, сладкий, без коровьего молока.</p> </article></div>/* Чужая библиотека — не трогай этот блок */.card .card__title { color: #9aa0ad; font-size: 15px;}/* Твои правила ниже */.page .card .card__title { color: #6544e0; font-size: 26px;}