Каскад и специфичность в CSS - это просто

alexei08/08/2024 - 08:51
Каскад и специфичность в CSS - это просто

Каскад и специфичность часто воспринимаются как что-то сложное и запутанное, что только мешает в работе с CSS. И в самом деле, если вы не учитываете их в своем коде CSS, каскад и специфичность очень быстро приведут к проблемам. Но на самом же деле, если у вас есть хотя бы базовые знания в этом вопросе, все становится довольно просто, и ваш уровень владения CSS буквально взлетает до небес.

С самого своего появления каскад и специфичность подробно разбирались в различных гайдах и учебных статьях. И эти материалы, безусловно, полезны, но их размер тоже способен отпугнуть разработчиков, особенно начинающих, поэтому в этой статье мы собираемся подойти к данной теме проще. На самом деле, если делать вещи проще, то это будет самым эффективным способом держать любой код в управляемом и легко обслуживаемом состоянии. И это особенно касается CSS. Итак, приступим.

C в CSS означает Каскадный

Для начала давайте обрисуем очевидное. Каскад - это определенный алгоритм, который позволяет разрешать конфликты в том случае, когда несколько однотипных стилевых правил могут применяться к одному и тому же HTML элементу.

Давайте рассмотрим очень простой пример:


.my-element {
	background: goldenrod;
	background: coral;
}

У данного элемента есть две декларации фонового цвета. Каскад проделал свою работу и определил, что у элемента .my-element фоновым цветом будет coral. Но почему так?

See the Pen Каскад и специфичность 1 by Alexei Goloviznin on CodePen.

Поскольку декларация цвета coral стоит после декларации цвета goldenrod, цвет coral побеждает. Помните об этом в большинстве ваших стандартных разработок стилей CSS, и все будет в порядке. Однако есть и свои исключения, так что давайте сосредоточимся на них.

Порядок по важности

И это не та странная затея в британской королевской семье. Это простой набор правил, который нужно запомнить, так как не все CSS свойства равны. Мы имеем в виду то, что у некоторых CSS свойств приоритет более высокий, чем у других. Вот список типов деклараций, упорядоченный от менее к более специфичным.

Обычные декларации

Это те самые свойства, вроде background, padding, line-height и margin, которые мы добавляем в свой код большую часть времени.

Активные анимации

Это когда на ваши анимируемые свойства влияет активная анимация @keyframes.

Декларации, использующие усилитель !important

Его не нужно бояться, им следует восхищаться: скромное свойство !important делает именно то, о чем и говорит - определяет важное значение. Оно добавляется как часть декларации, вот так:


.my-element {
	background: grey !important;
}

И в конечном итоге такая декларация будет побеждать почти всегда. Относитесь к ним так, как будто они будут побеждать все время, и у вас исчезнут всякие проблемы.

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

Порядок по происхождению

Данный порядок учитывается каскадом от менее к более специфичному типу.

Базовые стили пользовательского агента

У каждого основного браузера есть таблица стилей пользовательского агента. Это стили по умолчанию, которые применяются к элементам HTML, если для них не определен CSS. Это сделано для того, чтобы элементы HTML не выглядели одинаково.

Примечание: Это еще одна из многих причин, почему писать симантически правильный код HTML - хорошая идея.

Локальные пользовательские стили

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

Авторские стили CSS

Это именно те стили, которые пишите вы!

Авторские стили с !important

Как мы говорили выше, значения с !important перекроют значения ваших стандартных свойств.

Локальные пользовательские стили с !important

Скажем, вы хотите, чтобы размер шрифта был одинаковым абсолютно на всех веб-сайтах. Операционная система вместе с браузером технически могли бы декларировать это как свойство с усилителем !important. И в этом случае такое свойство перекроет все подобные свойства предыдущих типов.

Стили пользовательского агента с !important

И, наконец, если пользовательский агент декларирует значения с усилителем !important, то они перекрывают абсолютно все.

Это то, что касается каскада. А теперь давайте поговорим о его лучшем друге - специфичности.

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

Расслабьтесь и выдохните, потому что в специфичности нет ничего сложного.

Давайте вернемся к декларации стиля для элемента .my-element, но немного изменим наш CSS. Важно обратить внимание, что мы используем элемент <div> с атрибутом class.


div {
	background: goldenrod;
}

.my-element {
	background: coral;
}

See the Pen Каскад и специфичность 2 by Alexei Goloviznin on CodePen.

И здесь победителем будет цвет coral, потому что у селектора .my-element очки специфичности 0-1-0, тогда как у селектора div очки специфичности 0-0-1. Давайте подробнее разберемся в том, как оценить специфичность селекторов.

Оценка специфичности

У каждого селектора в таблице стилей есть свой вес, выраженный в количестве очков.

Чем у селектора выше оценка, тем больше вероятность его применения. Обычно очки записываются в формате 0-0-0 или "сотни"-"десятки"-"единицы". Это представление может расширяться, но пока не будем заострять на этом внимание. Давайте коротко пробежимся по правилам оценки.

Примечание: Иногда, очки специфичности записывают с запятыми, например, 0,0,0.

