CSS-live.ru

Важность !important: гарантия неизменяемости в CSS

Перевод статьи The Importance of !important: Forcing Immutability in CSS с сайта csswizardry.com, опубликовано на css-live.ru с разрешения автора — Гарри Робертса.

От одного моего совета у клиентов и слушателей волосы встают дыбом:

Я советую использовать !important.

Вообще-то я рад, что !important поначалу вызывает отвращение — обычно от него и вправду нельзя ждать ничего хорошего — но всё же с ним связано кое-что еще…

В жизни порой случаются исключительные обстоятельства, в которых следовать правилам — далеко не лучшая тактика, и лишь контекст да здравый смысл определяют, когда стоит их нарушить.

Например, в Великобритании действует ограничение скорости 70 миль/ч (увы, мы всё ещё используем мили). Быстрее ехать не разрешено. Это незаконно.

Однако… когда на заднем сиденье умирает ваш друг и нужно срочно доставить его в больницу, пока он не истёк кровью, приходится вдавить газ в пол.

В 90% случаях правило оправдано. В целом, надёжнее ему следовать. Но наверняка встретятся ситуации, не попадающие в 90%.

Точно так же, по мере профессионального роста мы осознаём, что при подходящих обстоятельствах у правил бывают исключения.

Мудрость приходит с возрастом (точнее, с опытом), так что начинающих разработчиков я бы учил не использовать !important вообще. Но когда разработчики взрослеют и понимают, что не всё делится на черное и белое, можно пуститься в более тонкие подробности и добавить несколько оговорок.

Но прежде чем увидеть исключение из правил, взглянем на само правило.

Никогда не используйте !important сгоряча

Пытаться выкрутиться с помощью !important — самая радикальная, отчаянная, запредельная мера, на которую мы можем пойти. Крайне нерационально использовать !important, чтобы отделаться от проблемы с существующим CSS. Это вызовет цепную реакцию и заставит добавить ещё !important, а затем ещё и ещё, до бесконечности.

Не хватайтесь чуть что за !important. Не решайте проблему специфичности с помощью него. Не используйте его сгоряча.

Хаки для специфичности

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

Если нужно повысить специфичность одного класа, можно продублировать его (например, .btn.btn {}). А чтобы понизить специфичность ID, можно переписать его в виде селектора атрибута (например, [id="header"] {}). Подробнее можете прочитать об этом в моей статье «Хаки для укрощения специфичности».

В большинстве случаев в !important нет необходимости.

Верно, но где он тогда нужен?

Гарантия неизменяемости с помощью !important

Идея неизменяемости мне очень созвучна, меня она просто восхищает. Идея, что что-то нельзя изменить после создания, звучит потрясающе. Насколько увереннее мы бы себя чувстовали, зная, что что-то выглядит и ведёт себя одинаково, независимо от места применения? Обожаю эту идею.

Обычно, с CSS такого добиться сложно, поскольку в нем всё изначально строится вокруг наследования и вовсю задействует изменения. Но есть определённый тип правила, который отлично использует неизменяемость, и практически безопасен: служебные классы.

Служебные классы — крошечные классы, выполняющие очень специфическую и чёткую работу. Это классы вроде:

.u-text-center { text-align: center; }

.u-float-left { float: left; }

.u-text-large { font-size: 48px; }

Все они начинаются с u-, чтобы разработчик сражу же понимал, для чего они, и каждый из них отвечает за один простой кусок оформления.

В этих примерах все правила объявлены без !important, но на самом деле их очень, очень нужно было бы объявить с ним. И вот почему:

Используя в HTML класс u-text-center, мы принимаем точное, ясное и однозначное решение, что текст должен выравниваться по центру. Без толики сомнений. Однако, у селектора .u-text-center {} низкая специфичность, поэтому есть вероятность, что другой селектор может случайно переопределить его. Возьмём такие HTML и CSS:

.sub-content h2 {
  ...
  text-align: left;
}

.u-text-center {
  text-align: center;
}
<div class="sub-content">
  <h2 class="u-text-center">...</h2>
</div>

К сожалению, здесь есть несоотвествие специфичности: у .sub-content h2 {} она выше, чем у .u-text-center {}, поэтому h2 в итоге выровнится по левому краю, несмотря на явное указание text-align: center;. Это тоже изменение: u-text-center больше не выравнивает по центру.

Вкратце, вот почему нужно вставлять !important в служебные стили. Нужно, чтобы служебные стили были неизменяемыми: не бывать такому, чтобы после применения класса u-text-center оказалось, что текст в блоке не отцентрирован.

Гарантируйте неизменяемость служебных классов, применяя !important в их объявлениях.

Конечно, в идеальном мире (что бы это ни было) у нас в CSS даже не было бы селектора .sub-content h2 {}, но это неизбежно.

  • кто-то подключается к разработке и в конце концов пишет такой селектор
  • похожий селектор уже был в какой-то устаревшей части проекта.

Гибкие и защищенные системы создаются не для идеального, а для реального мира, а в реальном мире люди пишут неаккуратный CSS. Неизменяемость благодаря !important — гарантия того, что никто не вызовет несоответствия или конфликта, даже случайно.

