← назад к разделу

Когда начинаешь писать React-приложение, стили кажутся мелочью. Напишем className, добавим немного CSS — и готово. Но когда приложение растёт, начинается боль: одна кнопка синяя, другая тёмно-синяя, на третьем экране она вовсе другого размера. Каждый разработчик стилизовал «как привык», и теперь интерфейс выглядит разрозненно.

Поэтому в frontend важны два решения: как изолировать стили (чтобы стили одного компонента не ломали другой) и где хранить единые значения (чтобы все кнопки были одного цвета).

Проблема с обычным CSS

В браузере CSS глобальный. Если написать .card { padding: 16px } в одном файле, это правило применится ко всем элементам с классом card во всём приложении. Когда компонентов десятки, классы начинают конфликтовать: кто-то случайно перетёр стили чужого компонента.

Чтобы избежать этого, придумали несколько подходов.

CSS Modules

CSS Modules — это обычный CSS-файл, но при сборке все имена классов автоматически делаются уникальными. Разные компоненты могут оба называть класс .card, и они не пересекутся.

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

export function ProductCard() {
  return <article className={styles.card}>...</article>;
}
/* ProductCard.module.css */
.card {
  padding: 1rem;
  border-radius: 8px;
  border: 1px solid #e2e8f0;
}

В браузере класс превратится во что-то вроде ProductCard_card__3xKm7 — уникальное, никуда не утечёт.

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

Tailwind

Tailwind — другой подход: вместо написания CSS вы используете готовые маленькие классы прямо в разметке. p-4 означает padding: 1rem, rounded-lg — скруглённые углы, border — рамка.

export function ProductCard() {
  return (
    <article className="rounded-lg border p-4">
      ...
    </article>
  );
}

Никакого CSS-файла нет — всё прямо в JSX. Tailwind генерирует только те классы, которые вы реально использовали, поэтому итоговый файл стилей маленький.

Плюсы: быстро, не нужно придумывать имена классов, согласованная шкала значений (все отступы кратны одной единице). Минус: разметка становится длиннее, нужна привычка читать className в несколько строк.

CSS-in-JS

CSS-in-JS — это когда стили пишутся прямо в JavaScript-коде и могут зависеть от пропсов компонента. Популярные библиотеки: styled-components, Emotion.

const Card = styled.article<{ highlighted: boolean }>`
  padding: 1rem;
  border: 1px solid ${(p) => (p.highlighted ? "#2f5b4f" : "#e2e8f0")};
`;

Плюс: стили могут меняться динамически в зависимости от состояния. Минус: часть библиотек вычисляет стили прямо в браузере во время работы приложения, что замедляет рендер. С серверным рендером (Next.js) нужна дополнительная настройка. Поэтому в новых проектах CSS-in-JS выбирают осторожнее, чем раньше.

Что выбрать

Нет одного правильного ответа — зависит от проекта и команды:

  • CSS Modules — безопасный выбор для тех, кто привык к классическому CSS. Хорошо работает в любом проекте.
  • Tailwind — хорош, если нужно быстро строить интерфейс и команда готова к его синтаксису. Очень популярен в 2020-х.
  • CSS-in-JS — когда стили сильно зависят от состояния компонента и это важнее цены рантайма.

Дизайн-токены

Какой бы механизм стилей вы ни выбрали, остаётся вопрос: откуда берётся значение 16px? Или #2f5b4f? Если каждый компонент хранит эти значения у себя, рано или поздно одна кнопка окажется 14px, другая 16px, третья 1rem — и всё это «почти одно и то же», но не одно.

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

:root {
  --color-accent: #2f5b4f;
  --color-border: #e2e8f0;
  --space-4: 1rem;
  --radius-md: 8px;
}
.card {
  padding: var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
}

Теперь если дизайнер решил изменить основной цвет — меняем в одном месте, и изменение расходится по всему приложению. Если убрать токены и хардкодить #2f5b4f в каждом компоненте, при смене цвета придётся искать по всему коду.

В Tailwind токены живут в конфигурационном файле (tailwind.config.js). В CSS — в :root как переменные. В CSS-in-JS — в объекте темы. Механизм разный, принцип один.

Общие компоненты и согласованность

Дизайн-токены решают проблему значений. Но если каждый разработчик пишет свою кнопку, кнопки всё равно будут разными. Поэтому токены дополняют общими UI-примитивами: Button, Input, Card, Badge.

Когда такой набор есть, новый экран собирают из уже готовых кирпичей с теми же токенами, а не стилизуют с нуля. Это и называют «дизайн-системой» в её минимальном виде — не обязательно большой библиотекой, иногда достаточно папки shared/ui с десятком компонентов.

Коротко

  • Обычный CSS глобальный — CSS Modules и другие подходы изолируют стили по компонентам.
  • CSS Modules: обычный CSS с локальными именами, без рантайма — хороший выбор по умолчанию.
  • Tailwind: утилитарные классы прямо в разметке, быстро и согласованно, нужна привычка.
  • CSS-in-JS: стили зависят от пропсов, но есть цена рантайма — выбирают осознанно.
  • Дизайн-токены — именованные переменные для цветов, отступов и прочего; меняешь в одном месте, расходится везде.
  • Общие UI-компоненты (Button, Card, Input) плюс токены дают согласованный интерфейс без повторной стилизации каждого экрана.

Что почитать дальше

  • Структура проекта — где держать общие компоненты и как организовать shared/ui.
  • Компоненты и пропсы — основа построения интерфейса из независимых блоков.
  • Доступность — как сделать интерфейс доступным для всех пользователей.