← HTML & CSS/Специфичность и каскад#18 из 383← ПредыдущийСледующий →+15 XP
Полезно по теме:Гайд: старт в frontendПрактика: DOM и событияТермин: DOMМаршрут: старт с нуля

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

Ты написал два CSS-правила для одного элемента, но браузер применил не то, что ты ожидал. Знакомая ситуация? Это работает специфичность — система приоритетов, по которой браузер решает, какой стиль победит.

Как браузер выбирает стиль

Когда к элементу подходит несколько CSS-правил, браузер применяет их в таком порядке:

1. Специфичность — «вес» селектора

2. Порядок — если вес одинаковый, побеждает последнее правило

3. Наследование — некоторые свойства дети получают от родителей

Система подсчёта специфичности

Специфичность считается по трём категориям (записывается как три числа: A-B-C):

| Тип | Баллы | Пример |

|-----|-------|--------|

| Инлайн-стиль | 1-0-0 | style="color:red" |

| ID-селектор | 0-1-0 | #header |

| Класс, псевдокласс, атрибут | 0-0-1 | .card, :hover, [type] |

| Тег, псевдоэлемент | 0-0-1 | div, ::before |

Подожди, классы и теги одинаковы? Нет! Классы считаются во второй категории, теги — в третьей. Правильно: (ID) - (классы/псевдоклассы/атрибуты) - (теги/псевдоэлементы).

p               → 0-0-1    (тег)
.card           → 0-1-0    (класс)
#header         → 1-0-0    (id)
div p           → 0-0-2    (два тега)
.card p         → 0-1-1    (класс + тег)
.card .title    → 0-2-0    (два класса)
#header .nav    → 1-1-0    (id + класс)

Сравнение идёт слева направо: 1-0-0 всегда побеждает 0-99-99.

Практический пример

<div id="sidebar" class="panel">
  <p class="text">Какого цвета я буду?</p>
</div>
p { color: black; }              /* 0-0-1 */
.text { color: blue; }           /* 0-1-0 */
.panel p { color: green; }       /* 0-1-1 */
#sidebar p { color: red; }       /* 1-0-1 */

Текст будет красным — #sidebar p имеет наибольшую специфичность (1-0-1).

Наследование

Некоторые свойства дети автоматически получают от родителей:

