Интеграция каскадных слоёв в существующий проект

Каскадные слои CSS (@layer) предоставляют эффективный механизм управления специфичностью и порядком применения стилей. Однако существующие материалы обычно демонстрируют их использование на новых проектах, тогда как большинство разработчиков работает с унаследованной кодовой базой. В статье на примере реального сайта рассматривается пошаговая стратегия внедрения Cascade Layers в существующий проект: от подготовительного рефакторинга до построения финальной архитектуры слоёв и решения сложных вопросов, возникающих в процессе миграции.

Введение

Каскадные слои CSS (@layer) — предоставляют эффективный механизм управления специфичностью и порядком применения стилей. Если вы хотите освежить в памяти, как вообще работает специфичность и каскад, рекомендуем начать с «Основ каскада и специфичности», а также разобрать типичные заблуждения, которые мешают правильно использовать слои.

Реальность разработки иначе: чаще всего мы работаем с кодовой базой, развившейся годами, содержащей множество компромиссных решений и технический долг. Как интегрировать каскадные слои в такой проект? Какие подводные камни ожидают разработчика? И главное — с чего вообще начинать этот процесс?

Цель статьи — ответить на эти вопросы на конкретном примере. Разберём рефакторинг реального проекта — сайта Discord-бота, разработанного Дриштантом Гхошем. Этот проект был выбран не случайно: при визуально безупречном результате его CSS-код демонстрирует типичные проблемы "органически выросших" таблиц стилей:

  • множественные дублирования селекторов;
  • избыточное использование ID-селекторов;
  • частое применение !important;
  • отсутствие единой архитектуры.

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

Материал будет полезен разработчикам, которые уже знакомы с синтаксисом @layer и ищут применимые на практике стратегии для внедрения этой технологии в существующие проекты.

Подготовка: рефакторинг существующего кода

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

Анализ исходного состояния

