Перейти к содержанию

Специфичность и классы компонентов

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

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

Специфичность имеет значение после предыдущих этапов каскада. В этом уроке сравниваем обычные авторские объявления в одном контексте, без слоёв, inline-стилей и !important. Такое ограничение нужно проговорить: иначе легко принять частный пример за правило для любого CSS. Точные принципы сравнения описаны в MDN.

Общая карточка и её вариант

У первой article расширьте атрибут класса. Один элемент может иметь несколько имён, разделённых пробелами:

<article class="course-card course-card--featured">

В конец styles.css добавьте рамку и вариант её цвета:

.layout-lab .course-card {
  border: 1px solid #3b3c4c;
}
.layout-lab .course-card--featured {
  border-color: #6c43af;
}

Ожидается рамка вокруг каждой карточки, а у первой — другой цвет. Толщина и вид остаются общими. Класс course-card--featured не является особой командой CSS: двойной дефис здесь лишь соглашение об имени варианта. Если удалить его из HTML, первая карточка снова получит обычную рамку, не потеряв остальные свойства компонента.

Оба селектора содержат два класса. У них одинаковая специфичность, поэтому в нашем контексте цвет варианта, записанный позже, будет выбран. Сокращённое объявление border задаёт сразу несколько составляющих, включая цвет; отдельное border-color затем меняет только одну из них. При поиске конфликта нужно учитывать и сокращённые свойства, а не смотреть лишь строки с точно таким же именем.

Такое устройство даёт понятную точку изменения. Если нужно расширить рамки всех карточек, правим общий компонент. Если нужно другой акцент только рекомендованному курсу, правим вариант. Это не требует селектора, привязанного к конкретному месту в DOM, и не предполагает, что рекомендуемая карточка навсегда будет первой. Признак хранится там, где автор принял решение о варианте.

Какие части сравниваются

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

Рассмотрим несколько селекторов для той же записи:

.course-card                           0–1–0
.layout-lab .course-card                0–2–0
.layout-lab article.course-card         0–2–1
.layout-lab .course-card--featured       0–2–0
#recommended .course-card               1–1–0

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

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

Опыт с перестановкой правил

Временно перенесите правило варианта выше общего border. Ожидается, что фиолетовая рамка уступит общему серому цвету: специфичность равна, а сокращённое свойство записано позже. Это показывает, почему положение варианта важно. Затем верните исходный порядок, чтобы полное состояние проекта соответствовало следующему уроку.

Для второго опыта временно замените селектор варианта таким:

.layout-lab .course-card.course-card--featured {
  border-color: #6c43af;
}

Теперь вторая часть сравнения содержит три класса, поэтому в тех же условиях более позднее общее правило с двумя классами не переопределит этот цвет. Ожидаемый эффект можно разобрать без мысли о «более длинной строке». Имеет значение состав селектора, а не количество символов. Пробел, дефис и длина имени класса не увеличивают специфичность.

После опыта верните простой вариант с двумя классами. Мы не нуждаемся в усилении, поскольку контролируем порядок собственных правил. Более сильный селектор был нужен только для понимания механизма. Если оставить все диагностические изменения вместе, последующие уроки начнут объяснять другую таблицу стилей, чем ту, которую читатель получил из основного пути.

Состояние компонента и структура страницы

Не заменяйте вариант правилом .course-list li:first-child article. Такое правило описывает положение, а не решение рекомендовать курс. При перестановке данных акцент автоматически уйдёт на другую запись. Иногда положение действительно является нужным условием, например для удаления интервала у первого элемента. Для смыслового варианта карточки явный класс точнее выражает намерение автора.

В панели стилей браузера выберите первую карточку и сравните два объявления цвета рамки. Итог должен объясняться их порядком и специфичностью. Затем выберите вторую карточку: селектор варианта к ней вообще не подходит. Это разные этапы рассуждения. Неподходящее правило не проигрывает каскад; оно не участвует в выборе значения этого элемента.

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

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

Inline-объявление в атрибуте style вводит ещё один способ задать значение. Для нашего каталога он не нужен: данные карточки и её оформление лучше читать в разных файлах. Если вручную записать цвет рамки прямо на статье, обычное правило варианта может перестать давать ожидаемый результат. Не пытайтесь объяснить это числом букв в селекторе. Сначала установите, где именно объявлено значение и на каком этапе оно участвует в каскаде. Для учебной линии сохраняем все правила в одной внешней таблице.

Для самостоятельной работы перенесите класс варианта с Markdown на SEO, не меняя CSS. Ожидается, что рамка последует за классом. Верните исходный выбор перед продолжением. Теперь у компонентов есть общее правило и управляемый вариант. В следующем уроке рамка и внутренние поля помогут увидеть блочную модель: почему заданная ширина иногда оказывается меньше полного размера коробки и как box-sizing меняет расчёт.

Оглавление курса