CSS Modules

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


Подключение и настройка

В современных проектах на React или Next.js CSS Modules интегрируются через стандартный импорт:

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

Здесь Button.module.css — файл с локальными стилями для компонента Button. Важно использовать расширение .module.css или .module.scss для корректной работы модулей. После импорта объект styles содержит ключи, соответствующие именам классов в CSS:

/* Button.module.css */
.button {
  background-color: #0070f3;
  color: white;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  border: none;
  cursor: pointer;
}
// Button.jsx
<button className={styles.button}>Click me</button>

При сборке проекта класс .button будет преобразован в уникальное имя, например Button_button__3fX4L, что полностью исключает конфликты с другими .button.


Локальные и глобальные стили

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

/* Button.module.css */
:global(.reset) {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

В этом случае класс reset будет доступен глобально, а все остальные стили останутся локальными.


Динамические классы

CSS Modules прекрасно работают с динамическим назначением классов через условные выражения. Для удобства можно использовать библиотеку clsx или classnames:

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

const Button = ({ primary }) => {
  return (
    <button className={clsx(styles.button, primary && styles.primary)}>
      Click me
    </button>
  );
};
/* Button.module.css */
.button {
  padding: 0.5rem 1rem;
  border-radius: 4px;
  border: none;
}
.primary {
  background-color: #0070f3;
  color: white;
}

clsx объединяет классы и применяет styles.primary только если проп primary равен true.


Поддержка вложенных селекторов и псевдоклассов

CSS Modules позволяет использовать все стандартные возможности CSS: вложенные селекторы, псевдоклассы, медиа-запросы:

/* Card.module.css */
.card {
  padding: 1rem;
  border: 1px solid #ddd;
  border-radius: 8px;
  transition: box-shadow 0.2s ease;
}

.card:hover {
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

@media (max-width: 768px) {
  .card {
    padding: 0.5rem;
  }
}

Локализация классов сохраняется даже при использовании вложенных селекторов, что делает стили полностью компонентными.


Работа с CSS Modules и Radix UI

Radix UI предоставляет низкоуровневые, полностью контролируемые компоненты интерфейса. Их стили можно легко адаптировать с CSS Modules, сочетая преимущества локальных стилей и готовых компонентов Radix. Например, для компонента Radix Dialog:

import * as Dialog from '@radix-ui/react-dialog';
import styles from './Dialog.module.css';

<Dialog.Root>
  <Dialog.Trigger className={styles.trigger}>Open</Dialog.Trigger>
  <Dialog.Content className={styles.content}>
    <Dialog.Title className={styles.title}>Dialog Title</Dialog.Title>
    <Dialog.Description className={styles.description}>Description text</Dialog.Description>
  </Dialog.Content>
</Dialog.Root>
/* Dialog.module.css */
.trigger {
  background-color: #0070f3;
  color: white;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  border: none;
}

.content {
  background: white;
  border-radius: 8px;
  padding: 1rem;
  max-width: 400px;
  box-shadow: 0 10px 25px rgba(0,0,0,0.15);
}

.title {
  font-size: 1.25rem;
  font-weight: bold;
  margin-bottom: 0.5rem;
}

.description {
  font-size: 1rem;
  color: #555;
}

Такое разделение обеспечивает строгую компонентную структуру, где стили Dialog изолированы и не конфликтуют с другими частями интерфейса.


Переход на SCSS Modules

CSS Modules можно комбинировать с SCSS, что добавляет возможности переменных, миксинов и вложенности:

/* Button.module.scss */
$primary-color: #0070f3;

.button {
  padding: 0.5rem 1rem;
  border-radius: 4px;
  border: none;
  background-color: $primary-color;
  color: white;
  
  &:hover {
    background-color: darken($primary-color, 10%);
  }
}

Импорт в компонент остается прежним:

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

Организация больших проектов

При работе с крупными проектами рекомендуется:

  • Структурировать файлы по компонентам: каждый компонент имеет свой .module.css или .module.scss.
  • Использовать конвенции именования: ComponentName_element или componentName__element для удобства.
  • Разделять глобальные стили: использовать отдельный globals.css для сброса стилей, сеток и общих переменных.

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