Принцип каскада

Один из основных принципов работы CSS. Стили читаются и применяются сверху вниз.

Время чтения: 6 мин

Если вы так боитесь каскада, что отказываетесь его изучать, вы не понимаете веба и лучше вам посмотреть в сторону чего-нибудь попроще в другой области кодинга. Спасибо, что прослушали мой доклад для фронтендеров.

— Эрик Мейер, автор «CSS: The Definitive Guide», источник

Большой Каскад в Петергофе

Каскад — одно из важнейших понятий в CSS. Само название CSS — Cascading Style Sheets, «каскадные таблицы стилей» явно упоминают это неочевидное определение.

Точнее, в определении каскада принимает участие не только специфичность, но и в первую очередь источник этих правил и область видимости. Также имеет важное значение порядок в коде (какие свойства были объявлены «позже», те «важнее»).

Источники правил

Скопировано

Чем выше — тем «важнее»:

  1. Стили во время CSS-переходов (transition);
  2. Браузерные стили с !important;
  3. Пользовательские стили с !important;
  4. Авторские (мы с вами пишем именно эти стили) стили с !important;
  5. Стили во время анимаций (animation);
  6. Обычные (т. е. без !important) авторские стили;
  7. Обычные пользовательские стили;
  8. Обычные браузерные стили.

Иногда браузеры нарушают этот порядок — порой осознанно (например, могут запретить делать слишком мелкий шрифт в полях ввода, как это делал Google Chrome) или по ошибке (до недавнего времени стили при анимации перекрывали остальные стили только в Firefox).

Область видимости

Скопировано

Понимание области видимости лучше всего начать с указания стилей в атрибуте тега style — их область видимости ограничена только тем тегом, в котором они указаны. Т. е. если вы описали style тегу <li>, то эти стили применятся только к этому тегу, остальные <li> не «увидят» эти свойства:

        
          
          <ul>  <li>Этот пункт имеет CSS-свойства по умолчанию</li>  <li>И этот тоже</li>  <li style="color: #2E9AFF">Текст в этом (и только в этом) теге будет синим</li>  <li>Снова обычное оформление</li></ul>
          <ul>
  <li>Этот пункт имеет CSS-свойства по умолчанию</li>
  <li>И этот тоже</li>
  <li style="color: #2E9AFF">Текст в этом (и только в этом) теге будет синим</li>
  <li>Снова обычное оформление</li>
</ul>

        
        
          
        
      
Открыть демо в новой вкладке

Со свойствами, описанными через классические CSS-селекторы (классы, теги, id, атрибуты) иначе — чем точнее мы описываем правило для селектора, тем сильнее мы суживаем область видимости (т. е. как бы точнее прицеливаемся), тем самым увеличивая и приоритетность правила. Но это работает до тех пор, пока CSS-правило не содержит «подлого» !important. Именно поэтому их использование считается плохой практикой — они «меняют правила игры». Это правило мгновенно становится приоритетнее, но чем шире область видимости — тем оно важнее 🤯

Специфичность

Скопировано

Самое простое и очевидное понятие в определении каскада. Когда мы описываем стили (инлайном в теге или на селектор в <style> или файле), каждое правило имеет свой вес. Правила, описанные инлайном перебивают правила на селектор, но не отменяют правила с !important (отменяют, если инлайн-правило тоже описано с !important).

Установим цвет текста абзаца в красный:

        
          
          <style>p {  color: red;}</style><p>  Цвет текста будет красным — единственное правило описано  в теге &lt;style&gt;.</p>
          <style>
p {
  color: red;
}
</style>

<p>
  Цвет текста будет красным — единственное правило описано
  в теге &lt;style&gt;.
</p>

        
        
          
        
      

Теперь переопределим цвет текста более специфичным инлайн-правилом:

        
          
          <style>p {  color: red;}</style><p style="color: blue">  Цвет текста стал синим — инлайн «перебивает» &lt;style&gt;.</p>
          <style>
p {
  color: red;
}
</style>

<p style="color: blue">
  Цвет текста стал синим — инлайн «перебивает» &lt;style&gt;.
</p>

        
        
          
        
      

Повысим специфичность, добавив !important в правила селектора:

        
          
          <style>p {  color: red !important;}</style><p style="color: blue">  Цвет текста снова стал красным за счёт !important в &lt;style&gt;.</p>
          <style>
