ФАЗА 39. РОУТИНГ И ДОСТАВКА Стилизация

Подход к CSS и токены сохраняет согласованность

CSS Modules · Tailwind · Tokens

CSS Modules изолирует классы, Tailwind собирает утилиты, CSS-in-JS связывает стили с кодом, токены задают общие решения дизайна.

1. Глобальный CSS создаёт конфликты имён

1 / 5

Одинаковые классы и растущая специфичность делают результат зависимым от порядка и мешают удалять неиспользуемые правила.

  • Владелец стиля неясен.
  • Каскад имеет побочные эффекты.
  • Локальность уменьшает риск.
Глобальные именаЛокальные стили

2. CSS Modules локализует класс

2 / 5

Сборка превращает .title в уникальное имя, компонент импортирует mapping и использует обычный CSS без runtime-библиотеки.

  • Файл принадлежит компоненту.
  • composes переиспользует правила.
  • Глобальные стили ограничены.
Component CSSGenerated classКомпонент

3. Tailwind собирает интерфейс из утилит

3 / 5

Классы выражают размеры и раскладку прямо в разметке, конфигурация и компонентные обёртки ограничивают длинные варианты.

  • Дизайн-шкала общая.
  • Неиспользуемый CSS удаляется.
  • Повтор выносится по смыслу.
РазметкаUtility classesTailwind themeProduction CSS

4. CSS-in-JS связывает стили и состояние

4 / 5

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

  • Динамика типизируется.
  • Тема доступна.
  • Производительность проверяется.
Статическая сборкаRuntime стили

5. Токены фиксируют общие решения

5 / 5

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

  • Тема меняет токены.
  • Семантика отделена от hex.
  • Общие компоненты проверяют варианты.
Design tokensТемаСогласованные компоненты

Стилизация и дизайн-токены

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

Когда начинаешь писать 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 выбирают осторожнее, чем раньше.

А где Sass и Less

Препроцессоры появились, когда в CSS не было переменных, вложенности и функций: Sass и Less добавили их на этапе сборки, компилируясь в обычный CSS. Сегодня переменные, calc, вложенность и @layer есть в самом CSS, и новые проекты обходятся без препроцессора, а PostCSS с плагинами закрывает оставшееся, в том числе автопрефиксы. Sass остаётся в проектах с большой базой стилей и миксинами, и знать, что он делает, стоит: вложенные селекторы в нём легко порождают длинные и слишком специфичные правила.

Что выбрать

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

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

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