CSS Modules в Vite основаны на концепции локальной области видимости классов, где каждый CSS-файл рассматривается как изолированный модуль. Это устраняет проблему глобального загрязнения пространства имён и конфликтов стилей, особенно в крупных приложениях с большим количеством компонентов.
Файлы, предназначенные для обработки как CSS Modules, в Vite обычно
имеют суффикс .module.css, .module.scss или
.module.less. Именно наличие этого суффикса определяет
режим обработки: классы внутри таких файлов автоматически преобразуются
в уникальные идентификаторы на этапе сборки.
Основная идея CSS Modules заключается в том, что каждый класс становится локальным для файла, в котором он объявлен. Вместо прямого использования имени класса в DOM, Vite преобразует его в уникальную строку, включающую хэш.
Пример исходного CSS:
.button {
padding: 12px 16px;
background: #4f46e5;
color: white;
}
После обработки Vite класс превращается во что-то подобное:
.button => button__3xK9a
Имя button__3xK9a генерируется автоматически и
гарантирует уникальность в рамках всего приложения. Это означает, что
даже если другой компонент также использует класс .button,
конфликта не произойдёт.
В Vite поддержка CSS Modules включена по умолчанию. Достаточно использовать соответствующее расширение файла и импортировать стили как модуль:
import styles from './Button.module.css';
Далее объект styles содержит отображение оригинальных
классов на сгенерированные:
console.log(styles.button);
Результат:
button__3xK9a
Использование в компоненте:
export function Button() {
return <button className={styles.button}>Click</button>;
}
Несмотря на автоматическую генерацию уникальных идентификаторов, исходные имена классов остаются важными. Они влияют на читаемость кода, отладку и структуру проекта.
Классы должны описывать роль элемента, а не его внешний вид:
.submitButton {
background: green;
}
или
.formError {
color: red;
}
Такой подход упрощает сопровождение, особенно при изменении дизайна.
В CSS Modules используются разные стили именования, и выбор зависит от команды и проекта.
.primaryButton {
padding: 10px;
}
Используется часто, поскольку напрямую соответствует JavaScript-ключам:
styles.primaryButton
.primary-button {
padding: 10px;
}
При использовании kebab-case доступ осуществляется через скобочную нотацию:
styles['primary-button']
camelCase обычно предпочтительнее в React-проектах, так как снижает вероятность ошибок при обращении к свойствам объекта.
CSS Modules позволяют комбинировать локальные и глобальные стили.
.title {
font-size: 20px;
}
Класс будет преобразован в уникальный идентификатор и применён только в пределах модуля.
Иногда требуется объявить стили, которые не должны модифицироваться системой модулей:
:global(.reset) {
margin: 0;
padding: 0;
}
Или целый блок:
:global {
body {
font-family: sans-serif;
}
}
Глобальные стили полезны для базовой типографики, reset-стилей или интеграции сторонних библиотек, которые ожидают фиксированные классы.
CSS Modules поддерживают механизм composes, позволяющий
переиспользовать стили без дублирования.
.baseButton {
padding: 10px 14px;
border-radius: 6px;
}
.primaryButton {
composes: baseButton;
background: blue;
color: white;
}
В результате primaryButton включает стили
baseButton, сохраняя при этом уникальность.
В JavaScript можно комбинировать классы из CSS Modules с помощью строковой конкатенации или библиотек.
Пример простого объединения:
className={`${styles.button} ${styles.active}`}
Более масштабируемый вариант — использование
classnames:
import cn from 'classnames';
import styles from './Button.module.css';
<button className={cn(styles.button, {
[styles.active]: isActive
})} />
Это особенно важно в сложных интерфейсах с множественными состояниями компонентов.
Vite может использовать плагины, которые генерируют типы для CSS Modules. Это позволяет получать автодополнение:
import styles from './Button.module.css';
TypeScript знает структуру:
styles.button // string
styles.active // string
При ошибке в имени класса появляется ошибка компиляции, что снижает количество runtime-проблем.
При масштабировании приложения важно придерживаться согласованной структуры:
Каждый UI-компонент имеет собственный CSS Module:
Button/
Button.tsx
Button.module.css
Это гарантирует независимость стилей и упрощает рефакторинг.
Часто используется модель состояний через отдельные классы:
.button { }
.disabled { }
.loading { }
.active { }
Такой подход предпочтительнее вложенных селекторов, так как он лучше сочетается с динамическим управлением классов в JavaScript.
Хотя CSS позволяет вложенность через препроцессоры, в CSS Modules рекомендуется минимизировать её:
.cardTitle { }
вместо:
.card .title { }
Причина заключается в снижении связности и улучшении переиспользуемости.
Vite обрабатывает CSS Modules через PostCSS-пайплайн и быстрый dev-сервер с HMR (Hot Module Replacement). При изменении CSS файла обновляются только стили, без полной перезагрузки страницы.
Механизм работает следующим образом:
.module.css<style> или отдельный CSS
chunkЭто делает работу с CSS Modules особенно быстрой в разработке.
Сгенерированные имена классов включают хэш, основанный на содержимом файла. Это означает:
Такой подход обеспечивает:
CSS Modules идеально сочетаются с компонентной архитектурой. Каждый компонент владеет своими стилями, что формирует строгую границу ответственности.
Пример структуры:
/components
/Header
Header.tsx
Header.module.css
/Footer
Footer.tsx
Footer.module.css
Такой подход исключает зависимость компонентов друг от друга через CSS.
Иногда требуется расширять базовые стили без изменения исходного файла. Для этого используется комбинация классов или композиция.
.baseInput {
padding: 8px;
border: 1px solid #ccc;
}
.largeInput {
composes: baseInput;
padding: 14px;
}
Альтернативный подход — передача дополнительных классов извне:
<input className={`${styles.input} ${styles.large}`} />
В режиме разработки Vite сохраняет читаемость классов. Часто итоговое имя включает оригинальное название:
.button__Button_module__3xK9a
Это облегчает поиск элемента в DevTools и понимание связи между DOM и исходным CSS.
CSS Modules не решают все задачи стилизации:
Также важно учитывать, что глобальные стили и сторонние библиотеки могут обходить изоляцию, поэтому требуется аккуратная интеграция.
В зрелых проектах часто используется единая стратегия:
Пример удачного подхода:
.errorMessage { }
.successMessage { }
Пример менее удачного:
.redTextLargeBold { }
Переименование класса в CSS Module автоматически обновляет только
локальные ссылки, так как связь идёт через объект styles.
Это снижает риск ошибок при масштабных изменениях интерфейса и делает
рефакторинг безопаснее по сравнению с глобальными стилями.