p {
  color: red !important;
}
</style>

<p style="color: blue">
  Цвет текста снова стал красным за счёт !important в &lt;style&gt;.
</p>

        
        
          
        
      

Последний «шанс» разработчику повлиять на правило — добавить !important в инлайн-стиль:

        
          
          <style>p {  color: red !important;}</style><p style="color: blue !important">  Цвет текста снова синий — !important инлайн-правила «перебивает» даже  !important в &lt;style&gt;.</p>
          <style>
p {
  color: red !important;
}
</style>

<p style="color: blue !important">
  Цвет текста снова синий — !important инлайн-правила «перебивает» даже
  !important в &lt;style&gt;.
</p>

        
        
          
        
      

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

Подробнее про специфичность можно прочитать в статье «Специфичность».

Порядок в коде

Скопировано

Тут ещё проще — при равной специфичности правила, написанные ниже по ходу чтения переопределяют написанное выше:

        
          
          <style>.color_red {  /* Определяем начальное значение */  color: red;}.color_red {  /* Переопределяем написанное ранее правило */  color: blue;}</style><p class="color_red">Цвет текста — синий!</p>
          <style>
.color_red {
  /* Определяем начальное значение */
  color: red;
}

.color_red {
  /* Переопределяем написанное ранее правило */
  color: blue;
}
</style>

<p class="color_red">Цвет текста — синий!</p>

        
        
          
        
      

Влияние на каскад 👻

Скопировано

Помимо знания как и где написать селектор, можно управлять каскадом с помощью CSS-свойств!

Начальное значение — initial

Скопировано

Любое CSS-правило можно сбросить на начальное значение — то, которое было у правила до того как было установлено явно разработчиком. color станет #000, positionstatic, displayinline, padding и margin0 и так далее.

Заимствование у родителя — inherit

Скопировано

Некоторые свойства наследуются от родителя автоматически (color, font, text-align и другие). Но можно и явно позаимствовать какое-то значение у родителя:

        
          
          <div class="wrapper">  <div class="inner"></div></div>
          <div class="wrapper">
  <div class="inner"></div>
</div>

        
        
          
        
      
        
          
          .wrapper {  background-color: #2E9AFF;  border: 10px solid #FFFFFF;}.inner {  background-color: #F498AD;  border: inherit;}
          .wrapper {
  background-color: #2E9AFF;
  border: 10px solid #FFFFFF;
}

.inner {
  background-color: #F498AD;
  border: inherit;
}

        
        
          
        
      
Открыть демо в новой вкладке

Отмена значения — unset

Скопировано

По своему поведению unset чем-то похож на initial + inherit — если это свойство ненаследуемое от родителя — оно сбросится до начального значения, наследуемое — получит значение родителя.

Сброс значения — revert

Скопировано

Это менее строгий режим сброса — он отменяет все установленные разработчиком значения для данного свойства до значения, которое считается браузером «значением по умолчанию».

Подсказки

Скопировано

💡 В CSS есть такое волшебное свойство — all — это своеобразный шорткат (сокращённый формат записи), который внутри себя содержит все-все CSS-свойства. В сочетании с initial, inherit, unset или revert это позволяет повлиять на каскад в одну строчку.

Все наследуемые свойства .widget и вложенных элементов будут сброшены:

        
          
          .widget {  all: revert;}
          .widget {
  all: revert;
}

        
        
          
        
      

На практике

Скопировано

Николай Лопин советует

Скопировано

🛠 Если вы знакомы с JavaScript, то каскад можно представить как создание объекта через спред-синтаксис на основе приоритета стилей:

        
          
          const styles = {  ...inheritedStyles,  ...tagStyles,  ...classStyles,  ...idStyles,  ...inlineStyles,  ...importantStyles,  ...transitionStyles}
          const styles = {
  ...inheritedStyles,
  ...tagStyles,
  ...classStyles,
  ...idStyles,
  ...inlineStyles,
  ...importantStyles,
  ...transitionStyles
}

        
        
          
        
      

Realetive советует

Скопировано

🛠 Понимание каскада — один из ключевых моментов в понимании работы CSS. С опытом вы научитесь так эффективно писать селекторы и группировать стили, что сам уровень каскада будет минимальным — это ускоряет «чтение» кода и упрощает поддержку. Ну и в идеале — исключить необходимость использования !important.