body { font-family: Arial; color: #333; }
/* Все элементы внутри body наследуют шрифт и цвет */

Наследуются: color, font-*, line-height, text-*, cursor.

НЕ наследуются: margin, padding, border, background, width, height.

!important — ядерная бомба CSS

.btn { color: blue !important; }  /* Перебивает ВСЁ, кроме другого !important */

Когда использовать: почти никогда. !important разрушает нормальный каскад и создаёт головную боль при поддержке кода. Единственное оправданное применение — переопределить стили сторонних библиотек, которые сами используют !important.

Порядок источников стилей

От низкого к высокому приоритету:

1. Стили браузера по умолчанию (user agent stylesheet)

2. Внешний CSS-файл

3. Тег <style> в HTML

4. Инлайн-стиль style="..."

5. !important

Типичные ошибки

Ошибка 1: Бороться с ID через классы

/* ID (1-0-0) всегда победит класс (0-1-0) */
#btn { color: blue; }
.btn-red { color: red; }  /* Никогда не применится к #btn */

/* Решение: не используй ID для стилей */
.btn { color: blue; }
.btn-red { color: red; }  /* Теперь работает */

Ошибка 2: Лечить специфичность через !important

/* Плохо: создаёт цепочку !important */
.card { color: red !important; }
.featured { color: blue !important; } /* Теперь снова не работает */

/* Хорошо: правильная структура селекторов */
.card { color: red; }
.card.featured { color: blue; }  /* 0-2-0 > 0-1-0 */

В реальных проектах

В больших командах принято избегать высокой специфичности. Методология BEM строит классы так, чтобы специфичность всегда была на уровне 0-1-0 (один класс). CSS-in-JS инструменты (styled-components, emotion) автоматически генерируют уникальные классы, полностью избегая конфликтов специфичности.

Примеры

Демонстрация специфичности: кто победит?

// Создаём CSS с разными уровнями специфичности
const style = document.createElement('style')
style.textContent = `
  p { color: black; }                    /* 0-0-1 */
  .text { color: blue; }                 /* 0-1-0 */
  .panel p { color: green; }             /* 0-1-1 */
  #sidebar p { color: red; }             /* 1-0-1 */
`
document.head.appendChild(style)

// Создаём структуру
const sidebar = document.createElement('div')
sidebar.id = 'sidebar'

const panel = document.createElement('div')
panel.className = 'panel'

const text = document.createElement('p')
text.className = 'text'
text.textContent = 'Какого я цвета?'

panel.appendChild(text)
sidebar.appendChild(panel)
document.body.appendChild(sidebar)

// Смотрим, кто победил
const computed = window.getComputedStyle(text)
console.log('Цвет текста:', computed.color)
// Победил #sidebar p (специфичность 1-0-1 — самая высокая)
// rgb(255, 0, 0) — красный

// Пример наследования
const parent = document.createElement('div')
parent.style.fontFamily = 'Georgia, serif'
parent.style.color = '#555'

const child = document.createElement('p')
child.textContent = 'Я наследую стили родителя'
parent.appendChild(child)
document.body.appendChild(parent)

const childStyle = window.getComputedStyle(child)
console.log('Шрифт ребёнка (наследован):', childStyle.fontFamily)  // Georgia, serif
console.log('Цвет ребёнка (наследован):', childStyle.color)        // rgb(85, 85, 85)

// Проверка: background НЕ наследуется
parent.style.backgroundColor = 'lightyellow'
console.log('Фон родителя:', window.getComputedStyle(parent).backgroundColor)
console.log('Фон ребёнка:', window.getComputedStyle(child).backgroundColor)
// Ребёнок не получил фон — он transparent

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

Ты написал два CSS-правила для одного элемента, но браузер применил не то, что ты ожидал. Знакомая ситуация? Это работает специфичность — система приоритетов, по которой браузер решает, какой стиль победит.

Как браузер выбирает стиль

Когда к элементу подходит несколько CSS-правил, браузер применяет их в таком порядке:

1. Специфичность — «вес» селектора

2. Порядок — если вес одинаковый, побеждает последнее правило

3. Наследование — некоторые свойства дети получают от родителей

Система подсчёта специфичности

Специфичность считается по трём категориям (записывается как три числа: A-B-C):

| Тип | Баллы | Пример |

|-----|-------|--------|

| Инлайн-стиль | 1-0-0 | style="color:red" |

| ID-селектор | 0-1-0 | #header |

| Класс, псевдокласс, атрибут | 0-0-1 | .card, :hover, [type] |

| Тег, псевдоэлемент | 0-0-1 | div, ::before |

Подожди, классы и теги одинаковы? Нет! Классы считаются во второй категории, теги — в третьей. Правильно: (ID) - (классы/псевдоклассы/атрибуты) - (теги/псевдоэлементы).

p               → 0-0-1    (тег)
.card           → 0-1-0    (класс)
#header         → 1-0-0    (id)
div p           → 0-0-2    (два тега)
.card p         → 0-1-1    (класс + тег)
.card .title    → 0-2-0    (два класса)
#header .nav    → 1-1-0    (id + класс)

Сравнение идёт слева направо: 1-0-0 всегда побеждает 0-99-99.

Практический пример

<div id="sidebar" class="panel">
  <p class="text">Какого цвета я буду?</p>
</div>
p { color: black; }              /* 0-0-1 */
.text { color: blue; }           /* 0-1-0 */
.panel p { color: green; }       /* 0-1-1 */
#sidebar p { color: red; }       /* 1-0-1 */

Текст будет красным — #sidebar p имеет наибольшую специфичность (1-0-1).

Наследование

Некоторые свойства дети автоматически получают от родителей:

body { font-family: Arial; color: #333; }
/* Все элементы внутри body наследуют шрифт и цвет */

Наследуются: color, font-*, line-height, text-*, cursor.

НЕ наследуются: margin, padding, border, background, width, height.

!important — ядерная бомба CSS

.btn { color: blue !important; }  /* Перебивает ВСЁ, кроме другого !important */

Когда использовать: почти никогда. !important разрушает нормальный каскад и создаёт головную боль при поддержке кода. Единственное оправданное применение — переопределить стили сторонних библиотек, которые сами используют !important.

Порядок источников стилей

От низкого к высокому приоритету:

1. Стили браузера по умолчанию (user agent stylesheet)

2. Внешний CSS-файл

3. Тег <style> в HTML

4. Инлайн-стиль style="..."

5. !important

Типичные ошибки

Ошибка 1: Бороться с ID через классы

/* ID (1-0-0) всегда победит класс (0-1-0) */
#btn { color: blue; }
.btn-red { color: red; }  /* Никогда не применится к #btn */

/* Решение: не используй ID для стилей */
.btn { color: blue; }
.btn-red { color: red; }  /* Теперь работает */

Ошибка 2: Лечить специфичность через !important

/* Плохо: создаёт цепочку !important */
.card { color: red !important; }
.featured { color: blue !important; } /* Теперь снова не работает */

/* Хорошо: правильная структура селекторов */
.card { color: red; }
.card.featured { color: blue; }  /* 0-2-0 > 0-1-0 */

В реальных проектах

В больших командах принято избегать высокой специфичности. Методология BEM строит классы так, чтобы специфичность всегда была на уровне 0-1-0 (один класс). CSS-in-JS инструменты (styled-components, emotion) автоматически генерируют уникальные классы, полностью избегая конфликтов специфичности.

Примеры

Демонстрация специфичности: кто победит?

// Создаём CSS с разными уровнями специфичности
const style = document.createElement('style')
style.textContent = `
  p { color: black; }                    /* 0-0-1 */
  .text { color: blue; }                 /* 0-1-0 */
  .panel p { color: green; }             /* 0-1-1 */
  #sidebar p { color: red; }             /* 1-0-1 */
`
document.head.appendChild(style)

// Создаём структуру
const sidebar = document.createElement('div')
sidebar.id = 'sidebar'

const panel = document.createElement('div')
panel.className = 'panel'

const text = document.createElement('p')
text.className = 'text'
text.textContent = 'Какого я цвета?'

panel.appendChild(text)
sidebar.appendChild(panel)
document.body.appendChild(sidebar)

// Смотрим, кто победил
const computed = window.getComputedStyle(text)
console.log('Цвет текста:', computed.color)
// Победил #sidebar p (специфичность 1-0-1 — самая высокая)
// rgb(255, 0, 0) — красный

// Пример наследования
const parent = document.createElement('div')
parent.style.fontFamily = 'Georgia, serif'
parent.style.color = '#555'

const child = document.createElement('p')
child.textContent = 'Я наследую стили родителя'
parent.appendChild(child)
document.body.appendChild(parent)

const childStyle = window.getComputedStyle(child)
console.log('Шрифт ребёнка (наследован):', childStyle.fontFamily)  // Georgia, serif
console.log('Цвет ребёнка (наследован):', childStyle.color)        // rgb(85, 85, 85)

// Проверка: background НЕ наследуется
parent.style.backgroundColor = 'lightyellow'
console.log('Фон родителя:', window.getComputedStyle(parent).backgroundColor)
console.log('Фон ребёнка:', window.getComputedStyle(child).backgroundColor)
// Ребёнок не получил фон — он transparent

Задание

Напиши HTML-страницу с тремя параграфами, демонстрирующими специфичность CSS. Первый параграф — только тег p (чёрный цвет). Второй — класс .highlight (оранжевый, специфичность выше тега). Третий — id #main-text И класс .highlight (фиолетовый, ID побеждает). Укажи в CSS-комментариях специфичность каждого правила.

Подсказка

Тег p: color: black (специфичность 0-0-1). Класс .highlight: color: orange (0-1-0 — победит тег). ID #main-text: color: purple (1-0-0 — победит класс). Второй параграф: class="highlight". Третий: id="main-text" class="highlight" — ID побеждает несмотря на класс.

Загружаем среду выполнения...
Загружаем AI-помощника...