Ты написал два 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.
.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.
.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 побеждает несмотря на класс.