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);
}

Частые ошибки

  1. @import вместо @use@import устарел в Dart Sass и будет удалён
  2. Глубокая вложенность — больше 3 уровней создаёт длинные селекторы
  3. Sass-переменные для тем — используйте CSS custom properties для runtime-тем
  4. Злоупотребление @extend — может создавать громоздкий CSS
  5. Один файл на 1000+ строк — разбивайте на partials

Практика

  • Создать файловую структуру с partials и @use
  • Написать mixins для responsive breakpoints
  • Использовать maps для генерации utility-классов
  • Создать BEM-компонент с вложенностью через &
  • Переделать @import на @use/@forward

Связанные темы

  • BEM — BEM + Sass = идеальная пара
  • CSS Modules — альтернативный подход к изоляции
  • CSS переменные — нативные переменные для runtime

Ресурсы