Основы каскада и специфичности
Почти так же неизбежно, как смерть и налоги, разработчики будут ссылаться на то, что каскад и специфичность — это то, что делает работу с CSS сложной. Конечно, каскад и специфичность будут создавать проблемы, если не учитывать их в CSS, но если есть базовое понимание, я обещаю, что ваши навыки работы с CSS взлетят до небес.
Чаще всего каскад и специфичность объясняются с точностью до сантиметра в обширных руководствах или подробных статьях. Подобные статьи — это замечательно, но думаю, что их размер и сложность могут напугать разработчиков, поэтому собираюсь их упростить. На самом деле, простота — один из самых эффективных способов сохранить любую кодовую базу управляемой и поддерживаемой. Особенно это касается CSS. Давайте разберёмся.
C в CSS означает Cascade (каскад)
Давайте сначала разберёмся со слоном в комнате. Каскад — это алгоритм, разрешающий конфликты, когда к элементу HTML может применяться несколько правил.
Рассмотрим очень простой пример:
.my-element {
background: goldenrod;
background: coral;
}
У этого элемента есть два объявления фона. Каскад сделал своё дело и определил, что у .my-element
фон будет coral
. Но почему?
Поскольку декларация coral
идёт после декларации goldenrod
, coral
побеждает. В подавляющем большинстве случаев при создании стандартных CSS запомните это, и всё будет в порядке. Однако есть и исключения, поэтому давайте остановимся на них, ведь первый пункт был очень простым, верно?
Порядок важности
Нет, это не странная британская королевская семья; это простой набор правил, которые нужно запомнить, потому что не все свойства одинаковы. Я имею в виду, что некоторые типы 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
. Давайте разберёмся с оценками подробнее.
Оценка специфичности
Каждое правило селектора получает оценку. Чем выше балл, тем больше вероятность применения CSS этого правила. Оценка ведётся в таком формате: 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 баллов
Атрибут встроенного/инлайн стиля
<div style="background: blue"></div>
Оценка: 1-0-0-0
— 1000 баллов
Правило !important
.my-element {
background: red !important;
}
Оценка: 1-0-0-0-0
— 10000 баллов
Погодите-ка, !important
— это же для каскада, верно?! Это и каскад, и специфичность. Некто с именем вроде Чада Смита в Twitter может назвать это причиной того, что CSS "отстой". Но это не так. Потому что всё, что нужно сделать, это запомнить, что !important
относится к каскаду и специфичности, точно так же, как необходимо помнить, что JavaScript не может правильно сравнивать числа. Во всех языках программирования есть такие вещи, которые нужно изучать, и CSS ничем не отличается.
Помните, я говорил, что счёт будет расширяться? Так вот. Как для атрибутов встроенного стиля, так и для правил !important
добавляется дополнительное число. Для вас, дорогой читатель, это сигнал о том, что CSS, который вы пишете, должен быть чертовски важным, потому что вы находитесь в области очень высокой специфичности.
Комбинирование селекторов даёт больше баллов
Теперь вы знаете, сколько очков даёт каждый тип селектора, давайте объединим их, чтобы продемонстрировать, как работает эта часть.
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 баллов от селектора ID были отброшены, так как он находится внутри псевдокласса :where()
. Именно поэтому вы, вероятно, заметили его использование в CSS сбросах, потому что сброшенные стили гораздо легче переопределить благодаря низкой специфичности :where()
.
"Дочерние" и "родственные" селекторы не добавляют специфичности
Следует помнить, что использование дочерних и родственных селекторов (>
, ~
и +
) не влияет на оценку специфичности. Например, эти селекторы имеют одинаковые показатели:
.my-element li {
/* Ваши CSS объявления и свойства здесь */
}
.my-element > li {
/* Ваши CSS объявления и свойства здесь */
}
Инструменты разработчика — ваш лучший друг
Если вы откроете "Инструменты разработчика" в браузере и перейдёте на вкладку "Стили", это будет выглядеть примерно так:
То, что менее специфично с точки зрения как каскада, так и специфичности, будет находиться в нижней части панели. Всё, что более специфично, будет находиться выше на панели. То, что было отброшено, будет вычеркнуто, а то, что не было вычеркнуто, будет применено. Одно это знание должно облегчить вам жизнь!
Подведение итогов
Настоятельно рекомендую иметь в своём арсенале калькулятор специфичности. Мой любимый — от фантастических ребят из Polypane. Их калькулятор специфичности очень прост в использовании и отлично справляется со своей задачей.
Кроме того, есть новые возможности CSS, которые могут помочь, например, Каскадные слои. Лично мне они пока не пригодились, поэтому не могу о них рассказать. Пусть об этом расскажет тот, кто в этом разбирается.
В заключение скажу, что писать об этом очень скучно, но это очень важно понимать. Надеюсь, статья поможет почувствовать себя более комфортно с двумя наиболее фундаментальными частями CSS. Если ищете быстрый совет, как справиться с этим в реальном мире, я бы посоветовал держать селекторы как можно ниже. Если попали в тупик, помните о порядке происхождения и используйте инструменты разработчика! Помните, что если в панели CSS он перечёркнут, значит, в дело вступило что-то более специфичное.