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 дает одну точку подключения для целой папки стилей.



