LEARN X · ЗА 13 МИН

Sass/SCSS

Sass/SCSS за 13 минут: переменные, вложенность, миксины, функции, @extend, циклы, мапы, интерполяция и партиалы — весь препроцессор в комментированном коде.

Sass — это препроцессор CSS: вы пишете на расширенном языке с переменными, вложенностью, миксинами и циклами, а компилятор превращает его в обычный CSS, который понимают браузеры. SCSS — самый популярный синтаксис Sass: это надмножество CSS, поэтому любой валидный CSS — уже валидный SCSS. Весь препроцессор уместился на одной странице — читайте код сверху вниз.

Что такое Sass/SCSS

SCSS-файлы имеют расширение .scss и компилируются в .css. У SCSS два вида комментариев.

// Однострочный комментарий: НЕ попадёт в скомпилированный CSS.

/* Многострочный комментарий: попадёт в CSS (если не сжатие). */

// SCSS — надмножество CSS: обычные правила работают как есть.
body {
  margin: 0;
  font-family: sans-serif;
}

// Компиляция через CLI Dart Sass:
//   sass input.scss output.css
//   sass --watch src:dist   // следить за изменениями

Переменные

Переменная начинается с $. Значения бывают разных типов: числа, строки, цвета, булевы, списки, мапы, null.

// Объявление переменных разных типов:
$primary: #3498db;            // цвет
$base-font: 16px;             // число с единицей
$ratio: 1.5;                  // число
$font-stack: "Inter", sans-serif; // строка/список
$enabled: true;               // булево
$gap: null;                   // пустое значение

// Список (через пробел или запятую):
$sizes: 8px 16px 24px;

// Использование:
.button {
  background: $primary;
  font: #{$base-font} / $ratio $font-stack;
}

// !default — задать значение, только если переменная ещё не определена
// (удобно для настраиваемых библиотек):
$radius: 4px !default;

Вложенность (nesting)

Селекторы можно вкладывать друг в друга. Символ & ссылается на родительский селектор.

.card {
  padding: 16px;
  border: 1px solid #ddd;

  // Вложенный селектор → .card .title
  .title {
    font-weight: bold;
  }

  // & = родитель. Это → .card:hover
  &:hover {
    border-color: #999;
  }

  // & справа → .theme-dark .card
  .theme-dark & {
    background: #222;
  }

  // Конкатенация имени → .card__footer (БЭМ)
  &__footer {
    margin-top: 8px;
  }

  // Вложенность свойств с общим префиксом:
  font: {
    size: 14px;
    weight: 400;
  }
}

Миксины (@mixin / @include)

Миксин — переиспользуемый блок объявлений. Объявляется через @mixin, подключается через @include, принимает аргументы.

// Миксин без аргументов:
@mixin flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

// С аргументами и значением по умолчанию:
@mixin button($bg, $color: white, $pad: 8px 16px) {
  background: $bg;
  color: $color;
  padding: $pad;
  border: none;
}

.modal {
  @include flex-center;
}

