Почему у !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. Это тоже может быть интересно:
Хотелось бы увидеть пример работы каскада во вложенных деревьях Shadow DOM (контекст инкапсуляции)
Конструктивное замечание, вот-вот будет обновление с примером!