26 Модуль 6 Современный CSS 18 мин урок 1 из 25

Cascade layers: наводим порядок в каскаде

Твой CSS разросся, новое правило не применяется, и ты дописываешь !important. Слои каскада убирают эту войну селекторов целиком.

Что ты научишься делать

  • Разложишь стили по слоям так, чтобы порядок побеждал специфичность
  • Перестанешь дописывать !important, чтобы перебить чужое правило
  • Переведёшь существующий проект на слои, не тронув ни одного селектора

#Знакомая история

Проект живёт полгода. В стилях три тысячи строк. Ты добавляешь .button { background: teal; } — и кнопка остаётся серой.

Открываешь инструменты разработчика: правило зачёркнуто, победило .sidebar .panel .button из файла, который писал не ты. Ты дописываешь !important. Через месяц кто-то дописывает !important поверх твоего.

Это не проблема дисциплины. Это проблема архитектуры: у каскада нет способа сказать «сброс всегда слабее компонентов». Слои каскада (cascade layers) его дают.

#Что делает @layer

Слой — это именованная группа правил. Порядок объявления слоёв важнее любой специфичности внутри них.

Правило из слоя components побеждает правило из base всегда — даже если в base селектор из трёх идентификаторов, а в components один класс.

CSSstyle.css
/* Один раз объявляем порядок — дальше он нерушим */@layer reset, base, components, utilities;@layer base {  /* Даже такой тяжёлый селектор проиграет слою components */  #app .sidebar .panel .button { background: #999; }}@layer components {  .button { background: teal; }}
Слой побеждает специфичность
В песочницу

#Как это выглядит в реальном проекте

Рабочий набор слоёв почти всегда один и тот же. Порядок читается сверху вниз как «от самого слабого к самому сильному».

CSSapp.css
@layer reset, tokens, base, layout, components, utilities;@import url("reset.css")      layer(reset);@import url("tokens.css")     layer(tokens);@import url("typography.css") layer(base);@import url("grid.css")       layer(layout);/* Чужая библиотека целиком уезжает в слабый слой */@import url("vendor/datepicker.css") layer(vendor);@layer components {  .card { padding: 20px; border-radius: 14px; }  .card__title { margin: 0 0 8px; font-size: 1.1rem; }}@layer utilities {  /* Утилита обязана побеждать компонент — поэтому слой последний */  .is-hidden { display: none; }  .text-center { text-align: center; }}

#Вложенные слои

Слой можно разбить на подслои через точку. Порядок внутри работает по тем же правилам, а снаружи вся группа остаётся одним слоем.

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

CSScomponents.css
@layer components {  @layer elements, blocks, pages;}@layer components.elements {  .button { padding: 10px 18px; border-radius: 8px; }}@layer components.pages {  /* Страничное правило перекроет общее, но останется     слабее всего слоя utilities */  .checkout .button { width: 100%; }}

Есть и анонимные слои — просто @layer { … } без имени. Такой слой нельзя дополнить позже и нельзя переупорядочить: он занимает место там, где написан.

Применение у них одно: изолировать кусок стороннего кода, к которому больше не планируешь возвращаться. Для своего кода имена почти всегда лучше.

#Как перевести существующий проект

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

  1. Объяви порядок первой строкой

    В главный файл, до всех импортов: @layer reset, vendor, base, layout, components, utilities;. Пока ничего не изменилось — слои объявлены, но пусты.

  2. Загони чужое в vendor

    Все библиотеки подключи как @import url("lib.css") layer(vendor). Уже на этом шаге исчезнет половина твоих !important.

  3. Перенеси сброс

    Оберни reset.css или normalize.css в слой reset. Теперь сброс перестанет спорить с компонентами.

  4. Раздели своё

    Типографика и теговые селекторы — в base. Сетки и контейнеры — в layout. Всё, у чего есть класс-компонент, — в components.

  5. Утилиты — последними

    Классы вроде .is-hidden и .text-center положи в utilities. Они должны побеждать компоненты, и теперь побеждают без !important.

  6. Убери важные объявления

    Пройдись по !important и удаляй по одному, проверяя страницу. Большая часть окажется лишней — её работу теперь делает порядок слоёв.

Интерактивный разбор доступен при включённом JavaScript.

#Когда НЕ надо

Слои — инструмент против большого чужого кода. У маленького проекта их цена выше пользы.

  • Лендинг на 200 строк. Порядок правил и так виден целиком. Слои добавят уровень вложенности и ничего не решат.
  • Как замена дисциплине. Если внутри слоя components продолжают писать селекторы из пяти частей, война просто переедет внутрь слоя.
  • Ради «модно». Слои усложняют отладку: в инструментах разработчика придётся смотреть не только на специфичность, но и на слой. Заводить их стоит, когда боль уже есть.

#Что слои не чинят

Слои управляют только приоритетом правил. Всё остальное остаётся на тебе.

Они не уменьшают количество кода: три тысячи строк останутся тремя тысячами, просто перестанут спорить друг с другом. Они не наводят порядок в именах: .block-2-final в слое components так и останется непонятным. И они не отменяют наследование — унаследованный color приходит от родителя мимо всех слоёв.

Ещё одна тонкость: слои никак не влияют на порядок загрузки. Если файл подключён позже, его правила всё равно применятся позже — просто внутри своего слоя.

Так не надо

.sidebar .card .title {
  font-size: 18px !important;
}

Классический ответ на «стиль не применяется»: утяжелить селектор и добавить важность. Работает ровно до следующего такого же правила.

Так надо

@layer components {
  .card__title { font-size: 18px; }
}

Один класс, никакой важности. Правило победит базовый слой по определению, а не по весу селектора.

#Поддержка браузерами

БраузерСостояние
Chrome, EdgeПоддерживают с 2022 года
FirefoxПоддерживает с 2022 года
SafariПоддерживает с 2022 года, включая мобильный
Старые версииПравила внутри @layer просто игнорируются целиком

Где спотыкаются почти все

Порядок слоёв объявлен не первой строкой

Почему Порядок задаётся тем, где слой впервые упомянут. Если @layer utilities встретился раньше components, утилиты станут слабее — и никто не поймёт почему.

Как надо Первой строкой файла объявляй порядок целиком: @layer reset, base, components, utilities;

Половина стилей осталась вне слоёв

Почему Внеслойные правила сильнее любого слоя. Получается та же война, только теперь ещё и неочевидная.

Как надо Либо всё в слоях, либо вне слоёв держи один осознанный набор — например, стили для печати.

!important внутри слоя ведёт себя «наоборот»

Почему Для важных объявлений порядок слоёв переворачивается: !important в самом слабом слое побеждает !important в сильном. Это логично (слабый слой не должен терять свои крайние меры), но неожиданно.

Как надо Не смешивай !important со слоями. Если завёл слои — важные объявления почти всегда лишние.

Слой каскада cascade layer
Именованная группа правил. Порядок слоёв важнее специфичности селекторов внутри них.
Внеслойные стили unlayered styles
Правила вне любого @layer. Считаются самым сильным слоем.
Слой vendor vendor layer
Приём: чужую библиотеку подключают в отдельный слабый слой, чтобы она не перебивала свои стили.

Проверь себя

  1. Правило <code>#app .panel .button</code> лежит в слое base, а <code>.button</code> — в слое components. Порядок: <code>@layer base, components;</code>. Кто победит?

    В этом и смысл слоёв: порядок объявления сильнее специфичности. Внутри одного слоя специфичность снова начинает работать как обычно.

  2. Где окажется правило, написанное вообще без @layer?

    Внеслойные стили считаются последним слоем. Сделано ради совместимости со старым кодом, но об этом легко забыть.

  3. Как подключить чужую библиотеку так, чтобы она не перебивала твои стили?

    Библиотека уезжает в слабый слой целиком — и любой твой компонентный класс её перекрывает.

  4. Что произойдёт в браузере, который не знает @layer?

    Непонятное @-правило игнорируется целиком, вместе с содержимым. Со слоями это опаснее, чем с обычными свойствами.

Задание

Разложить свалку по слоям

В CSS справа четыре группы правил, и они дерутся: сброс перебивает компоненты, утилита не работает. Расставь слои так, чтобы кнопка стала бирюзовой, а класс .is-hidden действительно прятал элемент.

Условие: не меняй ни одного селектора — только оберни правила в @layer и объяви порядок.

Открыть в песочнице
    Подсказка 1

    Первой строкой: @layer reset, base, components, utilities; — дальше просто разложи существующие правила по этим слоям.

    Подсказка 2

    Правило для button — это сброс, два тяжёлых селектора — база, .button с отступами — компонент, .is-hidden — утилита.

    Показать решение
    HTMLindex.html
    <button class="button">Кнопка</button><p class="note is-hidden">Этот абзац должен быть скрыт</p>
    CSSstyle.css
    @layer reset, base, components, utilities;@layer reset {  button { background: #999; color: #fff; }}@layer base {  #app .button,  .panel .button { background: #777; }}@layer components {  .button {    padding: 10px 18px;    background: teal;    border: 0;    border-radius: 8px;    font: inherit;    color: #fff;  }}@layer utilities {  .is-hidden { display: none; }}

    Проверь себя