HTML/CSSMiddleTechnical

Что такое cascade layers (@layer) и как они помогают управлять CSS в больших проектах?

CSS Cascade Layers (@layer) позволяют явно задать приоритет групп стилей независимо от специфичности селекторов. Слои объявляются в порядке возрастания приоритета: последний объявленный слой побеждает при конфликте.

Что такое CSS Cascade Layers

Директива @layer (Cascade Layers, CSS Cascading and Inheritance Level 5) позволяет разработчику явно управлять порядком каскада, создавая именованные слои. Стили внутри слоя с более высоким приоритетом (объявленного позже) побеждают стили из более низких слоёв, независимо от специфичности.

Синтаксис

/* 1. Объявить порядок слоёв (обязательно в начале файла) */
@layer reset, tokens, base, components, utilities;

/* 2. Заполнить слои */
@layer reset {
  *, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }
}

@layer tokens {
  :root {
    --color-primary: #c28b00;
    --color-text: #1a1a1a;
    --radius-md: 12px;
    --spacing-4: 1rem;
  }
}

@layer base {
  body {
    font-family: 'Rubik', sans-serif;
    color: var(--color-text);
    line-height: 1.5;
  }

  h1, h2, h3 { font-weight: 600; }
}

@layer components {
  .card {
    background: #fff;
    border-radius: var(--radius-md);
    padding: var(--spacing-4);
    box-shadow: 0 1px 4px rgba(0,0,0,0.1);
  }

  /* Высокая специфичность, но слой низкий */
  .card .card__title.card__title--featured {
    color: var(--color-primary);
  }
}

@layer utilities {
  /* Низкая специфичность, но слой высокий — всегда побеждает */
  .mt-4 { margin-top: var(--spacing-4) !important; }
  .text-primary { color: var(--color-primary); }
}

Ключевое свойство: приоритет по порядку объявления

@layer A, B;

@layer A {
  p { color: blue; } /* specificity: 0,0,1 */
}

@layer B {
  p { color: red; }  /* specificity: 0,0,1 — тот же */
}

/* Результат: red — слой B объявлен позже */

Стили вне любого слоя (unlayered) имеют наивысший приоритет — выше всех @layer:

@layer components {
  .card { background: white; }
}

/* Unlayered — побеждает даже с той же специфичностью */
.card { background: #f5f5f5; }

Интеграция с @import

/* Подключить стороннюю библиотеку в слой с низким приоритетом */
@import url('bootstrap.min.css') layer(vendor);
@import url('normalize.css') layer(reset);

@layer reset, vendor, base, components, utilities;

/* Теперь наши компоненты всегда побеждают Bootstrap */
@layer components {
  .btn { background: var(--color-primary); }
}

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

@layer components {
  @layer form {
    .input { border: 1px solid #ddd; }
  }

  @layer button {
    .btn { cursor: pointer; }
  }
}

/* Обращение к вложенному слою извне */
@layer components.form {
  .input:focus-visible { border-color: var(--color-primary); }
}

Практический пример: Tailwind + кастомные компоненты

/* globals.css */
@import 'tailwindcss/base' layer(tw-base);
@import 'tailwindcss/components' layer(tw-components);
@import 'tailwindcss/utilities' layer(tw-utilities);

@layer reset, tw-base, tw-components, project-components, tw-utilities, project-utilities;

@layer project-components {
  /* Компоненты проекта выше Tailwind компонентов, но ниже утилит */
  .job-card {
    border-radius: 12px;
    padding: 1.5rem;
    background: white;
  }
}

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

  • Chrome 99+, Firefox 97+, Safari 15.4+
  • Глобальная поддержка: ~92% (caniuse.com, 2026)
  • Polyfill: @csstools/postcss-cascade-layers для старых браузеров
// postcss.config.mjs
import postcssCascadeLayers from '@csstools/postcss-cascade-layers';

export default {
  plugins: [
    postcssCascadeLayers(),
  ],
};

Подводные камни

  • Unlayered стили побеждают все @layer: если подключить стороннюю библиотеку без layer(), она автоматически получает наивысший приоритет — всегда оборачивайте vendor CSS в слой.
  • Порядок объявления @layer: если один файл объявляет @layer A, B, а другой — @layer B, A, итоговый порядок определяется первым встреченным объявлением — легко запутаться при разбивке CSS на файлы.
  • @import layer и CORS: @import url(...) layer() для cross-origin ресурсов требует корректных CORS-заголовков, иначе стили не загрузятся.
  • DevTools и @layer: в Chrome DevTools стили внутри слоёв отображаются с пометкой слоя, но это сравнительно новый UI — часть команды может не знать, как читать приоритеты.
  • !important внутри @layer: !important инвертирует порядок слоёв — !important из нижнего слоя побеждает !important из верхнего. Это контринтуитивно и ломает ожидания.
  • PostCSS-полифил и динамические слои: полифил @csstools/postcss-cascade-layers работает статически во время сборки и не обрабатывает слои, добавленные через JavaScript (CSSStyleSheet API).

Common mistakes

  • Отвечать определением без production-сценария.
  • Не называть runtime boundary, security boundary или failure mode.
  • Игнорировать версию API, observability и тестовую проверку.

What the interviewer is testing

  • Объясняет механизм своими словами и без выдуманных API.
  • Называет реальные риски, диагностику и критерий корректности.
  • Связывает ответ с текущей документацией и миграционными ограничениями.

Sources

Related topics