Sass — мощный препроцессор CSS
Sass (Syntactically Awesome Style Sheets) расширяет CSS переменными, вложенностью, миксинами и функциями. Браузеры его не понимают, поэтому файлы .scss компилируются в обычный .css — но писать и поддерживать стили становится заметно проще.
1. Что такое Sass и два синтаксиса
У Sass два синтаксиса:
- SCSS — супермножество CSS, файлы
.scss(практически весь CSS валиден); - Sass (indented) — синтаксис без фигурных скобок и точек с запятой, файлы
.sass.
Большинство проектов используют SCSS — он дружелюбен к новичкам, потому что похож на обычный CSS.
2. Установка и компиляция
npm install --save-dev sass
# компиляция одного файла
npx sass styles.scss styles.css
# слежение за изменениями (watch)
npx sass --watch styles.scss styles.css
# минифицированный вывод
npx sass --style=compressed styles.scss styles.cssВ реальных проектах Sass подключается в сборку: в Vite достаточно поставить sass и использовать <style lang="scss"> во Vue или импорты в React.
3. Переменные
$primary: #2563eb;
$spacing: 16px;
$font-base: "Segoe UI", Arial, sans-serif;
.button {
background: $primary;
margin: $spacing;
font-family: $font-base;
}Переменные компилируются в значения — в итоговом CSS их не будет. Для работы в рантайме браузера понадобятся CSS Custom Properties (--var), а Sass-переменные — для времени компиляции.
4. Вложенность
.card {
padding: 16px;
.card-title {
font-size: 1.25rem;
}
&:hover { // & — ссылка на родителя
transform: scale(1.02);
}
&.active { // .card.active
border-color: $primary;
}
}// Компилируется в:
.card { padding: 16px; }
.card .card-title { font-size: 1.25rem; }
.card:hover { transform: scale(1.02); }
.card.active { border-color: #2563eb; }>-вложенность раздувает специфичность селекторов.5. Частичные файлы: @use и @import
Файл, начинающийся с подчёркивания (например, _variables.scss), называется частичным и не компилируется в отдельный CSS. Его подключают в основной файл:
// _variables.scss
$accent: #f59e0b;
// _mixin.scss
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
// main.scss
@use "variables";
@use "mixins";
.hero {
background: variables.$accent;
@include mixins.flex-center;
}@import в Sass обесценен и заменён на @use: он подключает файл один раз, позволяет использовать неймспейсы и не греет глобальное пространство. В новых проектах пиши только @use.6. Миксины: @mixin и @include
Миксины — переиспользуемые блоки свойств с параметрами:
@mixin button($bg, $color: #fff) {
background: $bg;
color: $color;
border-radius: 8px;
padding: 10px 16px;
}
.btn-primary { @include button(#2563eb); }
.btn-danger { @include button(#dc2626); }@extend? Миксин копирует свойства в каждое место (логично с параметрами), а @extend группирует селекторы в одно правило и даёт более компактный CSS без параметров.7. Операторы и расчёты
$base: 8px;
.title {
font-size: $base * 3; // 24px
padding: $base * 2 $base; // 16px 8px
}
.container {
width: (100vw - 240px) / 2; // скобки считаются
}Sass умеет складывать, вычитать, умножать, делить и брать остаток (%). Деление пишется в скобках или со / внутри операции, чтобы не конфликтовать со старым CSS (font: 16px/1.5).
8. Функции: @function
// Переводим пиксели в rem
@function rem($px, $base: 16px) {
@return $px / $base * 1rem;
}
.title { font-size: rem(24); } // 1.5remУ Sass есть и встроенные функции: lighten(), darken(), mix(), rgba(), nth(), map-get() и другие.
$primary: #2563eb;
.button:hover {
background: darken($primary, 8%);
}9. Циклы, условия и @content
Sass — настоящий язык: в нём есть @for, @each, @if и блок @content:
@for $i from 1 through 4 {
.col-#{$i} { width: 25% * $i; }
}
$breakpoints: (sm: 576px, md: 768px, lg: 992px);
@each $name, $px in $breakpoints {
@media (min-width: $px) {
.container-#{$name} { max-width: $px; }
}
}
@mixin hover-bg($color) {
&:hover {
background: $color;
}
}
.btn { @include hover-bg(#e5e7eb) { cursor: pointer; } }#{$var} нужна там, где переменная участвует в имени селектора или свойстве. Внутри значений пиши переменную как обычно.10. Медиа-запросы без дублирования
Sass позволяет держать медиа-запрос внутри селектора, а не копировать селекторы вниз файла:
.menu {
display: flex;
@media (max-width: 768px) {
display: none;
}
}
// Спокойнее: вся логика одного блока в одном месте.@media (min-width: ...) для расширения. На мобильной эмуляции в Chrome DevTools это удобно проверять вживую.11. Практика: мини-проект
Собери кнопку с состояниями и карточку в осмысленной структуре:
// _settings.scss
$primary: #2563eb;
$radius: 10px;
$space-sm: 8px;
$space-md: 16px;
// _components.scss
@use "settings";
.card {
border: 1px solid #e5e7eb;
border-radius: settings.$radius;
padding: settings.$space-md;
&__title { font-weight: 600; }
&__body { margin-top: settings.$space-sm; }
}
.btn {
display: inline-block;
padding: settings.$space-sm settings.$space-md;
border-radius: settings.$radius;
background: settings.$primary;
color: #fff;
&:hover { opacity: .9; }
&:focus-visible { outline: 2px solid #93c5fd; }
}Компонентная структура блоков по БЭМ и переменные из одного файла-настроек — стандарт промышленной вёрстки.
12. Sass и переменные против CSS Custom Properties
Переменные Sass работают на этапе компиляции: они подставляются в готовый CSS и недоступны браузеру. CSS Custom Properties (--var) живут в рантайме и могут меняться из JavaScript и медиа-запросов.
// переменные Sass — значения на этапе сборки
$color-primary: #2563eb;
// CSS-переменные — динамика в браузере
:root {
--color-accent: #f59e0b;
}
.toggle--dark .theme {
--color-accent: #fbbf24;
color: var(--color-accent);
}| Критерий | Sass-переменная | CSS-переменная |
|---|---|---|
| время жизни | во время компиляции | в браузере |
| меняется из JS | нет | да |
| подходит для | палитра, сетка, препроцессор | темы, стили компонентов |
| в итоговом CSS | не остаётся | остаётся |
13. Структура проекта с @use
В промышленных проектах стили разделяют по файлам. Классический паттерн — «7-1»: abstracts, base, components, layout, pages, themes, vendors.
scss/
├─ abstracts/ _variables.scss _mixins.scss
├─ base/ _reset.scss _typography.scss
├─ components/ _buttons.scss _cards.scss
├─ layout/ _header.scss _footer.scss
├─ pages/ _home.scss
├─ themes/ _dark.scss
├─ vendors/ _bootstrap.scss
└─ main.scss// main.scss — единственная точка входа
@use "abstracts/variables";
@use "abstracts/mixins";
@use "base/reset";
@use "base/typography";
@use "components/buttons";
@use "layout/header";@use важен: частичные файлы подключаются один раз, а имена неймспейсов (variables.$color) делают зависимости явными.14. Тёмная тема и переключение стилей
Соберём переключение темы на CSS-переменных, которые наполняются из Sass-настроек. Тёмная тема переопределяет только цвета, а вся вёрстка остаётся общей.
// tokens.scss
$colors-light: (bg: #ffffff, text: #111827);
$colors-dark: (bg: #111827, text: #f9fafb);
:root[data-theme="light"] {
--bg: #{map.get($colors-light, "bg")};
--text: #{map.get($colors-light, "text")};
}
:root[data-theme="dark"] {
--bg: #{map.get($colors-dark, "bg")};
--text: #{map.get($colors-dark, "text")};
}.app {
background: var(--bg);
color: var(--text);
}15. Source maps и отладка
Source maps связывают скомпилированный CSS с исходными файлами .scss: в DevTools вы видите не готовую кашу, а точную строку оригинального Sass. Без карт отладка превращается в поиск в двух файлах сразу.
// генерируем карты при компиляции
npx sass --watch --source-map styles.scss styles.css
// рядом появится styles.css.mapВ Vite карты включаются автоматически в dev-режиме. Помогает и сама консоль Sass: @debug печатает значение, @warn предупреждает, а @error останавливает компиляцию.
$gutter: 12px;
@debug $gutter; // main.scss:2 DEBUG: 12px
@warn "Переменная $gutter устарела";
@if not $gutter { @error "Нет отступов"; }16. Встроенные модули: sass:math и sass:color
Современный Sass выносит расчёты в модули. Классическое деление / в новых версиях уступает math.div(), а устаревшие lighten()/darken() заменяет color.adjust().
@use "sass:math";
@use "sass:color";
$primary: #2563eb;
.title { font-size: math.div(24px, 16px) * 1rem; } // 1.5rem
.btn { background: color.adjust($primary, $lightness: 8%); }
.line { border-color: color.mix(#fff, $primary, 25%); }
.rounded { width: math.round(14.7px); } // 15pxМодули sass:map и sass:list играют вместе с циклами из раздела 9: map.merge() объединяет словари брейкпоинтов, map.get() достаёт значение, а math.min()/math.max() ограничивают размеры.
@use "sass:map";
$bp: (sm: 576px, md: 768px, lg: 992px);
$bp: map.merge($bp, (xl: 1200px));
@media (min-width: map.get($bp, xl)) {
.g--xl { display: grid; }
}@use "sass:..." и вызывайте функции с префиксом модуля: так понятно, откуда пришла функция, и проще избежать конфликтов с собственными именами.17. Тонкая настройка вывода: %placeholder и @extend
Плейсхолдер %имя — селектор-призрак: он не попадает в итоговый CSS, пока его не унаследуют. А @extend объединяет селекторы в одно правило и делает вывод компактнее.
%card-base {
border: 1px solid #e5e7eb;
border-radius: 10px;
padding: 16px;
}
.form-card { @extend %card-base; }
.profile-card { @extend %card-base; }// Итог: один блок вместо двух копий
.form-card, .profile-card {
border: 1px solid #e5e7eb;
border-radius: 10px;
padding: 16px;
}Чем это отличается от миксина из раздела 6? Миксин копирует свойства в каждое место, поэтому итоговый CSS больше, зато селекторы независимы. @extend экономит байты, но создаёт групповые селекторы, которые труднее переопределять на маленьких экранах.
@extend внутри @media, не расширяйте «живые» классы библиотек и вложенные БЭМ-элементы — компилятор предупредит, а правила станут хрупкими.18. Stylelint, pre-commit и CI
Стили линтуют так же, как JavaScript. stylelint с SCSS-конфигом ловит лишнюю вложенность, разнобой в кавычках и «мёртвые» переменные ещё до ревью.
// .stylelintrc.json
{
"extends": ["stylelint-config-standard-scss"],
"rules": { "color-hex-length": "short" }
}
// package.json
"scripts": {
"lint:css": "stylelint \"**/*.scss\""
}Связка husky + lint-staged прогоняет линтер только по изменённым файлам перед коммитом, а в CI (например, GitHub Actions) задача выполняется на каждый пуш — «у меня всё работает» больше не аргумент.
| Конфиг | Философия | Когда брать |
|---|---|---|
| standard-scss | официальный «стандарт» | проект с нуля |
| sass-guidelines | правила от Хьюго Жирарделя | крупные команды |
| stylelint-high-performance | запрет «дорогих» свойств | сложные интерфейсы |
--fix, затем спорные правила обсуждайте на ревью и только потом включайте жёсткую проверку в CI.19. Оптимизация сборки CSS: минификация и purge
Sass пишет удобный для разработчика код — но именно на продакшн CSS должен прийти компактным. Современные сборщики делают три вещи: автопрефиксы, минификацию и удаление неиспользуемых стилей (purge).
// vite.config.js
css: { postcss: { plugins: [ autoprefixer() ] } }
// команда сборки
npm run build → dist/assets/*.css (минифицированный)- автопрефиксы добавляют
-webkit-и-moz-по списку browserslist; - минификация убирает пробелы и комментарии, CSS обычно сжимается на 60–80%;
- purgecss/Tailwind-style: скрипты проходят по шаблонам и оставляют только реально используемые селекторы — у крупных проектов экономия килобайт в десятки раз.
// purgecss.config.js
content: ['./src/**/*.html', './src/**/*.js']
defaultExtractor: content => content.match(/[A-Za-z0-9_-]+/g) || []"icon-" + name), надо перечислять в safelist иначе стили исчезнут на проде.20. Современный CSS против Sass
CSS развивается быстрее, чем раньше: нативные переменные, вложенность, @layer и color(): функции. Иногда препроцессор уже не нужен. Считаем честно, когда что использовать.
| Возможность | Нативный CSS | Sass |
|---|---|---|
| вложенность | да (c 2023, с ограничениями) | да, без ограничений |
| переменные | custom properties | компилируются в значения |
| миксины | нет | да |
| условия/циклы | нет | да |
При вложенности в нативном CSS помните: селекторы & работают, но без сасс-переменных. Custom properties живут при рантайме — их можно менять в JS, преимущество для тем (см. раздел 14).
.card {
--pad: 1rem;
padding: var(--pad);
&.active { border-color: gold; }
}- статичный сайт без сложной логики — чистый CSS достаточно;
- архитектура с миксинами, циклами, функциями — Sass по-прежнему выигрывает;
- оптимально: Sass для миксинов и токенов + custom properties для тем.
21. Дизайн-система: токены и утилиты
Крупный интерфейс не может жить на «магических» числах. Дизайн-система — это источник правды: токены (отступы, цвета, типографика) задаются один раз и переиспользуются.
$spacing-base: 8px;
// отступы растут кратно базе
$sp-1: $spacing-base; $sp-2: $spacing-base * 2; $sp-3: $spacing-base * 3;
$color-bg: #121420;
$color-accent: #ff6b35;
$color-text: #e8e8f0;Хорошая практика — миксин для типографики и fluid spacing через clamp():
@mixin fluid-type($min, $max) {
font-size: clamp(#{$min}px, #{$min}px + 1vw, #{$max}px);
}
.title-brand { @include fluid-type(20, 42); }- токены лучше хранить в отдельном
_tokens.scssметодом 7-1; - имена токенов отражают назначение (color-accent), а не значение;
- утилиты-миксины (fluid-type, container) переиспользуются во всех компонентах без дублирования.
22. Публикация библиотеки стилей (SCSS как npm-пакет)
Когда кнопки, отступы и миксины вы готовы переиспользовать в нескольких проектах, самое время опубликовать дизайн-пакет. Публичный репозиторий, npm-пакет с sass в exports — и команда тянет @import 'design-kit' отовсюду.
// package.json дизайн-пакета
{
"exports": { ".": "./src/index.scss" },
"scripts": { "build": "sass src/index.scss dist/design.css" }
}npm install @team/design-kit
@use '@team/design-kit' as kit;
.btn { @include kit.button-size('lg'); }- семантика версий: ломающие изменения сбрасывают мажор (1.x → 2.0);
- публикуйте и собранный CSS, и исходники SCSS — потребителям на разных стековых уровнях;
- покройте сборку тестами (stylelint из раздела 18) и примером в storybook-стиле.
Что дальше
Стили на Sass соединятся с разметкой из курса HTML и базовых стилей CSS. В Vue 3 и React Sass подключается парой строк, а сборку ускорит Vite. Проверить результат можно аудитом Lighthouse.