Sass
Sass (SCSS) — CSS-препроцессор, который добавляет переменные, вложенность, миксины, функции, циклы и модульность. SCSS-файлы компилируются в обычный CSS.
Зачем нужно
CSS мощный, но ему не хватает программных конструкций: переменных с логикой, функций, циклов, наследования. Sass добавляет эти возможности, позволяя писать более организованный и DRY-код. Хотя современный CSS закрыл часть этих потребностей (custom properties, nesting), Sass по-прежнему актуален для крупных проектов.
Где используется
- Крупные проекты с множеством файлов стилей
- Design-системы с переменными и миксинами
- Проекты, где нужны циклы и условная логика в CSS
- Legacy-проекты (Bootstrap 4/5 написан на Sass)
Предпосылки
- CSS переменные — нативные переменные CSS
- BEM — Sass отлично работает с BEM
- Установка:
npm install -D sass
Переменные
// Sass-переменные — компилируются в значения (нет в runtime)
$primary: #007bff;
$danger: #dc3545;
$font-base: 16px;
$spacing: 8px;
$border-radius: 6px;
.button {
background: $primary;
padding: $spacing * 1.5 $spacing * 3;
border-radius: $border-radius;
font-size: $font-base;
}
// Переменные по умолчанию (можно переопределить ДО импорта)
$primary: #007bff !default;
Sass-переменные vs CSS custom properties: Sass-переменные компилируются и исчезают. CSS custom properties (
--var) живут в браузере и могут меняться в runtime. Для тем используйте CSS custom properties.
Вложенность (Nesting)
.card {
padding: 16px;
border: 1px solid #dee2e6;
border-radius: 12px;
&__title {
font-size: 1.25rem;
margin-bottom: 8px;
}
&__body {
color: #6c757d;
line-height: 1.6;
}
// & = родительский селектор
&:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
&--featured {
border-color: $primary;
}
}
// Компилируется в:
// .card { ... }
// .card__title { ... }
// .card__body { ... }
// .card:hover { ... }
// .card--featured { ... }
Не вкладывайте глубже 3 уровней! Глубокая вложенность создаёт длинные селекторы и повышает специфичность.
Mixins — переиспользуемые блоки
// Определение
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@mixin responsive($breakpoint) {
@if $breakpoint == tablet {
@media (min-width: 768px) { @content; }
} @else if $breakpoint == desktop {
@media (min-width: 1024px) { @content; }
}
}
@mixin button-variant($bg, $color: white) {
background: $bg;
color: $color;
&:hover {
background: darken($bg, 10%);
}
}
// Использование
.hero {
@include flex-center;
min-height: 100dvh;
}
.container {
padding: 16px;
@include responsive(tablet) {
padding: 24px;
max-width: 720px;
}
@include responsive(desktop) {
max-width: 960px;
}
}
.btn-primary {
@include button-variant(#007bff);
}
.btn-danger {
@include button-variant(#dc3545);
}
@extend — наследование
%button-base {
display: inline-flex;
align-items: center;
padding: 10px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
}
.button-primary {
@extend %button-base;
background: $primary;
color: white;
}
.button-secondary {
@extend %button-base;
background: #6c757d;
color: white;
}
// % — placeholder (не компилируется сам по себе)
Предпочитайте mixins вместо @extend — extend может создавать неожиданные комбинации селекторов. Mixins предсказуемее.
Partials и @use / @forward
Файловая структура
styles/
├── _variables.scss
├── _mixins.scss
├── _reset.scss
├── components/
│ ├── _button.scss
│ ├── _card.scss
│ └── _nav.scss
├── layout/
│ ├── _header.scss
│ └── _footer.scss
└── main.scss
@use (замена @import)
// _variables.scss
$primary: #007bff;
$spacing: 8px;
// _mixins.scss
@use 'variables' as vars;
@mixin spacing($multiplier: 1) {
padding: vars.$spacing * $multiplier;
}
// components/_button.scss
@use '../variables' as *;
@use '../mixins';
.button {
background: $primary;
@include mixins.spacing(2);
}
// main.scss
@use 'reset';
@use 'components/button';
@use 'components/card';
@forward — реэкспорт
// _index.scss
@forward 'variables';
@forward 'mixins';
@forward 'functions';
// Использование — один import вместо трёх
@use 'index' as *;
Maps (ассоциативные массивы)
$colors: (
primary: #007bff,
success: #28a745,
danger: #dc3545,
warning: #ffc107,
);
// Получение значения
.button {
background: map-get($colors, primary);
}
// Итерация
@each $name, $color in $colors {
.text()-#{$name} {
color: $color;
}
.bg-#{$name} {
background-color: $color;
}
}
Циклы и условия
@for
@for $i from 1 through 12 {
.col-#{$i} {
width: percentage($i / 12);
}
}
@if / @else
@mixin theme($mode) {
@if $mode == dark {
background: #1a1a2e;
color: #e9ecef;
} @else if $mode == light {
background: white;
color: #333;
} @else {
@error "Unknown theme: #{$mode}";
}
}
Встроенные функции
.element {
color: lighten(#007bff, 20%);
background: darken(#007bff, 10%);
border-color: rgba(#007bff, 0.5);
width: percentage(3 / 12);
}
Частые ошибки
- @import вместо @use —
@importустарел в Dart Sass и будет удалён - Глубокая вложенность — больше 3 уровней создаёт длинные селекторы
- Sass-переменные для тем — используйте CSS custom properties для runtime-тем
- Злоупотребление @extend — может создавать громоздкий CSS
- Один файл на 1000+ строк — разбивайте на partials
Практика
- Создать файловую структуру с partials и @use
- Написать mixins для responsive breakpoints
- Использовать maps для генерации utility-классов
- Создать BEM-компонент с вложенностью через
& - Переделать @import на @use/@forward
Связанные темы
- BEM — BEM + Sass = идеальная пара
- CSS Modules — альтернативный подход к изоляции
- CSS переменные — нативные переменные для runtime