Обратите внимание на служебные классы

Полагаю, было бы не лишним немного рассказать про общее использование служебных классов.

Если мы не придерживаемся функционального CSS в стиле Tachions, Basscss или Atomic CSS (но это совсем другая тема), то нам, скорее всего, ни к чему много служебных классов в HTML.

Если вместо этого выбрать более модульный и компонентный подход в CSS (что мы скорее всего и делали), большинство из наших классов будут очень специальными, например:

.c-carousel {}

.o-media__body {}

.c-nav-primary {}

.c-btn--large {}

У них будет область видимости (блок, в БЭМ) и хорошая инкапсуляция. Красота классов вроде этого в том, что можно легко обнаружить взаимосвязи в HTML (что гораздо труднее — практически невозможно — с функциональным CSS), например:

<blockquote class="o-media  c-testimonial">
  <img class="o-media__img c-testimonial__photo" ... />
  <p class="o-media__body c-testimonial__text">...</p>
</blockquote>

Здесь можно ясно видеть взаимосвязь между двумя различными и раздельными ветками оформления.

Предположим, что у одного конкретного отзыва margin-bottom гораздо больше. Ни с одним другим отзывом этого делать не надо, и при этом ему нужен бОльший margin-bottom, когда он находится именно в этой части страницы. Такое изменение дизайна нужно только один раз и в одном месте.

Здесь как нельзя кстати придется служебный класс:

<blockquote class="o-media
                   c-testimonial
                   u-margin-bottom-large">

Мы используем служебный класс, поскольку дополнительный margin-bottom никак не связан с конкретным отзывом; это крайне контексто-зависимая и временная процедура, поэтому не следует жёстко вбивать это изменение в CSS нашего отзыва.

С другой стороны, если в стиле отзыва есть большой размер шрифта, и эту особенность можно придать любому отзыву, служебный класс ни к чему. Это не временная процедура, относящаяся к конкретному отзыву, поэтому нужно систематизировать и инкапсулировать его с помощью модификатора БЭМ:

<blockquote class="o-media
                   c-testimonial  c-testimonial--large">

Так что вот общее правило:

Если это неизменное оформление, оформляйте и кодируйте его прямо в CSS. Если же это кратковременное и разовое оформление, используйте служебный класс.

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

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

7 комментариев

      1. Всё понял, я просто не слишком понимал конструкцию .blue.red, которая задаёт вопрос про наличие сразу двух классов у элемента. При этом она просто опрашивает на наличия каждого класса отдельно. Соответственно при .blue.blue задаст этот вопрос дважды? чем увеличит специфичность селектора.
        Спасибо!

    1. Как представил, что кто то будет использовать повторы ID типа #id#id#id, так стало страшно. Страшный приём, никогда не буду использовать и постараюсь забыть, что так вообще можно.

      1. Утешает то, что в проекте с нормальной архитектурой такое безобразие и не понадобится, а где может понадобиться (для быстрого тяп-ляп-и-в-продакшн фикса какого-нибудь случайно попавшего на поддержку ископаемого барахла), там почти наверняка даже такой ужОс не сделает намного хуже, чем было.

        Но… наверное, «мои вкусы слишком специфичны» (каламбур, однако:), но почему-то я испытываю странное тайное удовольствие от вида этого извращения:). Что-то в нем есть от детской надежды на «волшебные слова»: «— Ну пожалуйста! — Нет! — Ну пожалуйста-пожалуйста! — Обойдешься! — Ну пожалуйста-пожалуйста-пожалуйста! — А, ладно, так и быть…» :)

        1. А чем принципиально плохо, вот, например, такое решение:
          Допустим нужно отменить инлайновые отступы в отдельно взятом меню, но есть понимание того, что это может понадобится и в другом месте кода. Ваяем такую конструкцию:

          .no_shift
          {
          font-size: 0;
          }
          .no_shift > *:not(.no_shift)
          {
          font-size: 1.6rem;
          }

          Логика, по-видимому, понятна :) Тем не менее, второе правило(для потомков), если я не ошибаюсь, будет иметь специфичность даже большую чем конкретно-заданный класс.
          Как же мне назначить нужный мне размер шрифта для класса, не думая, при этом, что избавление от отступов для предка сможет внести путаницу в мои настройки для потомков?
          Либо задавать для стиля !impotant, чего мне бы не хотелось, хотя я, и не очень представляю, навскидку, чему бы это могло мне помешать. Вы представляете?
          Либо удвоить специфичность для класса через дублирование обращения к классу, как и указано в статье.
          Второй вариант мне ближе, поскольку позволяет увеличивать специфичность более тонко. Врят ли, в реальной жизни мне потребуется более чем утроение специфичности, а !impotant я бы приберёг для классов, оформление которых просто и конкретно и должно работать при любых условиях (служебные классы в статье).
          Чем же плох такой подход? Чем плохо дублирование, кроме того, что выглядит не изящно?
          Или нужно как-то по-другому, к указанной проблеме подходить изначально?
          И стоит ли в стили, из разряда
          .big{font-size: 2rem} добавлять !impotant?

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

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

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