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 (контекст инкапсуляции)

Оставить комментарий

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

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