$ sudo teach IT

Рано или поздно каждый новичок сталкивается с загадкой: «я написал стиль, а он не применяется». Почти всегда причина — в специфичности селекторов: другое правило оказалось «сильнее» и перебило ваше. Понимание того, как браузер выбирает, какой стиль победит, снимает эту головную боль раз и навсегда. А заодно вы научитесь выбирать элементы по вложенности — точечно, не плодя лишних классов.

После этого урока вы сможете осознанно комбинировать селекторы, понимать, почему один стиль перебивает другой, и держать специфичность под контролем.

Что уже умеем / что добавим

Вы умеете именовать классы по 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 оставался чистым в любом масштабе.

На следующий урок