CSS переменные vs SCSS переменные - когда какие?
Сегодня покажу вам, как я выбираю между CSS custom properties (--var) и SCSS-переменными ($var). Оба инструмента решают похожую задачу, но работают в разное время и по-разному влияют на проект.
1) Как они живут
- SCSS $var - подставляются на этапе сборки. В итоговом CSS переменных уже нет. Быстро, просто, но не изменить в рантайме.
- CSS --var - существуют в браузере, участвуют в каскаде, могут меняться через медиа- и контейнер-запросы, селекторы, JS. Идеальны для тем, адаптивов и динамики.
2) Примеры на пальцах
Тема/бренд + тёмный режим (CSS)
:root { --primary: #4f46e5; }
.button { background: var(--primary); }
/* Тёмная тема меняет только значение */
.dark { --primary: #7c3aed; }
Адаптив без дублирования (CSS)
:root { --gap: 8px; }
@media (min-width: 768px) {
:root { --gap: 16px; }
}
.grid { gap: var(--gap); }
Живая смена из JS (CSS)
document.documentElement.style.setProperty('--primary', '#22c55e');
Дизайн-токены, генерация классов (SCSS)
$colors: (
primary: #4f46e5,
success: #16a34a,
danger: #dc2626
);
@function color($name) { @return map-get($colors, $name); }
@each $name, $val in $colors {
.text-#{$name} { color: $val; }
}
.btn--primary { background: color(primary); }
Флюидная типографика (CSS)
:root { --fs-h1: clamp(1.75rem, 1rem + 3vw, 3rem); }
h1 { font-size: var(--fs-h1); }
3) Когда что выбрать
- Берите SCSS $var, если:
- значения никогда не меняются в браузере;
- нужно удобно генерировать кучу классов/правил из карт и циклов;
- вы хотите ранний фейл при ошибке (компилятор сразу ругнётся).
- Берите CSS --var, если:
- требуется темизация, переключатели, A/B, кастомизация у пользователя;
- значения зависят от медиа/контейнер-запросов;
- хотите использовать calc(), clamp() и каскад как фичу.
4) Частые ошибки
- «Почему $primary не меняется в тёмной теме?» - потому что это SCSS и оно уже подставлено при сборке.
- var(--x) без фоллбэка:
color: var(--link, #2563eb); /* ← безопаснее */
- Переопределение не там, где действует селектор. Помните: CSS-переменные каскадируют — задавайте их на нужной “высоте” (:root, .dark, контейнер).
5) Любимый гибрид-подход (очень практично)
- Храню источник правды в SCSS-карте → генерю из неё CSS-переменные в :root. Так у меня есть и удобство сборки, и сила рантайма.
$tokens: (
primary: #4f46e5,
spacing: 8px,
radius: 12px
);
:root {
@each $k, $v in $tokens {
--#{$k}: #{$v};
}
}
/* дальше в CSS / компоненте */
.card {
padding: var(--spacing);
border-radius: var(--radius);
background: var(--primary);
}
.dark { --primary: #7c3aed; }
6) Мини-практика (сделайте прямо сейчас)
1. Возьмите ваш $colors в SCSS и сгенерируйте из него :root { --color-name: value }.
2. Переключите тему добавлением класса .dark на и переопределите 2–3 ключевых токена.
3. Переведите один медиазависимый размер (например, `gap`) на CSS-переменную как в примере выше.
7) Чеклист напоследок
- Нужна динамика/тема/адаптив → CSS --var.
- Нужна генерация/сборка/статичность → SCSS $var.
- Большой проект? Делайте гибрид: токены → :root через SCSS.
👉