Препроцессоры и современный тулинг

Сравниваем инструменты, которые надстраиваются над CSS: препроцессор Sass, постпроцессор PostCSS, utility-first c Tailwind и CSS Modules — и разбираемся, что и когда уместно.

Препроцессор — это инструмент, который превращает расширенный синтаксис (Sass/SCSS) в обычный CSS на этапе сборки. Браузер видит уже готовый CSS — про Sass он ничего не знает.

Голый CSS многое умеет, но на больших проектах хочется переменных-вычислений, переиспользуемых кусков и автоматической рутины (префиксы, минификация). Этим занимается слой тулинга. Важно различать препроцессоры (работают до CSS, добавляют синтаксис) и постпроцессоры (работают над готовым CSS, преобразуют его).

Зачем это на практике

Тулинг убирает ручную рутину и дублирование. Без него вы вручную дописываете вендорные префиксы, копируете один и тот же блок свойств в десять мест, держите цвета строками. Sass даёт миксины и вложенность, PostCSS автоматически проставляет префиксы под нужные браузеры, Tailwind избавляет от придумывания имён классов, CSS Modules гарантируют, что классы компонента ни с чем не столкнутся. Каждый инструмент закрывает свою боль — и выбирать их стоит осознанно.

Sass/SCSS: переменные, вложенность, миксины

Sass — самый известный препроцессор. Современный синтаксис — SCSS: это надмножество CSS (любой валидный CSS — валидный SCSS), поэтому порог входа низкий. Ключевые возможности:

Переменные и вложенность

$primary: #3b82f6;
$radius: 8px;

