CSS-live.ru

Почему у !important в CSS восклицательный знак в начале?

Непостижимые алгоритмы Твиттера принесли мне в ленту занятный вопрос Саймона Хойберга, автора нескольких книг про JavaScript:

Почему ‘!important’ перекрывает значения в css? 🤔

Для меня это читается как «not important», т.е. «не важно», и я ждал бы от него обратного 😅

Действительно, для программистов, привыкших обозначать знаком ! операцию отрицания (инверсии), эта запись выглядит очень нелогично. Ее даже включили в список ошибок, допущенных при проектировании CSS (последний пункт).

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

Всё дело в том, что конструкция !important, хотя и считается единой в синтаксисе, логически как бы состоит из двух частей. Одна — ключевое слово important, помечающее CSS-объявление как важное. А другая, ! — «оператор инверсии приоритета», меняющий на противоположный порядок важности почти во всех критериях каскада.

Смотрите сами. Берем самый свежий из почти готовых модулей каскада — 5 уровня:

  • Первый критерий каскада — происхождение стилей. Для обычных стилей авторские (т.е. стили сайта) приоритетнее пользовательских, а те — приоритетнее дефолтных стилей браузера. Для !important-стилей же самые приоритетные — браузерные, а самые «слабые» — стили сайта: всё наоборот!
  • Второй критерий — контекст инкапсуляции, т.е., по-простому, глубина вложенности теневых DOM-деревьев в «матрёшке» из нативных веб-компонентов. Опять же: для обычных свойств приоритетнее пришедшие из самого внешнего контекста, чтобы вся «матрёшка» была оформлена единообразно и выглядела цельной. А для !important-стилей, наоборот, приоритетнее стили самого внутреннего компонента! Чтобы что-то действительно важное для компонента сохранялось, в какой контекст бы его ни запихнули.
  • Третьим критерием отдельно вынесены стили из атрибута style: тут инвертировать нечего, так что инверсия ни на что не влияет.
  • А вот четвертый критерий — новинка сезона, каскадные слои. И снова та же логика: для обычных стилей «побеждают» те, чей слой указан последним, а для !important-ов — те, чей слой указан первым, наоборот!

Лишь два последних критерия каскада, специфичность селектора (пятый) и порядок появления в коде (шестой), !important не инвертирует: среди важных стилей, как и среди обычных, побеждает правило из более специфичного селектора, а при равной специфичности — идущее последним в коде. Но это логично: будь оно и здесь наоборот, первый !important-стиль в коде было бы вообще ничем не перебить.

Стоит заметить, что сами по себе специфичность и !important, строго говоря, друг с другом никак не связаны: специфичность бывает у селектора, а !important — у конкретного объявления (свойства со значением). Во многих учебных материалах наличие !important рассматривается как «еще одна цифра в механизме специфичности», но это большое переупрощение. Каскад куда интереснее!

Возвращаясь к первому критерию — происхождению стилей — давние читатели нашего сайта наверняка вспомнят два случая, занимающие в каскаде особое место — стили анимаций и переходов (transition). К счастью, в них !important не вмешивается: стили transition высчитываются браузером динамически для каждого кадра и «дописать» в них что-либо нельзя, а !important внутри @keyframes попросту невалиден. Так что стили переходов всегда будут перекрывать вообще всё — хоть с !important, хоть без, а анимации будут перекрывать все обычные стили, но перекрываться всеми важными. Баг с неправильным приоритетом анимаций (выше !important-стилей) остался только в Safari (обновлено 27.01.2023: баг пофикшен, ждем исправленного релиза!).

По-моему, такой взгляд на !important не только поможет легче запомнить очередность составляющих в каскаде (запоминаем «обычную» половину и добавляем «…а для !important всё наоборот»), но и нагляднее покажет, почему не стоит злоупотреблять им в повседневных задачах — ведь с инвертированным чем бы то ни было лучше не шутить:

Кадр из фильма «Довод» — схватка главного героя с самим собой, инвертированным во времени
Все ведь видели фильм про «инвертированное оружие»?..

Так что пусть !important останется только для действительно важных вещей, как гарантия их постоянства. А для повседневных задач в CSS-каскаде найдется немало других, не менее мощных и более полезных инструментов (кстати, самое время их осваивать 😉). А вы что думаете?

P.S. Это тоже может быть интересно:

2 комментария

  1. для обычных свойств приоритетнее пришедшие из самого внешнего контекста

    Хотелось бы увидеть пример работы каскада во вложенных деревьях Shadow DOM (контекст инкапсуляции)

Добавить комментарий для alex Отменить ответ

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.