CSS Modules

CSS Modules — подход, при котором CSS-классы автоматически получают уникальные имена при сборке, предотвращая конфликты стилей между компонентами. Поддерживается Vite, Webpack, Next.js и другими bundler-ами.

Зачем нужно

В обычном CSS все классы глобальные: .title в одном файле конфликтует с .title в другом. CSS Modules автоматически делает классы локальными (scoped), генерируя уникальные имена. Это решает проблему конфликтов без изменения синтаксиса CSS.

Где используется

  • React / Next.js проекты
  • Vue.js (через <style module>)
  • Любой проект с Vite или Webpack
  • Компонентные архитектуры

Предпосылки

  • BEM — ручной подход к изоляции (CSS Modules автоматизирует)
  • Понимание bundler-ов (Vite, Webpack)

Базовое использование

Файл стилей (Button.module.css)

.button {
  padding: 10px 24px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 1rem;
}

.primary {
  background: #007bff;
  color: white;
}

.secondary {
  background: #6c757d;
  color: white;
}

.large {
  padding: 14px 32px;
  font-size: 1.125rem;
}

Импорт в компоненте (React)

import styles from './Button.module.css';

function Button({ variant = 'primary', size, children }) {
  const className = [
    styles.button,
    styles[variant],
    size === 'large' ? styles.large : ''
  ].filter(Boolean).join(' ');

  return <button className={className}>{children}</button>;
}

Результат в HTML

<!-- CSS Modules генерирует уникальные имена -->
<button class="_button_x3k2d_1 _primary_x3k2d_7">
  Отправить
</button>

Compose (композиция)

/* base.module.css */
.reset() {
  margin: 0;
  padding: 0;
  border: none;
}

/* Button.module.css */
.button {
  composes: reset from './base.module.css';
  padding: 10px 24px;
  border-radius: 6px;
  cursor: pointer;
}

/* Или из того же файла */
.primary {
  composes: button;
  background: #007bff;
  color: white;
}

:global — глобальные стили

/* Локальный класс (по умолчанию) */
.card {
  padding: 16px;
}

/* Глобальный класс — не будет переименован */
:global(.active) {
  border-color: #007bff;
}

/* Комбинация локального и глобального */
.card :global(.highlight) {
  background: #fff3cd;
}

/* Блок глобальных стилей */
:global {
  .page-title {
    font-size: 2rem;
  }
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
  }
}

Паттерны использования

Условные классы

import styles from './Alert.module.css';

function Alert({ type = 'info', children }) {
  return (
    <div className={`${styles.alert} ${styles[type]}`}>
      {children}
    </div>
  );
}

С библиотекой clsx

import styles from './Card.module.css';
import clsx from 'clsx';

function Card({ featured, compact, className }) {
  return (
    <div className={clsx(
      styles.card,
      featured && styles.featured,
      compact && styles.compact,
      className
    )}>
      ...
    </div>
  );
}

CSS Modules в Vue

<template>
  <button :class="[$style.button, $style[variant]]">
    <slot />
  </button>
</template>

<style module>
.button {
  padding: 10px 24px;
  border-radius: 6px;
}

.primary {
  background: #007bff;
  color: white;
}
</style>

CSS Modules + CSS-переменные

/* Theme.module.css */
.light {
  --bg: #ffffff;
  --text: #333333;
  --primary: #007bff;
}

.dark {
  --bg: #1a1a2e;
  --text: #e9ecef;
  --primary: #4dabf7;
}

/* Card.module.css — использует переменные */
.card {
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--primary);
}

Частые ошибки

  1. Обращение к классу как к строке:
    /* ОШИБКА */
    <div className="card">
    /* ПРАВИЛЬНО */
    <div className={styles.card}>
    
  2. Дефисы в именах классов:
    /* ОШИБКА — дефис в точечной нотации */
    styles.card-title
    /* ПРАВИЛЬНО */
    styles['card-title']
    /* Или используйте camelCase в CSS */
    .cardTitle { }
    
  3. Забыли .module.css в имени файла — обычный .css файл не будет обработан как CSS Module
  4. Стилизация дочерних компонентов — CSS Modules не стилизуют вложенные компоненты. Передавайте className как prop

Практика

  • Создать компонент с CSS Modules (.module.css)
  • Использовать composes для наследования стилей
  • Применить :global для глобальных классов
  • Настроить условные классы через clsx
  • Комбинировать CSS Modules с CSS-переменными

Связанные темы

  • BEM — ручная изоляция стилей
  • Sass — препроцессор (можно использовать .module.scss)
  • CSS переменные — кастомные свойства

Ресурсы