Кратко
СкопированоСпецифичность — это алгоритм, благодаря которому браузер определяет, какие именно стили из всего набора применить к элементу. В вычислениях участвуют CSS-селекторы. Если одному и тому же элементу подходит сразу несколько CSS-правил с разными селекторами, то браузер применяет те стили, вес селектора которых больше. Правило каскада «кто ниже, тот и выигрывает» при этом может нарушаться.
Специфичность — это одно из базовых понятий в CSS.
Вес селекторов
СкопированоДавайте разберёмся, как браузер взвешивает селектор. Ниже перечислены типы селекторов по убыванию специфичности:
- Селекторы по идентификатору;
- Селекторы по классу, селекторы по атрибуту и селекторы с псевдоклассами;
- Селекторы по тегу, селекторы с псевдоэлементами.
Комбинаторы +
, >
, ~
, универсальный селектор *
и псевдокласс :where
веса не имеют.
Псевдоклассы :is
, :has
и :not
принимают вес наиболее специфичного селектора внутри скобок.
Система расчёта
СкопированоСуществует удобный способ вычисления веса селектора в уме. Выше мы перечислили три группы сущностей, из которых может состоять селектор. Представим любой селектор в виде трёх нулей: 0.0.0.
- Селекторы по идентификатору увеличивают первую цифру.
- Селекторы по классу, по атрибуту или псевдокласс увеличивают вторую цифру.
- Селектор по тегу или псевдоэлемент увеличивают третью цифру.
Один селектор равен единице.
Пока сложно понять. Давайте разберёмся на примерах.
- Селектор
#some
состоит из одного идентификатора. Один селектор = 1. Увеличиваем первую цифру на 1. В итоге вес такого селектора равен 1.0.0. .class
состоит из одного класса. Увеличиваем вторую цифру на 1. Получаем вес селектора 0.1.0.section
состоит из одного тега. Увеличиваем последнюю цифру на 1. Вес селектора равен 0.0.1.
Дальше аналогично можем посчитать вес комбинированных селекторов.
div
состоит из одного селектора по тегу и одного идентификатора. Селектор по тегу увеличивает последнюю цифру, селектор по идентификатору — первую. Вес селектора равен 1.0.1.
section h1
состоит из двух селекторов по тегу. Увеличиваем последнюю цифру на два и получаем вес 0.0.2.
#block section >
состоит из идентификатора (первая цифра), двух тегов (последняя цифра) и класса (вторая цифра). Вес селектора равен 1.1.2.
*
состоит из одного класса и одного тега. Итоговый вес будет 0.1.1. Универсальный селектор ничего не весит 🪶
Для наглядности расположим селекторы по убыванию веса. Сверху самый тяжёлый.
Селектор | Вес |
---|---|
#block section > | 1.1.2 |
div | 1.0.1 |
#some | 1.0.0 |
* | 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; }
Потому что у него выше специфичность! Нужно больше специфичности? Легко! Класс можно продолжать наращивать, пока вам не хватит.
советует Скопировано
🛠 Понимание концепции специфичности очень важно для понимания общего механизма вычисления стилей браузерами. Но, как правило, если проект написан хорошо, то вам не придётся перебивать селекторы более специфичными.
Если у вас возникает соблазн перебить существующие стили более специфичным селектором, то советую вам разобраться в первопричине проблемы, а не поддаваться порыву.
Такой трюк может быть обоснован, только если вы работаете на проекте, к исходникам которого у вас нет доступа, а стили поменять надо. Там придётся подбирать тяжёлые в плане специфичности селекторы, ничего не поделать 😔