Sass: что это и для чего нужен препроцессор CSS

Sass: что это и для чего нужен препроцессор CSS Полезное

Sass — это препроцессор CSS: язык стилей с переменными, вложенностью, миксинами, модулями и циклами, который компилятор превращает в обычный CSS. Браузер Sass не понимает, он получает только готовый CSS.

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

Примеры проверены на Dart Sass 1.105.0 (пакет sass из npm, Node.js) 24.09.2026.

Sass, SCSS и Dart Sass: три слова, которые путают

Термин Что это Пример
Sass язык-препроцессор целиком «проект на Sass»
SCSS основной синтаксис Sass, файлы .scss, фигурные скобки и ; как в CSS .a { color: $brand; }
Отступный синтаксис старый синтаксис, файлы .sass, без скобок и ;, вложенность отступами .a и ниже color: $brand
Dart Sass компилятор, основная реализация языка команда sass

Любой корректный CSS — это корректный SCSS, поэтому переход на SCSS можно начать с переименования style.css в style.scss. С отступным синтаксисом так не выйдет: там другая грамматика. Возможности у обоих синтаксисов одинаковые, выбор — дело привычки команды, но в проектах и документации чаще встречается SCSS.

Для чего нужен Sass

Задача Sass — убрать повторения и дать CSS структуру, как у кода на языке программирования. Удобно смотреть по конкретным задачам:

Задача Как решает Sass Есть ли в чистом CSS
Один цвет в 40 местах переменная $brand да, custom properties --brand (работают иначе, см. ниже)
Стили .card, .card__title, .card:hover вложенность и & вложенность есть в современных браузерах, склейки &__title нет
Одинаковый набор правил у кнопок @mixin / @include с параметрами нет
Стили в разных файлах, общие токены модули @use и partials _file.scss @import есть, но это отдельные запросы или работа сборщика
Сгенерировать .mt-sm, .mt-md, .mt-lg @each, @for, карты нет
Затемнить цвет на 10% модуль sass:color да, относительный синтаксис цвета hsl(from var(--brand) h s calc(l - 10)) (Chrome 125+, Safari 18+, Firefox 128+), а также color-mix()

Главное отличие переменных Sass от CSS-переменных: $brand существует только при компиляции и в итоговом CSS заменяется значением, а --brand живет в браузере, наследуется по DOM и может меняться в рантайме (тема, медиазапрос, JavaScript). Поэтому это не конкуренты: часто Sass-переменная хранит токен, а в CSS он выводится как --brand: #{$brand};.

Минимальный рабочий пример

Проект из трех файлов. Файлы с подчеркиванием в начале имени — partials: они подключаются в другие, но в отдельный CSS не компилируются.

// src/_tokens.scss
$brand: #2563eb;
$radius: 8px;
$gap: 16px;
// src/_mixins.scss
@use "sass:color";
@use "tokens";

@mixin button($bg: tokens.$brand) {
  padding: tokens.$gap * 0.5 tokens.$gap;
  border-radius: tokens.$radius;
  background: $bg;
  color: #fff;

  &:hover {
    background: color.adjust($bg, $lightness: -10%);
  }
}
// src/main.scss
@use "tokens";
@use "mixins";

