«Хороший сайт делается для людей, а поисковики приходят следом»

— из принципов редакции SeoLiga.ru
Главная » Статьи » Веб-программирование » CSS: каскад, специфичность и наследование

CSS: каскад, специфичность и наследование

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

Что такое каскад

Название CSS расшифровывается как каскадные таблицы стилей, и слово «каскад» здесь ключевое. Когда на один элемент действует несколько правил, браузер должен решить, чьё значение применить. Он делает это по строгому алгоритму, который учитывает несколько факторов в определённом порядке.

  1. Источник и важность. Сначала сравнивается, откуда пришло правило: от браузера по умолчанию, от пользователя или от автора страницы. Правила с пометкой important имеют приоритет над обычными.
  2. Специфичность. Если важность одинакова, побеждает более специфичный селектор.
  3. Порядок появления. Если и специфичность равна, побеждает правило, которое встречается позже.

Именно этот порядок надо держать в голове. Большинство «необъяснимых» багов объясняются одним из трёх шагов.

Порядок следования правил

Самый простой случай: два одинаковых по весу селектора. Выигрывает последний.

p { color: red; }
p { color: blue; }
/* Абзацы будут синими */

Это касается и подключаемых файлов. Если вы подключили сначала свой файл, а потом библиотеку, правила библиотеки с тем же весом перекроют ваши. Поэтому порядок подключения таблиц стилей — не мелочь, а важная часть архитектуры.

Специфичность селекторов

Специфичность — это своего рода вес селектора. Чем точнее селектор указывает на элемент, тем он весомее. Считается она по компонентам, которые удобно представить как три разряда.

РазрядЧто входитПримеры
Первый (самый сильный)Идентификаторы#header, #main
ВторойКлассы, атрибуты, псевдоклассы.menu, [type="text"], :hover
Третий (самый слабый)Элементы и псевдоэлементыdiv, p, ::before

Сравнение идёт по разрядам слева направо. Один идентификатор перевешивает любое количество классов, а один класс перевешивает любое количество тегов. Универсальный селектор и комбинаторы веса не добавляют.

Посчитаем несколько примеров в формате (идентификаторы, классы, элементы):

  • p — вес (0, 0, 1);
  • .note — вес (0, 1, 0);
  • div p — вес (0, 0, 2);
  • .box .note — вес (0, 2, 0);
  • #page .box p — вес (1, 1, 1);
  • a:hover — вес (0, 1, 1).
p { color: red; }          /* (0,0,1) */
.note { color: green; }    /* (0,1,0) */
#intro { color: blue; }    /* (1,0,0) */

/* Для <p id="intro" class="note"> цвет будет синим */

Строковые стили, прописанные прямо в атрибуте элемента, весомее любых селекторов из таблиц, но слабее правил с важностью. Поэтому их использование осложняет жизнь и считается плохой практикой.

Директива important

Пометка important поднимает правило над всеми обычными. Соблазн использовать её велик, ведь она «просто работает». Но у неё есть цена: перебить такое правило можно только другим важным правилом с большей специфичностью или более поздним порядком. В итоге стили превращаются в гонку вооружений.

Правила здравого смысла:

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

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

Часть свойств передаётся от родителя к потомкам автоматически. Если вы задали цвет текста и шрифт для body, их унаследуют абзацы, заголовки и ссылки внутри. Это удобно: не нужно прописывать одно и то же для каждого элемента.

Но наследуются не все свойства. Условное правило такое: свойства, связанные с текстом, обычно наследуются, а связанные с блоками и компоновкой — нет.

НаследуютсяНе наследуются
colormargin и padding
font-family, font-sizeborder
line-heightbackground
text-alignwidth и height
visibilityposition

Для управления наследованием есть специальные значения. Значение inherit принудительно берёт значение у родителя. Значение initial сбрасывает свойство к стандартному. Значение unset ведёт себя как inherit для наследуемых свойств и как initial для остальных.

a { color: inherit; }
/* Ссылки будут того же цвета, что и окружающий текст */

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

Типичные ловушки

Слишком длинные селекторы

Цепочки вроде «body div.container ul li a» имеют высокий вес и тяжело поддаются переопределению. Потом приходится писать ещё более длинные цепочки. Придерживайтесь коротких селекторов на основе классов.

Использование идентификаторов для стилей

Идентификатор даёт очень большой вес, и переопределить его можно только таким же или важностью. Для оформления лучше применять классы, а идентификаторы оставлять для якорей и скриптов.

Порядок и подключение библиотек

Если стили фреймворка перекрывают ваши, не спешите добавлять important. Часто достаточно подключить свой файл после библиотеки или чуть усилить селектор.

Забытое наследование шрифтов в формах

Поля ввода и кнопки по умолчанию не наследуют шрифт страницы. Многие недоумевают, почему форма выглядит иначе. Решение простое: задать для таких элементов font-family со значением inherit.

Как отлаживать конфликты

Инструменты разработчика в браузере показывают, какие правила действуют на элемент, какие перечёркнуты и откуда пришло итоговое значение. Порядок действий при отладке:

  1. Откройте инструменты разработчика и выберите проблемный элемент.
  2. Посмотрите панель применённых стилей: перечёркнутые правила проиграли конфликт.
  3. Определите причину: меньшая специфичность, более ранний порядок или отсутствие наследования.
  4. Исправьте причину, а не симптом: измените селектор или порядок вместо добавления важности.
  5. Проверьте, не сломали ли соседние элементы.

Практики, которые упрощают жизнь

  • Держите специфичность низкой и ровной: опирайтесь на один класс на компонент.
  • Используйте понятную методологию именования, чтобы не плодить вложенные селекторы.
  • Группируйте общие стили в начале файла, частные — ближе к концу.
  • Задавайте базовые шрифты и цвета на body и пользуйтесь наследованием.
  • Применяйте переменные CSS для значений, которые повторяются.
  • Не стилизуйте через теги там, где можно использовать классы, иначе стили будут цеплять лишнее.

Заключение

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

ИБ
Ирина Белова
Автор SeoLiga.ru. Пишет о практике: что работает на реальных проектах, а что лучше обойти стороной. Хотите предложить свою статью? Загляните в раздел «Авторам».