Sass: что реально нужно, а что вредно
Половина возможностей препроцессора переехала в обычный CSS. Разбираем, что осталось незаменимым, а что теперь только вредит.
Что ты научишься делать
- Поймёшь принципиальную разницу между переменными Sass и CSS
- Увидишь, во что превращается вложенность и сколько кода даёт миксин
- Решишь для конкретного проекта, нужен ли препроцессор вообще
#Зачем он был нужен
Sass появился, когда в CSS не было ни переменных, ни вложенности, ни функций. Он давал всё это ценой одного шага сборки — и это был честный обмен.
С тех пор CSS сильно вырос. Переменные есть с 2016 года, вложенность — с 2023, математика через calc() была всегда. Половина причин брать препроцессор исчезла.
Вопрос перестал быть «использовать ли Sass». Он стал «что именно из Sass мне ещё нужно».
#Переменные: разница принципиальная
Это не два способа сделать одно и то же. Это разные вещи, которые случайно похоже выглядят.
Переменная Sass существует только во время сборки. В готовом файле её нет — там подставлено значение. Поменять её из браузера, из скрипта или медиазапросом невозможно.
Переменная CSS живёт в браузере. Наследуется, переопределяется в любом селекторе, меняется скриптом и читается через getComputedStyle.
| Sass: <code>$accent</code> | CSS: <code>--accent</code> | |
|---|---|---|
| Когда существует | Только при сборке | В браузере, во время работы |
| Наследуется потомкам | Нет | Да |
| Меняется в медиазапросе | Нет | Да |
| Меняется скриптом | Нет | Да |
| Темизация | Только пересборкой | Переключением атрибута |
| Где уместна | Значения, известные заранее: брейкпоинты, шаги шкалы | Всё, что может меняться: цвета, размеры, темы |
#Вложенность: удобно и опасно
Вложенность экономит буквы и делает структуру наглядной. Расплата — специфичность, которая растёт незаметно.
Каждый уровень добавляет часть к селектору. Три уровня — и получается вес, который потом нечем перебить, кроме ещё более длинного селектора.
Так не надо
.page {
.sidebar {
.card {
.title { font-size: 18px; }
}
}
}
/* На выходе: .page .sidebar .card .title — вес 0-4-0 */Читается приятно, а результат почти неперебиваемый. Чтобы поменять размер в одном месте, придётся написать селектор ещё длиннее.
Так надо
.card {
padding: 16px;
&__title { font-size: 18px; }
&:hover { box-shadow: var(--shadow-2); }
}
/* На выходе: .card__title — вес 0-1-0 */Вложенность используется для группировки, но селектор остаётся плоским. Амперсанд склеивает имя, а не добавляет уровень.
#Миксины: копирование, а не переиспользование
Миксин выглядит как функция, но ведёт себя как шаблон: при каждом вызове его содержимое вставляется целиком.
Десять вызовов миксина из восьми свойств — это восемьдесят строк в готовом файле. На глаз этого не видно: исходник-то короткий.
/* Плохо: миксин копирует одно и то же в каждый вызов */@mixin card-base { padding: 16px; background: #fff; border-radius: 14px; box-shadow: 0 2px 8px rgb(20 24 48 / .08);}.product { @include card-base; }.article { @include card-base; }.promo { @include card-base; }/* Лучше: общий класс в разметке, ноль дублей в CSS */.card { padding: 16px; background: #fff; border-radius: 14px; box-shadow: 0 2px 8px rgb(20 24 48 / .08);}Миксин оправдан там, где повторяется логика, а не набор свойств: сложный медиазапрос, обёртка над градиентом с параметрами, генерация нескольких правил разом.
#Что осталось незаменимым
- Циклы и списки. Сгенерировать двадцать классов отступов по шкале в чистом CSS нечем. Это главная причина, по которой Sass до сих пор в проектах.
- Разбиение на файлы через
@use. В CSS есть@import, но он делает лишний сетевой запрос и работает медленнее. Sass склеивает файлы при сборке. - Функции с вычислениями. Округление, работа со списками, генерация имён — в CSS этого нет.
- Общий код для нескольких проектов. Библиотека миксинов, подключаемая в несколько сайтов сразу.
$spaces: (0: 0, 1: 4px, 2: 8px, 3: 12px, 4: 16px, 6: 24px, 8: 32px); @each $name, $value in $spaces { .p-#{$name} { padding: $value; } .m-#{$name} { margin: $value; } .gap-#{$name} { gap: $value; } } /* Двадцать одно правило из семи строк исходника. В чистом CSS их пришлось бы написать руками */#@use вместо @import
Старый @import в Sass объявлен устаревшим. Он подключал файлы в общее пространство имён: две переменные с одинаковым именем из разных файлов молча перетирали друг друга.
@use подключает файл под своим именем — становится видно, откуда что взялось.
/* main.scss */ @use "tokens"; @use "mixins" as m; .card { padding: tokens.$space-4; @include m.card-shadow; } /* Видно, что переменная из tokens, а миксин из mixins. При старом @import это было бы неотличимо */#Когда НЕ надо
- Ради вложенности. Она есть в чистом CSS и работает так же. Это больше не аргумент.
- Ради переменных. Переменные CSS мощнее: они живут в браузере. Sass-переменные — шаг назад для всего, что связано с темами.
- На маленьком проекте. Сборка добавляет шаг между правкой и результатом. На лендинге в пятьсот строк это чистая потеря времени.
- Когда команда не знает Sass. Обычный CSS читают все. Препроцессор — это ещё один язык, который придётся объяснять каждому новому человеку.
#Как решать в конкретном проекте
Есть ли уже сборка?
Если проект и так собирается — Sass почти бесплатен. Если сборки нет, он тянет за собой всю инфраструктуру ради нескольких удобств.
Нужны ли генерируемые классы?
Набор утилит, шкала отступов, сетка колонок — здесь циклы окупаются сразу.
Сколько тем?
Больше одной — цвета обязаны быть на переменных CSS независимо от того, есть Sass или нет.
Кто будет это поддерживать?
Если проект передадут человеку без опыта сборки, чистый CSS окажется дешевле в поддержке, даже если писать его дольше.
Где спотыкаются почти все
Тема собрана на переменных Sass
Почему В готовом CSS переменных нет — переключать нечего. Тёмную тему приходится делать вторым файлом.
Как надо Всё, что зависит от темы, — только на переменных CSS. Sass оставь для того, что известно на этапе сборки.
Вложенность повторяет структуру разметки
Почему Селекторы вырастают до четырёх уровней, специфичность становится неперебиваемой, а перенос блока в другое место ломает стили.
Как надо Не глубже двух уровней. Для имён используй склейку через &, а не вложенные селекторы.
Миксин вместо общего класса
Почему Каждый вызов копирует все свойства. Исходник короткий, готовый файл раздувается.
Как надо Повторяется набор свойств — заводи класс. Миксин нужен для повторяющейся логики.
Используется устаревший @import
Почему Общее пространство имён: переменные из разных файлов молча перетирают друг друга.
Как надо Переходи на @use — источник каждой переменной становится виден.
- Препроцессор preprocessor
- Инструмент, превращающий расширенный синтаксис в обычный CSS на этапе сборки.
- Переменная Sass Sass variable
- Значение, подставляемое при сборке. В готовом файле её нет.
- Миксин mixin
- Шаблон правил, содержимое которого копируется в каждое место вызова.
- Интерполяция interpolation
- Подстановка значения в имя селектора:
.gap-#{$name}.
Проверь себя
Sass-переменная подставляется при сборке. В браузере остаётся только конечное значение.
Каждый уровень добавляет часть к селектору. Отсюда и незаметный рост специфичности.
Миксин — это шаблон, а не ссылка. Для повторяющегося набора свойств дешевле общий класс.
Вложенность и переменные в CSS уже есть. А сгенерировать двадцать классов по списку — нечем.
Старый <code>@import</code> сваливал всё в общее пространство имён, и переменные молча перетирали друг друга.
Задание
Разобрать повторы
Перед тобой чистый CSS с тремя одинаковыми наборами свойств и цветами, размазанными по правилам. Sass тут нет и не будет — задача в том, чтобы решить ту же проблему средствами самого CSS.
Вынеси повторяющийся набор в общий класс .card, а цвета — в переменные :root. Разметку можно менять: добавь общий класс к трём блокам.
Подсказка 1
Добавь класс в разметку: <article class="card product">, <article class="card promo"> и так далее.
Подсказка 2
Общее вынеси в .card, а в .promo и .note оставь только то, чем они отличаются.
Подсказка 3
Цвета — в :root { --color-surface: #fff; --color-accent: #6544e0; --color-muted: #5b6070; }
Показать решение
<article class="card product"> <h3>Раф</h3> <p>240 ₽</p></article><article class="card promo"> <h3>Акция</h3> <p>Второй кофе бесплатно</p></article><article class="card note"> <h3>Заметка</h3> <p>Работаем до 22:00</p></article>:root { --color-surface: #ffffff; --color-accent: #6544e0; --color-muted: #5b6070; --radius-md: 14px; --space-4: 16px;}/* Общее — один раз */.card { padding: var(--space-4); background: var(--color-surface); border-radius: var(--radius-md); box-shadow: 0 2px 8px rgb(20 24 48 / .08); margin-bottom: 12px;}/* Отличия — только они */.promo { border-left: 3px solid var(--color-accent); }.note { color: var(--color-muted); }h3 { margin: 0 0 6px; font-size: 16px; }p { margin: 0; font-size: 14px; }