.card {
  gap: tokens.$gap;

  &__title {
    font-size: 20px;
  }

  .btn {
    @include mixins.button;
  }

  .btn--danger {
    @include mixins.button(#dc2626);
  }
}

Компиляция: npx sass --no-source-map src/main.scss dist/main.css. Результат в dist/main.css:

.card {
  gap: 16px;
}
.card__title {
  font-size: 20px;
}
.card .btn {
  padding: 8px 16px;
  border-radius: 8px;
  background: #2563eb;
  color: #fff;
}
.card .btn:hover {
  background: rgb(7.2829131653%, 29.8599439776%, 79.3837535014%);
}
.card .btn--danger {
  padding: 8px 16px;
  border-radius: 8px;
  background: #dc2626;
  color: #fff;
}
.card .btn--danger:hover {
  background: rgb(69.9019607843%, 11.2745098039%, 11.2745098039%);
}

Цвет при наведении записан через rgb() с процентами — это валидный CSS, так Dart Sass 1.105 выводит цвет с дробными каналами. Формат вывода цвета зависит от версии компилятора, само значение — нет.

Разбор примера по возможностям

Переменные. $gap: 16px задается один раз, дальше участвует в выражениях: tokens.$gap * 0.5 стало 8px. Sass знает единицы измерения: 1px + 2s остановит компиляцию ошибкой 1px and 2s have incompatible units.

Вложенность и &. Селектор внутри .card дописывается к родителю через пробел (.card .btn), а & подставляет родителя без пробела: &__title дает .card__title, &:hover — :hover на том же элементе. Это удобно для БЭМ-именования.

Миксины. @mixin button($bg: tokens.$brand) — набор правил с параметром и значением по умолчанию. @include mixins.button вставляет его с синим фоном, @include mixins.button(#dc2626) — с красным. В отличие от функции, миксин возвращает не значение, а блок деклараций.

Модули @use. Каждый @use подключает файл один раз и дает пространство имен по имени файла: tokens.$gap, mixins.button. Встроенные модули подключаются так же: sass:color, sass:math, sass:map. Переменные с - или _ в начале имени ($-secret) приватны и снаружи модуля недоступны.

Настройка модуля и циклы

Значения модуля можно переопределить при подключении, если в нем они объявлены с !default:

// _tokens.scss: $brand: #2563eb !default; $gap: 16px !default;
@use "tokens" with ($brand: #16a34a);

.a { color: tokens.$brand; margin: tokens.$gap; }

Результат: color: #16a34a; margin: 16px; — цвет подменен, отступ остался по умолчанию. Так настраивают UI-библиотеки, не редактируя их исходники.

Флаг !default обязателен. Если подключить так _tokens.scss из первого примера, где переменные объявлены без него, компиляция остановится ошибкой This variable was not declared with !default in the @used module.

Циклы по картам генерируют однотипные классы:

$sizes: (sm: 4px, md: 8px, lg: 16px);

@each $name, $value in $sizes {
  .mt-#{$name} { margin-top: $value; }
}

На выходе три правила: .mt-sm { margin-top: 4px; }, .mt-md { margin-top: 8px; }, .mt-lg { margin-top: 16px; }. Конструкция #{...} — интерполяция, она вставляет значение в селектор или имя свойства.

Тот же подход в отступном синтаксисе .sass выглядит так и компилируется в .nav { display: flex; }, .nav a { color: #2563eb; } и .nav a:hover { ... }:

$brand: #2563eb

.nav
  display: flex
  a
    color: $brand
    &:hover
      text-decoration: underline

Как скомпилировать Sass в CSS

Актуальный компилятор — Dart Sass. Старые реализации устарели: Ruby Sass закрыт, LibSass и npm-пакет node-sass объявлены устаревшими и не поддерживают модули @use. Новые проекты на них не начинают.

Установка, на выбор:

npm install --save-dev sass   # в проект, запуск через npx sass
npm install -g sass           # глобально
brew install sass/sass/sass   # macOS, Homebrew
choco install sass            # Windows, Chocolatey

Основные команды:

npx sass src/main.scss dist/main.css            # один файл
npx sass src:dist                               # каталог: каждый не-partial файл -> .css
npx sass --watch src:dist                       # пересборка при сохранении
npx sass --style=compressed src/main.scss dist/main.min.css

В режиме каталога src:dist из примера выше появится только main.css (плюс main.css.map): partials _tokens.scss и _mixins.scss отдельно не собираются. --style=compressed убирает пробелы и переносы, например .mt-sm{margin-top:4px}.mt-md{margin-top:8px}.

Файл .map — source map: DevTools браузера по нему показывают строку в .scss, а не в итоговом CSS. Поэтому довод «с Sass теряется инспектор элементов» устарел: на время разработки source maps нужно просто не отключать. В сборщиках (Vite, webpack) Sass обычно подключается пакетом sass или sass-embedded, и файлы .scss импортируются прямо из кода.

Типичные ошибки

Переменная без пространства имен. С @use переменные не становятся глобальными:

@use "tokens";
.a { color: $brand; }
Error: Undefined variable.
  ╷
2 │ .a { color: $brand; }
  │             ^^^^^^
  ╵
  undef.scss 2:13  root stylesheet

Исправление: color: tokens.$brand; или @use "tokens" as *;, если пространство имен не нужно (но тогда легче получить конфликт имен).

@import вместо @use. Старый код компилируется, но с предупреждением:

DEPRECATION WARNING [import]: Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.

@import сваливает все переменные в одно глобальное пространство и подключает файл столько раз, сколько встретился. Замена — @use (и @forward для реэкспорта). Для перевода старого кода есть официальный инструмент sass-migrator.

Деление через /. Выражение $gap / 2 сейчас дает 8px, но с предупреждением [slash-div]: в CSS косая черта — разделитель (font: 16px/1.5 sans-serif), и такое деление будет удалено в Dart Sass 2.0.0. Правильно — math.div($gap, 2) после @use "sass:math"; или calc($gap / 2).

Глобальные цветовые функции. darken($brand, 10%) работает с предупреждениями [global-builtin] и [color-functions]. Замена — color.adjust($brand, $lightness: -10%) или color.scale(...) из модуля sass:color.

Слишком глубокая вложенность. Код .page { .content { .list { .item { a { ... } } } } } компилируется в .page .content .list .item a — длинный селектор с высокой специфичностью, который трудно перебить. Рабочее правило — не глубже двух-трех уровней и вложенность по смыслу (состояния, модификаторы), а не по дереву HTML.

Когда Sass можно не брать

Sass не обязателен. В 2026 году браузеры поддерживают CSS-переменные, нативную вложенность и color-mix(), поэтому для лендинга или небольшого сайта чистого CSS часто хватает. Если проект собран на Tailwind или CSS Modules, Sass может быть лишним слоем.

Sass окупается, когда стилей много и нужна система: дизайн-токены, генерация утилитарных классов, миксины для медиазапросов, настройка библиотеки через with. Цена — шаг сборки, зависимость от компилятора и необходимость, чтобы вся команда писала стили в одном формате.

Выводы

  • Sass — препроцессор: пишется SCSS или .sass, компилятор Dart Sass превращает его в CSS, браузер видит только результат.
  • Нужен, чтобы не повторяться: переменные, вложенность с &, миксины, модули @use, циклы @each и функции sass:color/sass:math.
  • Переменные $x работают при компиляции, CSS-переменные --x — в браузере; их часто комбинируют.
  • Для нового кода — @use вместо @import, math.div вместо /, color.adjust вместо darken; устаревшее компилируется с предупреждениями и будет удалено.
  • Для небольших проектов современного CSS может хватить; Sass окупается на больших кодовых базах и дизайн-системах.

Где применяется / связь с практикой

Освойте тему на практике

Sass встречается в большинстве проектов с собственной дизайн-системой, в UI-библиотеках и в задачах поддержки существующей верстки, поэтому верстальщику и фронтенд-разработчику нужно уметь хотя бы читать SCSS и переводить старый код на модули. Системно освоить верстку от семантики HTML до адаптивных макетов и работы с препроцессорами можно на курсе HTML/CSS. Разобрать отдельные темы с практикующими разработчиками помогут бесплатные открытые уроки Otus.

FAQ

Чем Sass отличается от Less?
Оба — препроцессоры CSS с переменными, вложенностью и миксинами. У Sass модульная система @use, богатая стандартная библиотека и больше распространенность; Less, как и SCSS, — надмножество CSS, переменные в нем пишутся через @, а вычисления можно делать и в браузере через less.js (для продакшена все равно компилируют заранее).

Можно ли подключить .scss файл прямо в HTML?
Нет, <link> должен указывать на скомпилированный .css. В проектах со сборщиком (Vite, webpack) .scss импортируют из JavaScript, но компиляция все равно происходит на этапе сборки или dev-сервера.

Что такое @forward?
Правило, которое реэкспортирует члены модуля: файл _index.scss с несколькими @forward дает одну точку подключения для целой папки стилей.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)