Загрузка...
Загрузка...
Что такое специфичность CSS, как она вычисляется (a,b,c), inline styles vs !important, типичные ошибки, методология BEM, стратегии избежания войн специфичности.
Бесплатные онлайн-инструменты по теме статьи
CSS vs SCSS/Sass: вложенность, переменные, миксины, партиалы. Когда использовать, миграция с CSS, инструменты конвертации. Конвертер CSS ↔ SCSS на reChecker.
CSSПолное руководство по единицам измерения CSS: px, rem, em, vw, vh. Когда использовать каждую, доступность, адаптивная вёрстка, важность базового размера шрифта.
CSSCSS border-radius: синтаксис, 1–4 значения, slash notation для эллипсов. Проценты vs px, круг, pill, blob. Поддержка браузеров, производительность, адаптивность.
CSSCSS clip-path: синтаксис polygon, circle, ellipse, inset. Практические примеры, анимации, поддержка браузеров, советы по производительности. Генератор clip-path на reChecker.
Поделитесь с коллегами или изучите другие материалы блога
Специфичность определяет, какое CSS-правило применится при конфликте. Понимание механизма расчёта избавляет от хаков с !important и «войн специфичности». В этой статье разберём, как вычисляется специфичность, чем отличаются inline-стили и !important, типичные ловушки и стратегии для поддержания предсказуемого каскада.
Для расчёта специфичности любого селектора используйте калькулятор специфичности CSS на reChecker.
Специфичность (specificity) — вес селектора, по которому браузер выбирает, какое правило применить при конфликте. При равной специфичности побеждает правило, объявленное позже в каскаде.
Специфичность не имеет единицы «в пикселях» — это тройка чисел (a, b, c), где каждая позиция считает свой тип селекторов.
Браузер считает три компонента:
| Компонент | Что считает | Примеры |
|---|---|---|
| a | ID-селекторы | #header, #nav-main |
| b | Классы, атрибуты, псевдоклассы | .class, [type="text"], :hover, :nth-child() |
| c | Элементы, псевдоэлементы | div, p, ::before, ::after |
Универсальный селектор * и комбинаторы (+, >, ~, пробел) не увеличивают специфичность.
| Селектор | a | b | c | Пояснение |
|---|---|---|---|---|
p | 0 | 0 | 1 | Один элемент |
.class | 0 | 1 | 0 | Один класс |
#id | 1 | 0 | 0 | Один ID |
div p | 0 | 0 | 2 | Два элемента |
.nav a:hover | 0 | 2 | 1 | Два псевдокласса/класса, один элемент |
#header .nav li.active | 1 | 2 | 1 | Один ID, два класса, один элемент |
* | 0 | 0 | 0 | Универсальный — нулевая специфичность |
Сравнение идёт слева направо: сначала a, потом b, потом c. (1, 0, 0) побеждает (0, 99, 99).
Стили в атрибуте style имеют специфичность выше любого селектора в таблице стилей (кроме !important). Их условно можно представить как (1, 0, 0, 0) — дополнительная «четвёртая» позиция, которая всегда побеждает обычные селекторы.
!important выводит правило из обычного каскада. При конфликте двух !important побеждает тот, у кого выше специфичность селектора. Inline-стили с !important побеждают всё.
/* Специфичность (0, 1, 0) */
.button { color: blue; }
/* Специфичность (0, 2, 0) — побеждает */
.nav .button { color: red; }
/* !important — побеждает всё без !important */
.button { color: green !important; }
Чем длиннее селектор, тем выше специфичность. Цепочка body .header .nav .menu .item a имеет (0, 4, 2). Чтобы переопределить её, нужен ещё более «тяжёлый» селектор — начинается гонка вооружений.
Один ID даёт (1, 0, 0) — больше, чем любой селектор без ID. #header побеждает .header .nav .menu .item. ID сложно переопределить без нового ID или !important.
Добавление !important ломает каскад. Следующее переопределение потребует ещё одного !important с ещё большей специфичностью. Итог — непредсказуемые стили.
Библиотеки вроде Bootstrap используют много классов и псевдоклассов. Переопределение .btn-primary может потребовать .my-form .btn-primary или даже дублирования селектора для увеличения специфичности.
:not(), :is(), :has() — специфичность берётся от самого «тяжёлого» аргумента. :not(.class) даёт (0, 1, 0), как .class. [type="text"] считается как класс — (0, 1, 0).
BEM (Block, Element, Modifier) снижает потребность в специфичности за счёт плоской иерархии классов:
.card, .button.card__title, .card__body.button--primary, .card--featured/* Без BEM — вложенность, рост специфичности */
.header .nav .menu .item a { color: blue; }
/* С BEM — один класс, (0, 1, 0) */
.nav__link { color: blue; }
Один класс — одна единица специфичности. Переопределение — добавление модификатора или нового класса, без вложенности.
Вместо .page .header .nav a используйте .nav__link. Максимум 1–2 уровня вложенности для контекста.
ID нужны для якорей и aria-labelledby, не для стилизации. Классы дают гибкость и предсказуемую специфичность.
Исключения: утилиты (например, .sr-only), переопределение сторонних библиотек при невозможности изменить порядок подключения. В остальных случаях — рефакторинг селекторов.
:root {
--button-bg: #3b82f6;
}
.button {
background: var(--button-bg);
}
/* Переопределение — без роста специфичности */
.header .button {
--button-bg: #1d4ed8;
}
Переменные наследуются, переопределение не требует более «тяжёлого» селектора.
@layer позволяет задать приоритет слоёв:
@layer reset, base, components, utilities;
@layer utilities {
.text-center { text-align: center; }
}
@layer components {
.card { text-align: left; }
}
Правила в utilities побеждают components, независимо от специфичности и порядка в файле. Слои — современный способ управления каскадом.
При одинаковой специфичности побеждает правило, объявленное позже. Порядок подключения: reset → normalize → базовые стили → компоненты → утилиты. Если в main.css правило идёт после components.css, оно переопределит компонент при равной специфичности. Используйте это осознанно: не полагайтесь на случайный порядок, а выстраивайте слои стилей.
::before и ::after считаются как один элемент (c). Селектор .card::before имеет (0, 1, 1). Псевдоклассы вроде :first-child, :nth-of-type() считаются как классы (b). ::placeholder и ::selection — псевдоэлементы. При отладке учитывайте, что вложенные псевдоэлементы накапливают специфичность так же, как обычные элементы.
Ручной подсчёт (a, b, c) для длинных селекторов утомителен. Калькулятор специфичности CSS на reChecker позволяет:
Инструмент полезен при отладке «не срабатывающих» стилей, при рефакторинге и при изучении каскада.
Вложенность в SASS генерирует длинные селекторы: .block { .element { .modifier { } } } превращается в .block .element .modifier с растущей специфичностью. Используйте & осознанно: &--modifier даёт .block--modifier (один класс), а не .block .block--modifier. BEM в SASS лучше писать плоскими блоками, без глубокой вложенности — иначе преимущества BEM теряются.
При конфликте стилей:
Скорее всего, побеждает правило с большей специфичностью. Проверьте селектор в DevTools или в калькуляторе специфичности reChecker. Упростите селектор или используйте класс с большей специфичностью, избегая !important.
Один ID (1, 0, 0) побеждает любое количество классов (0, n, m). Десять классов (0, 10, 0) всё равно меньше одного ID. Поэтому ID в CSS не рекомендуют.
BEM использует один класс на элемент (.block__element, .block--modifier). Специфичность всегда (0, 1, 0). Нет вложенности — нет накопления специфичности. Переопределение — через модификатор или новый класс.
Исключительно для утилитарных классов (.sr-only, .hidden), которые должны перебивать всё. Для переопределения сторонних библиотек — только если нельзя изменить порядок подключения. В остальных случаях — рефакторинг.
Подсчитайте ID (a), классы/атрибуты/псевдоклассы (b), элементы/псевдоэлементы (c). Или используйте калькулятор специфичности на reChecker — введите селектор и получите разбивку (a, b, c) для любого селектора.