.btn-primary {
  // По позиции и по имени:
  @include button(#3498db, $pad: 12px 24px);
}

// @content — вставить произвольный блок внутрь миксина:
@mixin on-hover {
  &:hover { @content; }
}

.link {
  @include on-hover {
    color: red; // этот блок попадёт внутрь &:hover
  }
}

Функции (@function и встроенные)

Функция возвращает значение через @return. Sass даёт много встроенных функций для цветов, чисел и строк.

// Своя функция:
@function rem($px, $base: 16px) {
  @return ($px / $base) * 1rem;
}

.text {
  font-size: rem(24px); // → 1.5rem
}

// Встроенные функции для цвета:
$base: #3498db;
.box {
  border-color: darken($base, 10%);   // темнее
  background: lighten($base, 30%);     // светлее
  color: rgba($base, 0.5);             // прозрачность
  outline-color: mix($base, red, 50%); // смешать два цвета
}

// Числовые и строковые:
//   percentage(0.4) → 40%
//   round(4.6)      → 5
//   to-upper-case("abc") → "ABC"

Наследование (@extend и плейсхолдеры)

@extend переносит стили одного селектора в другой. Плейсхолдер %name не компилируется сам по себе — только когда его расширяют.

// Плейсхолдер: в CSS попадёт, только если его @extend'нут
%card-base {
  padding: 16px;
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}

.info-card {
  @extend %card-base;
  background: #e8f4fd;
}

.warn-card {
  @extend %card-base; // селекторы сгруппируются в общем правиле
  background: #fff3cd;
}

// @extend можно применять и к обычным классам:
.message { padding: 10px; }
.error {
  @extend .message;
  color: red;
}

Управляющие директивы (@if, @for, @each, @while)

Условия и циклы позволяют генерировать стили программно.

// Условие:
@mixin theme($dark) {
  @if $dark {
    background: #111;
    color: #eee;
  } @else {
    background: #fff;
    color: #111;
  }
}

// Цикл @for (генерируем сетку отступов):
@for $i from 1 through 5 {
  .m-#{$i} { margin: #{$i * 4}px; }
}

// Цикл @each по списку:
$colors: red, green, blue;
@each $name in $colors {
  .text-#{$name} { color: $name; }
}

// @each с распаковкой пар:
@each $name, $hex in ("primary": #3498db, "danger": #e74c3c) {
  .bg-#{$name} { background: $hex; }
}

// Цикл @while:
$i: 1;
@while $i <= 3 {
  .col-#{$i} { width: $i * 10%; }
  $i: $i + 1;
}

Операции

Sass умеет арифметику с числами и единицами, сравнения и конкатенацию строк.

.layout {
  // Математика:
  width: 100% - 20px;        // вычитание
  height: 200px / 2;         // деление → 100px
  margin: 8px * 3;           // умножение → 24px
  padding: 10px + 5px;       // сложение → 15px

  // Сравнения дают true/false:
  // 10px > 5px → true,  $a == $b,  $a != $b

  // Логические операторы: and, or, not
}

// Конкатенация строк через +:
$dir: "images";
.hero {
  background: url("/" + $dir + "/bg.png");
}

// Внимание: в современном Dart Sass деление / устарело —
// используйте функцию math.div($a, $b) из модуля sass:math.

Партиалы и импорт (@use / @import)

Код разбивают на частичные файлы — партиалы, имена которых начинаются с подчёркивания (_colors.scss). Подключают современным @use (или устаревшим @import).

// Файл _variables.scss (партиал, отдельно не компилируется):
$primary: #3498db;
$radius: 4px;

// Файл main.scss — современный способ @use:
@use "variables";          // подключаем партиал
@use "sass:math";          // встроенный модуль

.button {
  // К членам модуля обращаемся через пространство имён:
  background: variables.$primary;
  border-radius: variables.$radius;
  width: math.div(100%, 3);
}

// Можно задать псевдоним или убрать префикс:
@use "variables" as v;     // v.$primary
@use "variables" as *;     // без префикса: $primary

// Устаревший способ (всё в глобальной области, дубли):
// @import "variables";

Мапы (map)

Мапа — набор пар ключ: значение. Доступ к значению — через map-get (или map.get из модуля sass:map).

// Объявление мапы:
$breakpoints: (
  "mobile": 480px,
  "tablet": 768px,
  "desktop": 1200px,
);

$theme: (
  "bg": #ffffff,
  "text": #222222,
);

.container {
  // Достаём значение по ключу:
  background: map-get($theme, "bg");
  color: map-get($theme, "text");
}

// Перебор мапы циклом:
@each $name, $size in $breakpoints {
  // ...используем $name и $size
}

// Полезные функции:
//   map-has-key($map, "mobile") → true/false
//   map-keys($map)   → список ключей
//   map-values($map) → список значений

Интерполяция #{}

Конструкция #{ } вставляет значение переменной или выражения в строку — в имена селекторов, свойств, медиа-запросов и URL.

$side: "left";
$size: 16;

.box {
  // Интерполяция в имени свойства:
  margin-#{$side}: 8px;          // → margin-left

  // В значении со строкой:
  content: "Размер: #{$size}px";

  // Склейка числа с единицей:
  width: #{$size}px;             // → 16px
}

// В имени селектора:
$name: "alert";
.#{$name}-box { padding: 12px; }  // → .alert-box

// В медиа-запросе (без интерполяции переменная не подставится):
$bp: 768px;
@media (min-width: #{$bp}) {
  .nav { display: flex; }
}

Практический пример: адаптивные миксины и тема

Соберём всё вместе: мапа брейкпоинтов, миксин медиа-запроса, мапа темы и генерация утилит циклом.

@use "sass:map";

// 1) Брейкпоинты и миксин адаптивности:
$breakpoints: (
  "sm": 576px,
  "md": 768px,
  "lg": 992px,
);

@mixin respond($key) {
  $value: map.get($breakpoints, $key);
  @if $value {
    @media (min-width: #{$value}) {
      @content; // блок-стили попадут внутрь медиа-запроса
    }
  } @else {
    @warn "Нет брейкпоинта: #{$key}";
  }
}

// 2) Тема как мапа + функция-аксессор:
$theme: (
  "primary": #3498db,
  "danger":  #e74c3c,
  "text":    #222222,
);

@function color($key) {
  @return map.get($theme, $key);
}

// 3) Применяем адаптивность и тему:
.container {
  padding: 16px;
  color: color("text");

  @include respond("md") {
    padding: 32px; // только на экранах >= 768px
  }
}

// 4) Генерируем кнопки по теме циклом:
@each $name, $hex in $theme {
  .btn-#{$name} {
    background: $hex;
    color: white;

    &:hover {
      background: darken($hex, 8%);
    }
  }
}
Поддержать проект