.card {
  border-radius: $radius;
  background: #fff;

  // вложенность: & — ссылка на родителя (.card)
  &__title { font-size: 1.25rem; }
  &--featured { border: 2px solid $primary; }

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

Символ & внутри вложенного блока означает «родительский селектор». Запись &__title внутри .card скомпилируется в .card__title — удобно для BEM. Это сахар записи: на выходе получаются обычные плоские селекторы.

Миксины — переиспользуемые блоки

Миксин — именованный набор объявлений, который можно подставлять с параметрами:

@mixin flex-center($gap: 0) {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: $gap;
}

.toolbar { @include flex-center(12px); }
.modal__footer { @include flex-center; }

Компилируется в:

.toolbar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

Важно помнить: переменные Sass — это сборочное время. Они подставляются в значения при компиляции и в браузере не существуют. Для рантайм-тем (предыдущий урок) нужны CSS-переменные, а не Sass-переменные — это разные инструменты для разных задач, и их часто используют вместе.

PostCSS и автопрефиксы

PostCSS — это не язык, а платформа плагинов, обрабатывающая уже готовый CSS. Сам по себе он почти ничего не делает; всё решают подключённые плагины. Самый известный — Autoprefixer: он смотрит на ваш список поддерживаемых браузеров (browserslist) и сам дописывает нужные вендорные префиксы.

/* вы пишете чистый стандарт */
.box { user-select: none; }

/* Autoprefixer на выходе добавит то, что требуется браузерам из browserslist */
.box { -webkit-user-select: none; user-select: none; }

Другие популярные плагины: postcss-preset-env (даёт писать ещё не везде поддержанный синтаксис будущего CSS), минификатор cssnano, импорт файлов. Многие сборщики (и Tailwind) под капотом устроены как раз на PostCSS.

Utility-first и Tailwind как подход

Tailwind переворачивает идею «семантических классов» (как BEM) с ног на голову. Вместо того чтобы писать .card и описывать его в CSS, вы собираете внешний вид прямо в разметке из готовых утилитарных классов, каждый из которых отвечает за одно свойство:

<article class="rounded-lg bg-white p-4 shadow flex items-center gap-3">
  <h3 class="text-lg font-semibold">Тариф Pro</h3>
</article>

Здесь p-4 — это padding, rounded-lg — скругление, flex — display. CSS вы почти не пишете. Плюсы: не нужно придумывать имена, стили не «протекают» между компонентами, значения берутся из единой шкалы (фактически встроенные дизайн-токены), а неиспользуемые классы вырезаются из финального бандла. Минусы: разметка «шумная», длинные строки классов, и часть людей считает это смешением представления с содержимым. Tailwind хорош там, где много уникальной верстки и важна скорость; BEM — там, где нужны строго переиспользуемые именованные компоненты. Это конкурирующие философии, а не «лучше/хуже».

CSS Modules — локальная область видимости

CSS Modules решают узкую, но важную задачу: изоляцию имён классов. Вы пишете обычный CSS в файле Card.module.css, а сборщик при импорте превращает каждое имя класса в гарантированно уникальное (например, titleCard_title_a3f9). Коллизии между компонентами становятся невозможны технически — то, что BEM делает дисциплиной имён, CSS Modules делают автоматикой.

/* Card.module.css — пишем простые имена, изоляцию даст сборщик */
.title { font-size: 1.25rem; }
.featured { border: 2px solid #3b82f6; }

В компоненте вы импортируете объект со стилями и подставляете сгенерированные имена. CSS Modules особенно естественны в компонентных фреймворках (React и подобные), где каждый компонент держит свой CSS рядом.

Как это работает под капотом

Объединяет все четыре инструмента одно: они работают на этапе сборки, а в браузер уезжает обычный CSS. Sass компилирует SCSS в CSS, разворачивая вложенность и миксины в плоские правила и подставляя значения переменных. PostCSS трансформирует уже готовый CSS, прогоняя его через цепочку плагинов (как middleware). Tailwind сканирует разметку, оставляет в бандле только реально использованные утилиты и выкидывает остальные тысячи. CSS Modules переименовывают классы при импорте и отдают компоненту карту «исходное имя → уникальное». Браузер ни про Sass, ни про модули не знает — он получает финальный плоский CSS. Поэтому все эти инструменты можно комбинировать: например, SCSS + PostCSS-автопрефиксы + CSS Modules в одном проекте.

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

  • Глубокая вложенность в Sass. Соблазн писать .a { .b { .c { ... } } } приводит к раздутой специфичности на выходе — ровно той проблеме, от которой спасает BEM. Вкладывайте на 1–2 уровня, чаще через & для модификаторов.
  • Ждут, что Sass-переменные дадут темы в рантайме. Они вычисляются при сборке. Переключаемые темы — это CSS-кастомные свойства; Sass и CSS-переменные решают разные задачи.
  • Ставят PostCSS «чтобы было», без плагинов. Чистый PostCSS бесполезен — пользу приносят конкретные плагины (Autoprefixer и т.п.) и настроенный browserslist под ваши браузеры.
  • Выбирают инструмент по моде, а не по задаче. Tailwind не «лучше» BEM, CSS Modules не «лучше» Sass — у каждого своя ниша. Реальные проекты часто комбинируют их.
  • Дублируют систему значений. Если уже есть дизайн-токены на CSS-переменных, не заводите параллельно те же значения Sass-переменными — будет два расходящихся источника правды.

Итоги

  • Препроцессор (Sass/SCSS) добавляет синтаксис до CSS: переменные, вложенность (& — родитель), миксины (@mixin/@include); компилируется в плоский CSS.
  • PostCSS — платформа плагинов над готовым CSS; ключевой плагин Autoprefixer проставляет вендорные префиксы по browserslist.
  • Tailwind (utility-first) собирает вид из мелких утилитарных классов прямо в разметке — конкурирующая с BEM философия, не «замена».
  • CSS Modules автоматически делают имена классов уникальными — изоляция компонентов через сборку, а не через дисциплину.
  • Все эти инструменты работают на этапе сборки и выдают обычный CSS, поэтому свободно комбинируются; выбор — по задаче, а не по моде.
Проверьте себя
1. Что означает символ & во вложенном правиле SCSS, например .card { &__title { ... } }?
AЛогическое И между двумя селекторами
BСсылку на родительский селектор — здесь .card, поэтому скомпилируется в .card__title
CОбъявление новой переменной
DКомментарий, который вырежется при компиляции
2. За что в первую очередь отвечает PostCSS-плагин Autoprefixer?
AКомпилирует SCSS в CSS
BАвтоматически дописывает вендорные префиксы (-webkit- и т.п.) по списку поддерживаемых браузеров
CДелает имена классов уникальными для изоляции компонентов
DПревращает утилитарные классы Tailwind в готовый CSS
3. Чем подход Tailwind (utility-first) принципиально отличается от BEM?
ATailwind вообще не использует классы
BBEM описывает компонент в CSS под семантическим именем, а Tailwind собирает вид прямо в разметке из мелких утилитарных классов на одно свойство
CTailwind работает только без сборщика, а BEM требует сборку
DЭто одно и то же, просто разные названия