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

Источник: «A primer on the cascade and specificity»
Каскад и специфичность, которых часто боятся разработчики, на самом деле очень просты, если использовать правильную модель мышления при создании CSS.

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

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

C в CSS означает Cascade (каскад)

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

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

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

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

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

See the Pen

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

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

Обычные объявления

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

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

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

Объявления использующие !important

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

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

В конечном счёте оно побеждает (почти) всегда, но относитесь к нему так, как будто оно побеждает всегда, и с ним не будет проблем.

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

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

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

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

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

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

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

Авторский CSS

Эй, чемпион, это твой продукт, который ты пишешь!

Авторский !important

Да, как и говорилось ранее, эти значения !important победят стандартные значения свойств.

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

Допустим, необходимо, чтобы размер шрифта был одинаковым на всех сайтах. Комбинация операционной системы и браузера может технически объявить это свойством !important, и это победит всё вышеперечисленное.

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

Наконец, если стили пользовательского агента имеют значение !important, то в конечном итоге победит этот вариант.


На этом с каскадом всё, давайте займёмся его лучшим другом — специфичностью.

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

Забавный факт: поскольку я родом из Йоркшира в Великобритании, мне трудно произнести это слово, что для человека, преподающего CSS, не является идеальным. Я отвлекаюсь от темы, но не просто так, а чтобы дать передышку — расправьте плечи и выдохните, потому что специфичность — это не сложно, поверьте мне.

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

div {
background: goldenrod;
}

.my-element {
background: coral;
}

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

See the Pen

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

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

Универсальный селектор — он же селектор подстановочных знаков

* {
/* Ваши CSS объявления и свойства здесь */
}

Оценка: 0-0-00 баллов

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

h1 {
/* Ваши CSS объявления и свойства здесь */
}

Оценка: 0-0-11 балл

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

::before {
/* Ваши CSS объявления и свойства здесь */
}

Оценка: 0-0-11 балл

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

.my-element {
/* Ваши CSS объявления и свойства здесь */
}

Оценка: 0-1-010 баллов

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

:hover {
/* Ваши CSS объявления и свойства здесь */
}

Оценка: 0-1-010 баллов

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

[href] {
/* Ваши CSS объявления и свойства здесь */
}

Оценка: 0-1-010 баллов

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

#myElement {
/* Ваши CSS объявления и свойства здесь */
}

Оценка: 1-0-0100 баллов

Атрибут встроенного/инлайн стиля

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

Оценка: 1-0-0-01000 баллов

Правило !important

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

Оценка: 1-0-0-0-010000 баллов

Погодите-ка, !important — это же для каскада, верно?! Это и каскад, и специфичность. Некто с именем вроде Чада Смита в Twitter может назвать это причиной того, что CSS "отстой". Но это не так. Потому что всё, что нужно сделать, это запомнить, что !important относится к каскаду и специфичности, точно так же, как необходимо помнить, что JavaScript не может правильно сравнивать числа. Во всех языках программирования есть такие вещи, которые нужно изучать, и CSS ничем не отличается.

Помните, я говорил, что счёт будет расширяться? Так вот. Как для атрибутов встроенного стиля, так и для правил !important добавляется дополнительное число. Для вас, дорогой читатель, это сигнал о том, что CSS, который вы пишете, должен быть чертовски важным, потому что вы находитесь в области очень высокой специфичности.

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

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

h1#myElement {
/* Ваши CSS объявления и свойства здесь */
}

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

Давайте попробуем ещё раз.

h1.my-element::before {
/* Ваши CSS объявления и свойства здесь */
}

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

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

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

h1:is(.my-element) {
/* Ваши CSS объявления и свойства здесь */
}

Он имеет точно такой же показатель специфичности, как и:

h1.my-element {
/* Ваши CSS объявления и свойства здесь */
}

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

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

h1#myElement {
/* Ваши CSS объявления и свойства здесь */
}

Счёт 1-0-1101 балл. Добавим :where().

h1:where(#myElement) {
/* Ваши CSS объявления и свойства здесь */
}

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

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

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

.my-element li {
/* Ваши CSS объявления и свойства здесь */
}

.my-element > li {
/* Ваши CSS объявления и свойства здесь */
}

Инструменты разработчика — ваш лучший друг

Если вы откроете "Инструменты разработчика" в браузере и перейдёте на вкладку "Стили", это будет выглядеть примерно так:

Панель CSS Инструменты разработчика демонстрирует, что в верхней части побеждает .my-element. Правила фона для div отменяются, потому что my-element более специфичен

Панель CSS "Инструменты разработчика" демонстрирует, что в верхней части побеждает .my-element. Правила фона для div отменяются, потому что my-element более специфичен

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

Подведение итогов

Настоятельно рекомендую иметь в своём арсенале калькулятор специфичности. Мой любимый — от фантастических ребят из Polypane. Их калькулятор специфичности очень прост в использовании и отлично справляется со своей задачей.

Кроме того, есть новые возможности CSS, которые могут помочь, например, Каскадные слои. Лично мне они пока не пригодились, поэтому не могу о них рассказать. Пусть об этом расскажет тот, кто в этом разбирается.

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

Дополнительные материалы

Предыдущая Статья

Статистика версий PHP: Июль, 2024

Следующая Статья

Поддомены для пользователей в Laravel