Универсальный селектор или селектор-звездочка


* {
	/* Здесь ваши свойства CSS */
}

Счет: 0-0-0 - 0 очков

Селектор типа или селектор элемента


h1 {
	/* Здесь ваши свойства CSS */
}

Счет: 0-0-1 - 1 очко

Селектор псевдоэлементов


::before {
	/* Здесь ваши свойства CSS */
}

Счет: 0-0-1 - 1 очко

Селектор класса


.my-element {
	/* Здесь ваши свойства CSS */
}

Счет: 0-1-0 - 10 очков

Селектор псевдокласса


:hover {
	/* Здесь ваши свойства CSS */
}

Счет: 0-1-0 - 10 очков

Селектор атрибута


[href] {
	/* Здесь ваши свойства CSS */
}

Счет: 0-1-0 - 10 очков

Селектор идентификатора


#myElement {
	/* Здесь ваши свойства CSS */
}

Счет: 1-0-0 - 100 очков

Встроенный атрибут style


<div style="background: blue"></div>

Счет: 1-0-0-0 - 1000 очков

Декларации с !important


.my-element {
	background: red !important;
}

Счет: 1-0-0-0-0 - 10000 очков

Но постойте, скажете вы, ведь !important это штука из каскада! Это одновременно и каскад, и специфичность. И вам нужно просто запомнить, что !important применяется как к каскаду, так и специфичности. Подобные хитрые штуки есть во всех языках программирования, и CSS здесь не исключение.

Кроме того, помните, мы говорили, что количество разрядов будет увеличиваться? Вот это оно и есть. Как для встроенного атрибута style, так и для деклараций с !important мы добавляем дополнительный разряд.

Комбинирование селекторов дает больше очков

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


h1#myElement {
	/* Здесь ваши свойства CSS */
}

Мы получим 1 балл, потому что используем селектор типов h1 и 100 баллов, потому что мы также используем селектор идентификаторов #myElement. Таким образом, счет специфичности здесь будет 1-0-1 или 101 балл.

Давайте пойдем немного дальше и посмотрим, что получится.


h1.my-element::before {
	/* Здесь ваши свойства CSS */
}

У нас есть 2 балла, потому что мы используем как селектор типов h1, так и селектор псевдоэлементов ::before. Мы также получаем 10 баллов, потому что используем селектор классов .my-element. Следовательно, в этом случае счет специфичности составляет 0-1-2 или 12 баллов.

:not(), :is() и :where()

Допустим, у вас есть такой код CSS:


h1:is(.my-element) {
	/* Здесь ваши свойства CSS */
}

У него будет точно такой же счет специфичности, что и у следующей декларации:


h1.my-element {
	/* Здесь ваши свойства CSS */
}

Это связано с тем, что берутся очки самого специфичного селектора, переданного в псевдоклассы :is() и :not(), поэтому даже не думайте использовать их для повышения специфичности.

Что касается :where(), то у этого псевдокласса и любого переданного в него селектора вообще нет никакой специфичности. Для демонстрации этого давайте вернемся к одному из предыдущих селекторов.


h1#myElement {
	/* Здесь ваши свойства CSS */
}

Счет этого селектора 1-0-1 или 101 балл. Давайте теперь сюда добавим псевдокласс :where().


h1:where(#myElement) {
	/* Здесь ваши свойства CSS */
}

Теперь у этого селектора счет 0-0-1 или 1 балл. И все, потому что 100 баллов от селектора идентификаторов теперь отброшены, так как он находится внутри псевдокласса :where(). Именно поэтому, как вы, вероятно, замечали, данный псевдокласс часто используют при определении исходных стилей CSS, потому что благодаря низкой специфичности :where() такие стили намного легче переопределять.

Дочерние и равноуровневые селекторы не добавляют специфичность

Следует запомнить одну вещь: использование дочерних и равноуровневых селекторов (>, ~ и +) никак не влияет на счет специфичности. Например, у следующих селекторов будет одинаковое количество очков специфичности:


.my-element li {
	/* Здесь ваши свойства CSS */
}

.my-element > li { 
	/* Здесь ваши свойства CSS */
}

Инструменты разработчика - ваши лучшие друзья

Если в своем браузере вы выберите меню "Инструменты разработчика" и перейдете на вкладку "Стили", то вы увидите примерно следующее:

Панель стилей CSS  в Инструментах разработчика
Панель стилей CSS в Инструментах разработчика

Элементы, которые менее специфичны с точки зрения как каскада, так и собственно специфичности, в панели будут располагаться ниже. Чем больше специфичность, тем элемент выше. То, что было переопределено, будет зачеркнуто. Все, что не зачеркнуто, будет применено. Таким образом, этот инструмент значительно облегчить вашу жизнь!

Подводим итоги

Можно порекомендовать вам иметь в своем арсенале калькулятор специфичности. В качестве одного из лучших можно назвать веб-приложение от команды Polypane. Их калькулятор специфичности очень прост в использовании и действительно хорошо все разбирает.

Также могут помочь новые возможности CSS, такие как каскадные слои. Пока в них мы не видим особой необходимости, поэтому ничего о них и не пишем.

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