Когда начинаешь писать 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. - Компоненты и пропсы — основа построения интерфейса из независимых блоков.
- Доступность — как сделать интерфейс доступным для всех пользователей.