Вы сверстали страницу, пишете стили и вдруг понимаете, что что-то не так. Текст, который должен быть зелёным, почему-то остался красным:

<p class="text">Это просто текст</p>
.text {
  color: red;
}

p {
  color: green;
}

Всё просто, вы столкнулись со специфичностью селекторов: свойства не переопределились, потому что у .text больший вес. Давайте разбираться, что здесь происходит.

Что такое специфичность селекторов

Разработчики часто применяют к одному элементу сразу несколько CSS-правил. Если два свойства из разных правил конфликтуют, браузер смотрит на селекторы. Свойство из CSS-правила с более «сильным» селектором побеждает в конфликте.

💡 Специфичность определяет приоритет CSS-правил у разных селекторов.

Как распределяется специфичность:

  • Наибольшая специфичность у встроенных стилей.
  • Меньше — у селекторов идентификаторов.
  • Ещё меньше — у классов, псевдоклассов и селекторов атрибутов.
  • Самый низкий приоритет у селекторов типов элемента и псевдоэлементов.

«Перебить» специфичность встроенных стилей можно только одним способом — использовать модификатор !important. Но это плохая практика: такие стили почти невозможно переопределить. Разве что использовать более специфичный селектор и указать CSS-правило с ещё одним !important.

Как посчитать специфичность селекторов

Порой в коде встречаются сложные конструкции. Например, стили для элемента <li> в меню:

ul .list-item.list-active {
	color: blue;
}

ul #catalog.list-item {
	color: yellow;
}

Чтобы определить, какое из правил применится, надо посчитать вес селекторов. Чем больше вес, тем выше специфичность.

Вес записывается в виде a, b, c, d, где:

  • a — встроенные стили,
  • b — селектор по идентификатору,
  • c — селектор классов псевдоклассов и атрибутов,
  • d — селектор по тегу и псевдоэлементу.

Если в цепочке есть один из этих селекторов, то на его место в строке a, b, c, d ставится единица. Если нет — нуль.

Посчитаем на примере ul .list-item.list-active:

  • Встроенных стилей нет. Ставим нуль — 0, b, c, d.
  • Идентификатора тоже нет, добавляем на его место нуль — 0, 0, c, d.
  • Есть два класса, каждый весит единицу. Складываем их и подставляем в формулу: 0, 0, 2, d.
  • Тег один, ставим 1.

Получается, вес ul #catalog.list-item — 0, 0, 2, 1.

Теперь можно рассчитать вес ul #catalog.list-item. Здесь тоже нет встроенных стилей, но есть один идентификатор, класс и тег. Получаем 0, 1, 1, 1.

Селектор

a, b, c, d

ul .list-item.list-active

0, 0, 2, 1

ul #catalog.list-item

0, 1, 1, 1

Сравниваем вес селекторов — у второго он больше, поэтому пункт меню станет жёлтым.

💡 Сложно запомнить? Считайте специфичность в онлайн-калькуляторе.

Специфичностью можно управлять. Например, если вы хотите придать веса селектору, навесьте на него дополнительный тег или класс: .news **→ .news.main-news. Только не используйте длинные цепочки. Идеально, когда вложенность селекторов не больше трёх — такой код проще читать и поддерживать. Если же селекторов больше — возможно, стили стоит переписать.

Нюансы

Селекторы с использованием + и ~ , а также псевдокласс :where() не увеличивают уровень вложенности.

Специфичность не зависит от последовательности селекторов. У .list-item#catalog и #catalog.list-item одинаковый вес.

Псевдокласс :not не учитывают при подсчёте специфичности — считают только расположенные в нём селекторы.

Немного практики

  1. Посчитайте вес селектора #popup-text.text.green-paragraph.

  2. Что специфичнее: #popup-button или .popup-button.popup.link.button.green-button.big-button.decorate-button.shadow-button.square-button.border-button:hover?

  3. Какой будет кнопка: синей или чёрной?

<button style="background-color: blue" type="button">Это кнопка</button>
button {
  background-color: black !important;
}

Материалы по теме