Рассматриваемый проект состоит из трёх основных файлов: index.html, index.css и index.js. CSS-файл содержит более 450 строк кода. При первичном анализе выявляются следующие закономерности, затрудняющие поддержку:

  • Множественные повторения селекторов, ссылающихся на одни и те же HTML-элементы;
  • Активное использование ID-селекторов (#loader, #page, #botLogo), что создаёт избыточно высокую специфичность;
  • Декларации !important, применённые без очевидной необходимости;
  • Дублирование правил: например, селектор #botLogo определён дважды с интервалом более 70 строк.

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

Приведение HTML-разметки к единообразию

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

В исходном коде обнаружены следующие элементы, требующие корректировки:

  • Селекторы id="loader" и id="page" заменены на классы .loader и .page соответственно. Такая замена снижает специфичность селекторов и повышает гибкость переиспользования стилей;
  • В разметке присутствовали несколько div-элементов без закрывающих тегов — они приведены к корректному виду;
  • Элемент <script> перемещён из блока .heading и расположен как прямой потомок body, что соответствует рекомендациям по загрузке скриптов.

Оптимизация CSS-кода

После приведения HTML к единообразию следует этап "санитарной обработки" таблицы стилей:

  • Удаление неиспользуемых селекторов. В процессе анализа обнаружены правила, не находящие применения в актуальной разметке. Их удаление сокращает объём кода и снижает когнитивную нагрузку при дальнейшей работе;
  • Объединение дублирующихся правил. Например, два блока с селектором #botLogo объединены в один, противоречия между ними разрешены;
  • Устранение избыточных деклараций !important. В большинстве случаев применение !important не было оправдано специфичностью или каскадом и могло быть удалено без последствий. Это критически важный шаг, поскольку, как будет показано далее, !important изменяет приоритеты внутри каскадных слоёв;
  • Снижение специфичности селекторов. Там, где это возможно, селекторы по возможности заменены на классы, а избыточно вложенные конструкции — упрощены.

Результат подготовительного этапа: мы имеем HTML с предсказуемой структурой классов и CSS-файл, очищенный от наиболее очевидных проблем. Код стал более прозрачным и готов к следующему шагу — проектированию архитектуры слоёв.

Проектирование архитектуры слоёв

После приведения кода в "чистое" состояние следующий логический шаг — разработка архитектуры каскадных слоёв. На этом этапе мы не пишем код, а определяем структуру, которая станет основой для дальнейшей миграции стилей.

Критически важные особенности каскадных слоёв

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

Приоритет слоёв определяется порядком объявления. Слои, объявленные позднее, имеют более высокий приоритет:

/* В этом случае new имеет более высокий приоритет */
@layer legacy, new;

/* А здесь - наоборот, legacy приоритетнее */
@layer new, legacy;

Модификация приоритетов при использовании !important. Декларации с !important следуют обратной логике: они получают тем более высокий приоритет, чем в более раннем слое находятся. Если в проекте останутся правила с !important, порядок приоритетов будет следующим (от высшего к низшему):

  1. !important-правила в самом раннем слое (legacy);
  2. !important-правила в следующем слое (new);
  3. Обычные правила в позднем слое (new);
  4. Обычные правила в раннем слое (legacy).

Именно поэтому подготовительный этап включал обязательное удаление избыточных !important — их наличие существенно усложняет предсказуемое поведение слоёв.

Выбор структуры слоёв

Для рассматриваемого проекта предлагается разделить все стили на пять слоёв, каждый из которых решает специфическую задачу:

Слой Назначение Состав
reset Нормализация стилей между браузерами Универсальный селектор (*), сброс отступов, box-sizing: border-box
base Глобальные стили по умолчанию Стили для HTML-элементов (body, h1, a, ссылки), базовая типографика, фоновые цвета, кастомный скроллбар
layout Крупные структурные блоки Сетка страницы, позиционирование основных разделов, навигационная панель как структурный элемент
components Переиспользуемые интерфейсные элементы Логотип, меню, кнопки, лоадер — всё, что может быть использовано в разных частях сайта
animations Динамические эффекты Все @keyframes и анимационные декларации

Объявление слоёв в начале файла будет выглядеть следующим образом:

@layer reset, base, layout, components, animations;

Обоснование предложенной структуры:

  • Слой reset закономерно размещается первым, поскольку задаёт базовую "чистую" основу;
  • base содержит глобальные стили, которые должны применяться ко всем элементам по умолчанию;
  • layout отделён от components, поскольку крупные структурные блоки обычно менее подвержены изменениям, чем конкретные компоненты;
  • animations вынесен в конец, так как анимации — это "декоративный" слой, который не должен конфликтовать со структурными и компонентными стилями.

Альтернативные подходы и обоснование выбора

Предложенная структура не является единственно возможной. Например, существуют подходы с выделением слоя утилитарных классов (.text-center, .mt-2) или разделением компонентов на под-слои:

@layer components {
@layer buttons, cards, menus;
}

От использования под-слоёв в данном проекте решено отказаться по следующим причинам:

  • Проект не обладает достаточной сложностью, чтобы оправдать многоуровневую вложенность;
  • Каждый дополнительный уровень абстракции увеличивает порог входа для новых разработчиков;
  • Под-слои целесообразно вводить при наличии формализованной дизайн-системы, что выходит за рамки рассматриваемого случая.

Результат этапа проектирования: у нас есть чёткая карта миграции, где каждому существующему стилю назначено целевое место в иерархии слоёв. Это позволяет перейти к непосредственному "переселению" кода.

Интеграция: распределение стилей по слоям

На данном этапе мы располагаем подготовленным кодом и утверждённой архитектурой слоёв. Задача — последовательно переместить стили в соответствующие слои, сохраняя функциональность на каждом шаге.

Базовые слои: reset и base

Первые два слоя формируют фундамент, на котором будет строиться всё остальное.

Слой reset. Сюда помещаются стили, обеспечивающие кроссбраузерную нормализацию:

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

Слой base. Содержит глобальные стили для HTML-элементов:

@layer base {
body {
background-image: url("bg.svg");
font-family: "Poppins", sans-serif;
/* остальные стили для body */
}

::-webkit-scrollbar {
width: 8px;
}

::-webkit-scrollbar-track {
background: #0e0e0f;
}

::-webkit-scrollbar-thumb {
background: #5865f2;
border-radius: 100px;
}

::-webkit-scrollbar-thumb:hover {
background: #202225;
}
}

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

Слой layout

В этот слой перемещаются стили, определяющие крупную структуру страницы. Критерий отнесения к layout: элемент формирует каркас и не предназначен для переиспользования в других контекстах.

@layer layout {
.page {
display: none;
}

nav {
display: flex;
height: 55px;
width: 100%;
padding: 0 50px;
/* остальные стили навигации как структурного элемента */
}
}

Слой components

Наиболее объёмный этап работы. В слой компонентов попадают все переиспользуемые элементы интерфейса. Для каждого компонента требуется предварительная "адаптация" кода.

Компонент лоадера. Ранее селектор #loader заменён на класс .loader, что позволяет разместить стили в компонентном слое:

@layer components {
.loader {
width: 100%;
height: 100vh;
/* остальные стили лоадера */
}

.loader .loading {
/* стили для внутреннего элемента */
}

.loader .loading span {
/* стили для span внутри лоадера */
}

.loader .loading span:before {
/* псевдоэлемент для анимации */
}
}

Компонент логотипа. Исходный код содержал три различных подхода к стилизации логотипа: nav .logo, .logo img и #botLogo. Такая множественность не только избыточна, но и затрудняет поддержку.

Решение: создание единого компонента с базовым классом и модификатором для специального контекста:

@layer components {
/* Базовая обёртка логотипа */
.logo {
font-size: 30px;
font-weight: bold;
color: #fff;
display: flex;
align-items: center;
gap: 10px;
}

/* Базовый класс для изображения логотипа */
.botLogo {
aspect-ratio: 1;
border-radius: 50%;
width: 40px;
border: 2px solid #5865f2;
}

/* Модификатор для крупного логотипа в заголовке */
.heading .botLogo {
width: 180px;
height: 180px;
background-color: #5865f2;
box-shadow: 0px 0px 8px 2px rgba(88, 101, 242, 0.5);
/* остальные стили для увеличенной версии */
}
}

Компонент навигационного меню. Исходный код использовал вложенные селекторы nav ul, что привязывало стили к конкретному контексту и повышало специфичность. Более предсказуемое решение — использование класса .mainMenu:

@layer components {
.mainMenu {
display: flex;
flex-wrap: wrap;
list-style: none;
}

.mainMenu li {
margin: 0 4px;
}

.mainMenu li a {
color: #fff;
text-decoration: none;
font-size: 16px;
/* остальные стили для ссылок меню */
}

.mainMenu li a:where(.active, .hover) {
color: #fff;
background: #1d1e21;
}

.mainMenu li a.active:hover {
background-color: #5865f2;
}
}

Псевдокласс :where() использован для сохранения низкой специфичности при группировке селекторов.

Кнопки управления меню. Два элемента — открывающая и закрывающая кнопки — тесно связаны с компонентом меню. Их стили также размещены в компонентном слое с использованием современного синтаксиса для группировки:

@layer components {
nav:is(.openMenu, .closeMenu) {
font-size: 25px;
display: none;
cursor: pointer;
color: #fff;
}
}

Слой animations

Финальный слой содержит анимационные правила, выделенные из основной таблицы стилей:

@layer animations {
@keyframes loading {
/* ключевые кадры для первой анимации */
}

@keyframes loading2 {
/* ключевые кадры для второй анимации */
}

@keyframes pageShow {
/* ключевые кадры для отображения страницы */
}
}

Вынесение @keyframes в отдельный слой решает две задачи: во-первых, отделяет динамические правила от статических, во-вторых, гарантирует, что анимации будут применены с правильным приоритетом (после всех структурных стилей).

Результат этапа интеграции: весь исходный CSS распределён по пяти слоям в соответствии с утверждённой архитектурой. Код стал предсказуемым, устранены дублирования и избыточная специфичность.

Сложные вопросы и их решения

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

Проблема размещения медиазапросов

Наиболее сложный вопрос, возникший в ходе рефакторинга: где должны располагаться медиазапросы? Существует два принципиально разных подхода:

Подход А: выделение отдельного слоя для адаптивности

@layer reset, base, layout, components, responsive, animations;

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

Подход Б: размещение медиазапросов в слоях соответствующих компонентов

@layer components {
.mainMenu {
display: flex;
/* базовые стили */
}

@media (max-width: 768px) {
.mainMenu {
display: none;
/* адаптивные стили здесь же */
}
}
}

Принятое решение и его обоснование. В данном проекте выбран второй подход по следующим причинам:

  1. Логическая связность. Стили, отвечающие за отображение компонента на разных устройствах, находятся в непосредственной близости от его базовых стилей. Это упрощает понимание полной картины поведения компонента.
  2. Предсказуемость переопределений. При изменении компонента разработчик видит все связанные с ним правила, включая адаптивные, что снижает риск внесения противоречий.
  3. Соответствие компонентному подходу. В современной фронтенд-разработке принято группировать всё, что относится к компоненту, в одном месте.

Плата за решение. Обратная сторона выбранного подхода — "размазывание" адаптивной логики по разным слоям. Для понимания того, как меняется сайт на разных разрешениях, требуется анализировать несколько файлов или разделов. Однако в контексте данного проекта это признано приемлемой платой за локальную связность.

Вопрос об утилитарных классах

В рассмотренном проекте отсутствуют утилитарные классы (такие как .text-center, .mt-2, .flex). Однако закономерен вопрос: куда их следует помещать при наличии?

В общей архитектуре утилитам отводится отдельный слой, размещаемый перед анимациями:

@layer reset, base, layout, components, utilities, animations;

Такое расположение обосновано функционально: утилиты должны иметь возможность переопределять стили компонентов (поэтому они расположены после components), но при этом анимации не должны конфликтовать с утилитарными классами (поэтому animations остаётся финальным слоем).

Управление специфичностью при работе с состояниями

При стилизации состояний (hover, active, focus) в рамках слоёв важно сохранять предсказуемость. Рекомендуется следующий подход:

@layer components {
.button {
background: #5865f2;
color: white;
}

.button:hover {
background: #4752c4; /* переопределение в том же слое */
}
}

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

Импорт внешних стилей и слоёв

В проекте использовался импорт шрифта Poppins. Важно отметить, что внешние ресурсы, подключаемые через @import или <link>, не попадают в создаваемые слои автоматически. Они остаются "вне слоёв" и, согласно спецификации, имеют приоритет над стилями в любых слоях.

При необходимости контролировать порядок применения внешних стилей их следует импортировать внутри соответствующих слоёв:

@layer base {
@import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
}

Работа с унаследованным кодом, содержащим !important

В процессе подготовительного этапа большинство деклараций !important были удалены. Однако в реальных проектах могут встречаться случаи, когда полное удаление невозможно (например, при интеграции сторонних библиотек).

В такой ситуации рекомендуется:

  1. Изолировать правила с !important в отдельном, максимально раннем слое;
  2. Документировать причину, по которой !important не может быть удалён;
  3. Стремиться к постепенному замещению таких правил при рефакторинге соответствующих компонентов.

Итоговые рекомендации по сложным случаям:

Ситуация Рекомендация
Медиазапросы Размещать в слоях соответствующих компонентов
Утилиты Выделить отдельный слой после компонентов
Состояния (:hover, :active) Оставлять в слое базового компонента
Внешние импорты Контролировать через размещение внутри слоёв
Не удаляемые !important Изолировать в раннем слое с документацией

Вопросы и ответы

Стоит ли внедрять каскадные слои в существующий проект, если он работает без видимых проблем?

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

Какой минимальный размер проекта оправдывает использование cascade layers?

Чёткого порога не существует, но практика показывает, что преимущества слоёв становятся ощутимы при превышении 300-400 строк CSS или при наличии более трёх-четырёх источников стилей (основной файл + библиотеки + критический CSS). В небольших проектах (до 200 строк) выгода от слоёв может не перевесить затраты на внедрение.

Как быть с !important, который действительно необходим (например, для переопределения сторонних стилей)?

В таких случаях !important следует изолировать в отдельном, максимально раннем слое, явно документировать причину его наличия и, по возможности, сопровождать комментарием о планируемом рефакторинге. Важно помнить, что каждое правило с !important снижает предсказуемость каскада.

Нужно ли распределять по слоям стили, подключённые из node_modules или внешних библиотек?

Рекомендуется импортировать внешние стили внутри соответствующих слоёв. Например, стили библиотеки компонентов можно разместить в слое components, а нормализацию вроде normalize.css — в слое reset. Это даёт контроль над порядком применения и возможность переопределения.

Как тестировать корректность внедрения слоёв?

Базовый подход — пошаговая проверка после перемещения каждого смыслового блока. Полезно использовать инструменты разработчика в браузере: на панели Styles видно, из какого слоя применяется правило. Для регрессионного тестирования рекомендуется создать скриншотные тесты ключевых состояний интерфейса до и после миграции.

Что делать с медиазапросами — оставлять в компонентах или выносить в отдельный слой?

Однозначного ответа нет, что подтверждается дискуссиями в профессиональном сообществе. Выбор зависит от приоритетов команды:

  • Если важна локальная связность компонентов — размещайте запросы внутри компонентов.
  • Если критично видеть адаптивность сайта как единое целое — создавайте отдельный слой.

В статье использован первый подход с соответствующим обоснованием.

Как мигрировать проект, если над ним работает несколько разработчиков одновременно?

В такой ситуации рекомендуется:

  1. Согласовать архитектуру слоёв на общем обсуждении;
  2. Выделить отдельную ветку для миграции;
  3. Провести подготовительный рефакторинг (Подготовка: рефакторинг существующего кода) как отдельный этап;
  4. Мигрировать слои последовательно, фиксируя результаты после каждого этапа;
  5. Провести командное ревью перед слиянием.
Поддерживают ли каскадные слои все современные браузеры?

На момент написания статьи каскадные слои поддерживаются во всех современных браузерах, включая последние версии Chrome, Firefox, Safari и Edge. Проблемы могут возникнуть только в устаревших браузерах (Internet Explorer, ранние версии мобильных браузеров). Для проектов, требующих широкой совместимости, следует использовать прогрессивное улучшение.

Заключение

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

Основные выводы

  1. Подготовка критически важнее самих слоёв. Успех миграции на @layer определяется не столько знанием синтаксиса, сколько качеством предварительного рефакторинга. Приведение HTML к единообразной структуре классов, удаление неиспользуемых правил и, главное, устранение избыточных !important — обязательные этапы, без которых внедрение слоёв теряет смысл.
  2. Архитектура слоёв требует предварительного проектирования. Спонтанное создание слоёв "по ходу дела" приводит к той же проблеме, которую призваны решить слои — к хаосу. Чёткое определение ответственности каждого слоя до начала миграции обеспечивает предсказуемый результат.
  3. Каскадные слои не решают проблемы автоматически. Они предоставляют инструмент для организации кода, но не заменяют дисциплину и архитектурное мышление. Проект, в котором стили хаотичны до применения слоёв, останется хаотичным и после, если не провести системную работу.

Рекомендации для внедрения в аналогичных проектах

На основе полученного опыта можно предложить следующий алгоритм действий при интеграции каскадных слоёв в существующий проект:

  1. Аудит текущего состояния. Выявить проблемные места: дублирование, избыточную специфичность, необоснованное использование !important.
  2. Подготовительный рефакторинг. Привести HTML к единообразию, удалить мёртвый код, объединить дубликаты, минимизировать !important.
  3. Проектирование архитектуры слоёв. Определить набор слоёв и их иерархию, зафиксировать ответственность каждого слоя.
  4. Поэтапная миграция. Перемещать стили в соответствующие слои последовательно, проверяя работоспособность после каждого этапа.
  5. Документирование принятых решений. Зафиксировать обоснования для неочевидных выборов (например, размещение медиазапросов).

Ограничения и области для дальнейшего изучения

Предложенный подход имеет определённые ограничения. Он ориентирован на проект средней сложности с монолитной таблицей стилей. Для крупных проектов с распределённой кодовой базой или использованием CSS-препроцессоров могут потребоваться дополнительные стратегии.

Дальнейшее изучение может быть направлено на:

  • интеграцию каскадных слоёв с методологиями типа БЭМ;
  • использование под-слоёв в крупных дизайн-системах;
  • взаимодействие слоёв с CSS-модулями и CSS-in-JS решениями;
  • стратегии тестирования при миграции на слои.

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

Бонус: Критические решения и их обоснование

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

Решение 1: Отказ от использования под-слоёв

В спецификации каскадных слоёв предусмотрена возможность создания вложенной структуры:

@layer components {
@layer buttons, cards, menus;
}

/* альтернативный синтаксис */
@layer components.buttons, components.cards, components.menus;

В рассматриваемом проекте этот механизм сознательно не применялся. Основания для отказа:

Критерий Оценка для данного проекта
Сложность кодовой базы Низкая — менее 20 компонентов
Наличие формальной дизайн-системы Отсутствует
Состав команды Один разработчик (поддержка)
Планируемая частота изменений Низкая

Введение под-слоёв создаёт дополнительный уровень абстракции, который в данном случае не компенсируется выигрышем в организации кода. Рекомендация: использовать под-слои только при наличии формализованной дизайн-системы и достаточной сложности проекта (от 50+ компонентов).

Решение 2: Отказ от отдельного слоя для утилит

Проект не содержал утилитарных классов, и введение слоя utilities "на вырост" признано нецелесообразным. Однако для проектов, активно использующих утилиты, рекомендуется следующая конфигурация:

@layer reset, base, layout, components, utilities, animations;

@layer utilities {
.text-center {
text-align: center;
}

.mt-2 {
margin-top: 0.5rem;
}

/* прочие утилитарные классы */
}

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

Решение 3: Размещение медиазапросов внутри компонентов

Это решение было наиболее дискуссионным. Ниже представлен сравнительный анализ обоих подходов:

Аспект Отдельный слой responsive Запросы внутри компонентов
Локализация изменений Рассредоточена В одном месте
Понимание адаптивности сайта в целом Высокое Низкое
Понимание адаптивности компонента Низкое Высокое
Сложность рефакторинга компонента Высокая Низкая
Предсказуемость каскада Средняя Высокая

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

Решение 4: Обработка псевдоэлементов и псевдоклассов

В проекте использован псевдокласс :where() для группировки селекторов без повышения специфичности:

.mainMenu li a:where(.active, .hover) {
color: #fff;
background: #1d1e21;
}

Это решение обосновано стремлением сохранить предсказуемость переопределений. Использование :where() даёт специфичность 0, что позволяет легко переопределять эти стили при необходимости.

Решение 5: Сохранение дублирования при невозможности объединения

В некоторых случаях полное устранение дублирования невозможно без изменения HTML-структуры. Например, в компоненте логотипа сохранены два селектора (.botLogo и .heading .botLogo), поскольку они обслуживают разные контексты использования.

Критерий допустимого дублирования: если два правила относятся к разным функциональным ролям компонента (базовая и модифицированная версия), их раздельное существование оправдано.

Итоговая таблица принятых решений:

Область Принятое решение Альтернатива Ключевой фактор выбора
Структура слоёв Плоская, 5 слоёв Иерархическая с под-слоями Сложность проекта
Утилиты Не выделены Отдельный слой Отсутствие в проекте
Медиазапросы Внутри компонентов Отдельный слой Приоритет локальной связности
Специфичность состояний :where() для группировки Вложенные селекторы Предсказуемость
Дублирование Допустимо по контексту Полное устранение Техническая возможность

Комментарии


Дополнительные материалы

Предыдущая Статья

CSS `@scope`: Как изолировать стили без BEM и Tailwind

Следующая Статья

Модальное окно или диалог: как выбрать и не сломать доступность