CSS Modules: базовая поддержка

CSS Modules в связке с esbuild представляют собой механизм локальной инкапсуляции CSS-классов, при котором имена стилей преобразуются в уникальные идентификаторы, а исходные названия экспортируются как значения модуля. Такой подход устраняет конфликты имён в глобальной области CSS и упрощает поддержку крупных интерфейсных приложений.

CSS Modules рассматривают каждый CSS-файл как модуль, в котором классы становятся локальными сущностями. При обработке файла сборщик:

  • анализирует селекторы классов;
  • генерирует уникальные имена;
  • создаёт JavaScript-объект экспорта;
  • связывает оригинальные имена с преобразованными значениями.

В результате импорт CSS перестаёт быть побочным эффектом подключения стилей и превращается в структурированное взаимодействие через объект.

Включение поддержки CSS Modules в esbuild

В esbuild поддержка CSS Modules реализуется на уровне обработки CSS-файлов и может активироваться разными способами в зависимости от конфигурации.

На практике используются два основных подхода:

Файловое соглашение

Файлы с расширением вида:

  • styles.module.css

рассматриваются как CSS Modules автоматически. Такой подход основан на соглашении именования и позволяет разделять глобальные стили и модульные.

Конфигурация сборки

При использовании API esbuild возможно включение режима модулей через параметры обработки CSS. В зависимости от версии и окружения сборки применяется настройка, активирующая модульную интерпретацию CSS-файлов.

Экспорт классов из CSS

CSS Modules преобразуют классы в свойства экспортируемого объекта.

Пример CSS-файла

.button {
  padding: 12px 16px;
  background: #2b6cb0;
  color: white;
}

.primary {
  background: #1a202c;
}

Использование в JavaScript

import styles from "./button.module.css";

const el = document.createElement("button");
el.className = styles.button;
el.textContent = "OK";

document.body.appendChild(el);

После сборки styles.button будет заменён на сгенерированную строку, например:

_button_button__a1b2c

Генерация уникальных имён классов

Одной из ключевых задач CSS Modules является предотвращение конфликтов имён. esbuild генерирует хешированные идентификаторы на основе:

  • имени файла;
  • имени класса;
  • внутреннего хеша содержимого;
  • параметров сборки.

Итоговое имя класса становится локально уникальным в рамках проекта.

Это позволяет безопасно использовать одинаковые названия классов в разных файлах:

/* header.module.css */
.title { font-size: 20px; }

/* footer.module.css */
.title { font-size: 14px; }

В результате:

import header from "./header.module.css";
import footer from "./footer.module.css";

header.title !== footer.title;

Локальная область видимости

CSS Modules изолируют селекторы внутри файла. По умолчанию:

  • все классы считаются локальными;
  • глобальный CSS не влияет на модуль;
  • модуль не загрязняет глобальную область имён.

Такой подход уменьшает необходимость использовать методологии типа BEM для предотвращения конфликтов.

Использование :global и :local

Для управления областью видимости в CSS Modules применяются специальные псевдоклассы.

:global

Позволяет явно указать глобальный селектор:

:global(.reset) {
  margin: 0;
  padding: 0;
}

Такой класс не будет преобразован и останется доступным глобально.

:local

Является противоположностью и подчёркивает локальность:

:local(.wrapper) {
  display: flex;
}

В большинстве случаев :local является поведением по умолчанию, но его использование улучшает читаемость в смешанных стилях.

Композиция классов (composes)

CSS Modules поддерживают композицию стилей, при которой один класс наследует стили другого.

.base {
  padding: 8px;
  border-radius: 4px;
}

.success {
  composes: base;
  background: green;
}

После обработки класс success будет включать стили base, но при этом сохранит собственный набор правил.

В Jav * aScript:

el.className = styles.success;

Экспорт нескольких классов

CSS Modules экспортируют все классы как свойства одного объекта:

.card { }
.title { }
.body { }
import card from "./card.module.css";

card.card;
card.title;
card.body;

Такой подход обеспечивает единый интерфейс доступа к стилям и упрощает масштабирование структуры UI.

Работа с несколькими классами

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

el.className = `${styles.button} ${styles.primary}`;

или более сложные условия:

el.className = [
  styles.button,
  isActive && styles.active
].filter(Boolean).join(" ");

Интеграция с бандлингом esbuild

При использовании esbuild CSS Modules обрабатываются на этапе сборки вместе с JavaScript-кодом. Это позволяет:

  • минимизировать количество проходов;
  • избежать отдельного CSS-пайплайна;
  • синхронизировать JS и CSS-выход.

Результатом становится либо инлайн CSS в bundle, либо отдельный CSS-файл, в зависимости от настроек сборки.

Типизация CSS Modules

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

declare const styles: {
  readonly button: string;
  readonly primary: string;
};
export default styles;

esbuild сам по себе не обязан генерировать .d.ts файлы для CSS Modules, поэтому типизация обычно достигается через дополнительные плагины или постобработку.

Ограничения базовой поддержки

Базовая реализация CSS Modules в esbuild имеет ряд особенностей:

  • отсутствует сложная система runtime-API для динамических стилей;
  • ограниченная поддержка расширенных возможностей Sass-like логики внутри модулей;
  • поведение может различаться в зависимости от версии и плагинов;
  • интеграция с PostCSS происходит отдельно через плагины.

Несмотря на это, базовая модель остаётся стабильной: преобразование классов в локальные идентификаторы и экспорт через JavaScript-модуль.