Если вы когда-нибудь подключали стороннюю библиотеку стилей и потом пытались её переопределить — вы знаете эту боль. Пишете правило, оно не работает. Добавляете класс поконкретнее — всё равно не работает. В итоге пишете !important и расстраиваетесь, потому что теперь нужно всё время учитывать это в дальнейшей работе.

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

В 2022 году в браузерах появился @layer — способ самим расставить приоритеты между группами стилей.

Попробуйте сами

Нажмите на кнопки, чтобы включать и выключать слои и посмотреть, как меняется результат:

Текст кнопки
все слои активны

Для обычных деклараций авторских стилей последующий слой имеет приоритет над предыдущим, даже если селектор предыдущего слоя специфичнее.

Как это работает

Сначала объявляем порядок слоёв одной строкой:

@layer reset, base, components, utilities;

Для обычных деклараций это как оглавление: utilities имеет приоритет над components, а тот — над base. Потом наполняем каждый слой стилями:

@layer reset {
  * { margin: 0; padding: 0; box-sizing: border-box; }
}

@layer base {
  body { font-family: sans-serif; color: #333; }
}

@layer components {
  .button { padding: 8px 16px; background: blue; color: white; }
}

@layer utilities {
  .hidden { display: none; }
}

Для обычных деклараций авторских стилей слой utilities имеет приоритет над components, даже если у components более специфичный селектор.

Главный сценарий — укрощение библиотек

Импортируем библиотеку в слой bootstrap, а собственные стили помещаем в следующий слой. @import находится на верхнем уровне, до блоков со стилями; объявление порядка слоёв можно поставить перед ним:

@layer reset, bootstrap, my-styles;

@import url("bootstrap.min.css") layer(bootstrap);

@layer my-styles {
  /* Обычные декларации этого слоя имеют приоритет над bootstrap. */
  .btn { background: green; }
}

Обычные декларации в слое my-styles имеют приоритет над обычными декларациями в bootstrap, независимо от специфичности селекторов.

Стили вне слоёв

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

@layer base {
  .title { color: blue; } /* проиграет */
}

/* Для обычных деклараций стиль вне слоя имеет приоритет. */
.title { color: red; }

Для деклараций с !important порядок слоёв обратный: первый слой важнее последующих, а важные декларации внутри слоёв имеют приоритет над важными декларациями вне слоёв. Обычная декларация в позднем слое не переопределит декларацию с !important в раннем.

Поддержка в браузерах

@layer работает в Chrome 99+, Firefox 97+, Safari 15.4+. Полная поддержка во всех современных браузерах. Актуально на caniuse.com.

Что запомнить

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

Чтобы лучше понять, как браузер вообще решает конфликты стилей, почитайте про специфичность селекторов и как работает каскад в CSS.


«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.

ТелеграмПодкастБесплатные учебники