Рано или поздно каждый новичок сталкивается с загадкой: «я написал стиль, а он не применяется». Почти всегда причина — в специфичности селекторов: другое правило оказалось «сильнее» и перебило ваше. Понимание того, как браузер выбирает, какой стиль победит, снимает эту головную боль раз и навсегда. А заодно вы научитесь выбирать элементы по вложенности — точечно, не плодя лишних классов.
После этого урока вы сможете осознанно комбинировать селекторы, понимать, почему один стиль перебивает другой, и держать специфичность под контролем.
Что уже умеем / что добавим
Вы умеете именовать классы по BEM. Теперь разберёмся, как браузер разрешает конфликты между правилами. В сквозном проекте мы аккуратно настроим стили ссылок внутри навигации, не задев остальные ссылки на странице.
Что вы узнаете в рамках статьи
- Как работает селектор потомков.
- Что такое каскад и как разрешаются конфликты.
- Что такое специфичность селекторов.
- Как сравнивается «вес» разных селекторов.
- Почему не стоит гнаться за высокой специфичностью.
Ключевые термины
- Селектор потомков — выбирает элементы внутри другого (
nav a). - Каскад — правила выбора, какой стиль применить при конфликте.
- Специфичность — «вес» селектора, определяющий его приоритет.
!important— пометка, принудительно повышающая приоритет правила.
Селектор потомков
Селектор потомков выбирает элементы, вложенные внутрь другого элемента. Записывается он через пробел: сначала внешний селектор, затем вложенный.
nav a {
color: #1a3c8c;
}
Это правило применится ко всем ссылкам a, находящимся внутри nav, — и не тронет ссылки в других местах страницы. Пробел здесь читается как «внутри»: nav a — «ссылки внутри навигации». Это удобно, когда нужно оформить элементы в определённом контексте, не давая каждому отдельный класс. Но злоупотреблять длинными цепочками (nav ul li a span) не стоит — они хрупкие и повышают специфичность.
Каскад: кто побеждает
CSS расшифровывается как Cascading Style Sheets — «каскадные таблицы стилей». Каскад — это набор правил, по которым браузер решает, какой стиль применить, если несколько правил претендуют на один элемент. Учитываются три вещи по порядку: важность, специфичность селектора и порядок в коде.
p { color: black; }
p { color: green; }
Здесь оба правила одинаково специфичны, поэтому побеждает последнее — текст станет зелёным. Правило «при равенстве выигрывает то, что ниже» — частый случай. Но когда селекторы разной силы, порядок уже не главное — решает специфичность.
Что такое специфичность
Специфичность — это «вес» селектора. Чем точнее селектор указывает на элемент, тем он весомее и тем вероятнее победит. Грубая иерархия от слабого к сильному: селектор по тегу (p) — самый лёгкий, класс (.card) — тяжелее, идентификатор (#id) — ещё тяжелее.
p { color: black; } /* тег — слабый */
.text { color: green; } /* класс — сильнее */
Если у абзаца есть класс text, он станет зелёным, даже если правило для тега p стоит ниже в коде: класс специфичнее тега и побеждает. Именно это чаще всего стоит за загадкой «стиль не применяется» — где-то есть более специфичное правило. Зная иерархию, вы сразу понимаете, кто кого перебьёт.
Держите специфичность низкой
Может показаться, что раз более специфичное правило побеждает, надо всегда писать «потяжелее». На деле — наоборот. Высокая специфичность делает стили негибкими: чтобы что-то переопределить, приходится городить ещё более тяжёлые селекторы, и код превращается в гонку вооружений. Крайний случай — !important, который перебивает почти всё:
.button {
color: white !important; /* так лучше не делать без крайней нужды */
}
!important кажется удобным, но он ломает нормальный каскад, и потом его самого невозможно переопределить. Хорошая практика — держать специфичность низкой и ровной: опираться в основном на одиночные классы (как в BEM), избегать #id в стилях и длинных цепочек, а !important не использовать без крайней необходимости. Тогда стили остаются предсказуемыми и легко переопределяются.
Применяем в проекте
Аккуратно оформим ссылки навигации, не задев другие ссылки на странице. Используем селектор потомков с одиночным классом — этого достаточно и специфичность остаётся низкой.
.nav a {
color: #1a3c8c;
text-decoration: none;
}
.nav a:hover {
text-decoration: underline;
}
Обновите страницу: ссылки внутри меню стали синими без подчёркивания, а при наведении подчёркивание возвращается. При этом ссылки в других местах — например, в подвале — сохранили свой обычный вид, потому что мы ограничили правило контекстом .nav. Мы добились точечного оформления, не раздувая специфичность и не плодя лишних классов.
Частые ошибки
- Загадка «стиль не работает».
Обычно есть более специфичное правило; проверьте, не перебивает ли его класс или#id. - Гонка специфичности.
Не переопределяйте стили всё более тяжёлыми селекторами; держите вес ровным и низким. !importantкак решение.
Он ломает каскад и сам не переопределяется; избегайте его без крайней нужды.- Длинные цепочки потомков.
nav ul li aхрупок и специфичен; чаще достаточно одиночного класса.
Итоги
Отличная работа — вы разгадали одну из главных загадок CSS! Теперь вы понимаете каскад и специфичность: знаете, почему класс перебивает тег, когда решает порядок в коде и почему !important — плохая идея. Вы умеете выбирать элементы по вложенности и держать специфичность низкой и предсказуемой. Это знание избавит вас от множества будущих «почему же стиль не применяется».
Вы отлично разбираетесь в тонкостях! В следующем уроке поговорим о переиспользуемых классах и разбиении стилей на логические части — чтобы CSS оставался чистым в любом масштабе.