Клавиша / esc
Весы, в левой и более лёгкой части которых один селектор, а в другой — набор селекторов
Иллюстрация: Кира Кустова

Специфичность

Как браузер решает, какие стили применять к элементу? Разбираемся, почему у селектора есть вес ⚖️

Время чтения: меньше 5 мин

Кратко

Скопировано

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

Специфичность — это одно из базовых понятий в CSS.

Вес селекторов

Скопировано

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

  1. Селекторы по идентификатору;
  2. Селекторы по классу, селекторы по атрибуту и селекторы с псевдоклассами;
  3. Селекторы по тегу, селекторы с псевдоэлементами.

Комбинаторы +, >, ~, универсальный селектор * и псевдокласс :where() веса не имеют.

Псевдоклассы :is(), :has() и :not() принимают вес наиболее специфичного селектора внутри скобок.

Система расчёта

Скопировано

Существует удобный способ вычисления веса селектора в уме. Выше мы перечислили три группы сущностей, из которых может состоять селектор. Представим любой селектор в виде трёх нулей: 0.0.0.

  • Селекторы по идентификатору увеличивают первую цифру.
  • Селекторы по классу, по атрибуту или псевдокласс увеличивают вторую цифру.
  • Селектор по тегу или псевдоэлемент увеличивают третью цифру.

Один селектор равен единице.

Пока сложно понять. Давайте разберёмся на примерах.

  • Селектор #some состоит из одного идентификатора. Один селектор = 1. Увеличиваем первую цифру на 1. В итоге вес такого селектора равен 1.0.0.
  • .class состоит из одного класса. Увеличиваем вторую цифру на 1. Получаем вес селектора 0.1.0.
  • section состоит из одного тега. Увеличиваем последнюю цифру на 1. Вес селектора равен 0.0.1.

Дальше аналогично можем посчитать вес комбинированных селекторов.

div#some состоит из одного селектора по тегу и одного идентификатора. Селектор по тегу увеличивает последнюю цифру, селектор по идентификатору — первую. Вес селектора равен 1.0.1.

section h1 состоит из двух селекторов по тегу. Увеличиваем последнюю цифру на два и получаем вес 0.0.2.

#block section > .list a состоит из идентификатора (первая цифра), двух тегов (последняя цифра) и класса (вторая цифра). Вес селектора равен 1.1.2.

* .list a состоит из одного класса и одного тега. Итоговый вес будет 0.1.1. Универсальный селектор ничего не весит 🪶

Для наглядности расположим селекторы по убыванию веса. Сверху самый тяжёлый.

Селектор Вес
#block section > .list a 1.1.2
div#some 1.0.1
#some 1.0.0
* .list a 0.1.1
.class 0.1.0
section h1 0.0.2
section 0.0.1

Если не очень хочется считать в уме, можно воспользоваться калькулятором специфичности CSS Specificity calculator.

Атрибут style

Скопировано

CSS-свойства, написанные в атрибуте style внутри HTML-разметки, перебивают свойства, написанные для этого элемента во внешних CSS-файлах или внутри тега <style>. Так что формально атрибут style самый специфичный, у него самый большой вес.

Иногда его добавляют в формулу в виде четвёртой цифры, стоящей перед всеми. Посмотрим на примере:

        
          
          <div class="element" id="this" style="color: purple; border: none">  Some smart text</div>
          <div class="element" id="this" style="color: purple; border: none">
  Some smart text
</div>

        
        
          
        
      
        
          
          div.element#this {  color: green;  border: 10px solid red;}
          div.element#this {
  color: green;
  border: 10px solid red;
}

        
        
          
        
      

У селектора в CSS будет специфичность 1.1.1, потому что там указан один идентификатор, один класс и один селектор тега. Но в итоге текст в блоке будет пурпурным, а рамки не будет совсем. Потому что у атрибута style вес равен 1.0.0.0 🏋️

!important

Скопировано

Ключевое слово !important нарушает все установленные спецификацией законы и насильно применяет свойство, после которого написано. Ему плевать на селектор CSS-правила, внутри которого это свойство написано. Будет так, и никак иначе.

Формально это ключевое слово не имеет отношения к концепции специфичности. Но из-за его варварских замашек нельзя не принимать его во внимание.

Тут к месту будет ещё раз напомнить, что ключевое слово !important всегда стоит использовать с осторожностью и не злоупотреблять им. В том числе из-за того, что оно нарушает естественную работу специфичности и каскада.

На практике

Скопировано

Вадим Макеев советует

Скопировано

Самый правильный способ справиться со специфичностью — отказаться от возни с ней и построить систему стилей так, чтобы у всех селекторов была одна специфичность. Например, с помощью БЭМ-классов. Но если уж пришлось сражаться, есть интересный трюк!

Представим, что у нас есть простой блок с двумя классами:

        
          
          <div class="tomato plum">  Кто я?</div>
          <div class="tomato plum">
  Кто я?
</div>

        
        
          
        
      

Если написать классы в порядке сначала .tomato, а потом .plum, то текст будет сливовый (plum):

        
          
          .tomato {  color: tomato;}.plum {  color: plum;}
          .tomato {
  color: tomato;
}

.plum {
  color: plum;
}

        
        
          
        
      

Если поменять классы в CSS местами, то победит последний, и текст станет томатным:

        
          
          .plum {  color: plum;}.tomato {  color: tomato;}
          .plum {
  color: plum;
}

.tomato {
  color: tomato;
}

        
        
          
        
      

Но если нарастить класс .tomato им самим же… что?! Да-да, он выиграет в любом порядке: текст будет томатным.

        
          
          .tomato.tomato {  color: tomato;}.plum {  color: plum;}
          .tomato.tomato {
  color: tomato;
}

.plum {
  color: plum;
}

        
        
          
        
      

Потому что у него выше специфичность! Нужно больше специфичности? Легко! Класс можно продолжать наращивать, пока вам не хватит.

Алёна Батицкая советует

Скопировано

🛠 Понимание концепции специфичности очень важно для понимания общего механизма вычисления стилей браузерами. Но, как правило, если проект написан хорошо, то вам не придётся перебивать селекторы более специфичными.

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

Такой трюк может быть обоснован, только если вы работаете на проекте, к исходникам которого у вас нет доступа, а стили поменять надо. Там придётся подбирать тяжёлые в плане специфичности селекторы, ничего не поделать 😔