CSS переменные

CSS-переменные (custom properties) — пользовательские свойства, начинающиеся с --, которые хранят значения для повторного использования через функцию var.

Зачем нужно

CSS-переменные устраняют дублирование значений (цвета, размеры, шрифты), упрощают тематизацию и позволяют менять стили динамически через JavaScript. В отличие от переменных Sass, они работают в рантайме и наследуются по DOM-дереву.

Где используется

  • Дизайн-токены (цвета, отступы, радиусы, шрифты)
  • Тёмная/светлая тема
  • Компонентный дизайн (настройка через переменные)
  • Динамическое изменение стилей через JS
  • Адаптивный дизайн (переопределение переменных в медиа-запросах)

Предпосылки

Синтаксис

Объявление

/* Глобальные переменные — на :root */
:root {
  --color-primary: #007bff;
  --color-secondary: #6c757d;
  --color-success: #28a745;
  --color-danger: #dc3545;

  --font-family: 'Inter', 'Segoe UI', sans-serif;
  --font-size-base: 16px;
  --font-size-lg: 1.25rem;
  --font-size-sm: 0.875rem;

  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 32px;

  --border-radius: 8px;
  --shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  --transition: 200ms ease-in-out;
}

Использование через var

.button {
  background-color: var(--color-primary);
  font-family: var(--font-family);
  font-size: var(--font-size-base);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--border-radius);
  box-shadow: var(--shadow);
  transition: background-color var(--transition);
}

.card {
  padding: var(--spacing-lg);
  border-radius: var(--border-radius);
  box-shadow: var(--shadow);
  margin-bottom: var(--spacing-md);
}

Fallback-значения

.element {
  /* Если --color-accent не определена, используется #ff6600 */
  color: var(--color-accent, #ff6600);

  /* Вложенные fallback */
  background: var(--bg-custom, var(--bg-default, white));

  /* Fallback с другой переменной */
  font-size: var(--font-size-custom, var(--font-size-base));
}

Fallback срабатывает только когда переменная не определена. Если переменная определена, но имеет невалидное значение — fallback НЕ сработает, свойство получит initial.

Область видимости (scope) и наследование

CSS-переменные наследуются по DOM-дереву и могут переопределяться на любом уровне:

:root {
  --color: blue;
}

.sidebar {
  --color: green; /* Переопределение для .sidebar и всех потомков */
}

.sidebar .title {
  color: var(--color); /* green */
}

.main .title {
  color: var(--color); /* blue (от :root) */
}

Компонентные переменные

/* Компонент с настраиваемыми переменными */
.alert {
  --alert-bg: #f8f9fa;
  --alert-color: #333;
  --alert-border: #dee2e6;

  background: var(--alert-bg);
  color: var(--alert-color);
  border: 1px solid var(--alert-border);
  padding: var(--spacing-md);
  border-radius: var(--border-radius);
}

/* Варианты через переопределение переменных */
.alert-success {
  --alert-bg: #d4edda;
  --alert-color: #155724;
  --alert-border: #c3e6cb;
}

.alert-danger {
  --alert-bg: #f8d7da;
  --alert-color: #721c24;
  --alert-border: #f5c6cb;
}

Тёмная тема

:root {
  --bg: #ffffff;
  --bg-secondary: #f5f5f5;
  --text: #1a1a1a;
  --text-secondary: #666666;
  --border: #e0e0e0;
}

/* Через медиа-запрос */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1a1a1a;
    --bg-secondary: #2d2d2d;
    --text: #e0e0e0;
    --text-secondary: #999999;
    --border: #404040;
  }
}

/* Или через класс (переключается JS) */
.theme-dark {
  --bg: #1a1a1a;
  --bg-secondary: #2d2d2d;
  --text: #e0e0e0;
  --text-secondary: #999999;
  --border: #404040;
}

/* Стили используют переменные — тема меняется автоматически */
body {
  background: var(--bg);
  color: var(--text);
}

.card {
  background: var(--bg-secondary);
  border: 1px solid var(--border);
}

Адаптивные переменные

:root {
  --container-width: 1200px;
  --font-size-h1: 2.5rem;
  --spacing-section: 80px;
}

@media (max-width: 768px) {
  :root {
    --container-width: 100%;
    --font-size-h1: 1.75rem;
    --spacing-section: 40px;
  }
}

.container {
  max-width: var(--container-width);
  margin: 0 auto;
}

h1 {
  font-size: var(--font-size-h1);
}

section {
  padding: var(--spacing-section) 0;
}

Взаимодействие с JavaScript

// Чтение переменной
const root = document.documentElement;
const primary = getComputedStyle(root).getPropertyValue('--color-primary');
// ' #007bff' (с пробелом в начале — trim!)

// Запись переменной
root.style.setProperty('--color-primary', '#ff6600');

// Удаление переопределения
root.style.removeProperty('--color-primary');

// Установка на конкретном элементе
const card = document.querySelector('.card');
card.style.setProperty('--card-bg', 'yellow');

Пример: слайдер меняет размер

<input type="range" id="size-slider" min="12" max="48" value="16">
<p class="demo-text">Текст меняет размер</p>
.demo-text {
  font-size: var(--dynamic-size, 16px);
}
document.getElementById('size-slider').addEventListener('input', (e) => {
  document.documentElement.style.setProperty(
    '--dynamic-size', e.target.value + 'px'
  );
});

CSS-переменные vs Sass-переменные

Особенность CSS --var Sass $var
Работа В рантайме При компиляции
Наследование по DOM Да Нет
Изменение через JS Да Нет
Медиа-запросы Переопределяются Нет
Fallback var(--x, fallback) $x: default !default
Scope DOM-дерево Блок кода

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

  1. Невалидное значение не вызывает fallback — свойство становится initial:
    :root { --size: red; } /* определена, но невалидна для width */
    .box {
      width: var(--size, 100px); /* fallback НЕ сработает! */
      /* width получит initial = auto */
    }
    
  2. Нельзя использовать в именах свойств:
    /* ОШИБКА */
    :root { --prop: margin; }
    .box { var(--prop): 20px; } /* Так нельзя */
    
  3. Нельзя собрать имя свойства или единицу:
    :root { --size: 20; }
    /* ОШИБКА */
    .box { width: var(--size)px; } /* Не сработает */
    /* ПРАВИЛЬНО */
    .box { width: calc(var(--size) * 1px); }
    
  4. Переменная с пробеломgetPropertyValue возвращает значение с пробелом в начале

Практика

  • Создать набор дизайн-токенов (цвета, шрифты, отступы) как CSS-переменные
  • Реализовать тёмную тему переопределением переменных
  • Создать компонент .button с вариантами через переменные
  • Изменить переменную через JavaScript
  • Использовать fallback-значения

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

Ресурсы