CSS

CSS: переменные и темы

📚 20 вопросовПройти тест →
Лекция

CSS: переменные и темы

CSS

CSS: переменные и темы

Введение: один источник стилей для всего интерфейса

Когда в проекте десятки экранов, одни и те же цвета, отступы и размеры начинают дублироваться.
Без системы любое изменение превращается в поиск по сотням строк.
CSS-переменные решают это как "единый пульт управления": меняете значение в одном месте, и интерфейс обновляется везде.
На этой базе строятся темы, дизайн-токены и стабильная поддержка проекта.
💡 Совет: Сначала выделите базовые токены (color, space, radius), потом начинайте усложнять тему.
Вывод: Переменные в CSS нужны для управления стилем на уровне системы, а не отдельных блоков.

Проблема -> решение

Проблема: Фиксированные значения "вшиты" в компоненты: тема меняется долго, стили расходятся, а поддержка дорожает.
Решение: Вынести ключевые значения в CSS custom properties, задавать их иерархически (глобально и локально) и строить тему через переопределение токенов.
Вывод: Переменные превращают разрозненный CSS в управляемую архитектуру стилей.

Чем помогает и как работает

CSS-переменные помогают:
  • снизить дублирование значений по проекту;
  • ускорить запуск светлой/темной и брендовых тем;
  • делать компоненты переиспользуемыми в разных контекстах;
  • уменьшить риск "сломать весь UI" при изменении цвета или размера.
Как это работает:
  • Шаг 1: определяете базовые дизайн-токены (--color-bg, --space-m, --radius-m).
  • Шаг 2: объявляете их на уровне :root как глобальные значения.
  • Шаг 3: используете токены в компонентах через var(...).
  • Шаг 4: локально переопределяете переменные внутри нужного контейнера.
  • Шаг 5: создаете тему через data-theme или media query и меняете только токены.
  • Шаг 6: добавляете fallback-значения для устойчивости компонентов.
  • Шаг 7: фиксируете правила именования и структуру токенов для команды.
Вывод: Тема на CSS-переменных это не магия, а дисциплина работы с общими значениями.

Ключевые термины (простыми словами)

  • Custom properties (CSS-переменные) - значения, которые можно переиспользовать.
  • :root - корневой селектор для глобальных переменных.
  • var() - функция, которая подставляет значение переменной.
  • Fallback (резерв) - запасное значение в var(--token, reserve).
  • Scope (область действия) - где переменная видна и применяется.
  • Design tokens (дизайн-токены) - стандартизированные значения интерфейса.
  • Theme (тема) - набор токенов для конкретного визуального режима.

1. Глобальные токены через :root

Назначение: Задать базовые значения, общие для всего приложения.
Простыми словами: Это "главная таблица стилей", из которой берут цвета и размеры все компоненты.
Для новичка: Если значение повторяется в нескольких местах, ему почти всегда нужен токен в :root.
Аналогия: Единый склад материалов для всей стройки.
Пример:
:root {  --color-bg: #ffffff;  --color-text: #111111;  --color-accent: #2563eb;  --space-m: 16px;  --radius-m: 12px;}
🔎 Как это происходит на практике:
  • В проекте есть кнопки, карточки и формы.
  • Все они берут цвет текста из --color-text.
  • Дизайнер меняет тон текста, разработчик правит одно значение в :root.
Характеристики:
  • ✅ единый источник правды для стилей;
  • ✅ быстрые массовые изменения;
  • ✅ легче поддержка и ревью.
Когда использовать: Всегда для базовых цветовых, пространственных и типографических токенов.
Вывод: :root это точка старта любой стабильной темы.

2. var() и fallback-значения

Назначение: Подставлять токены в свойства и страховать компонент резервным значением.
Простыми словами: var() берет значение из переменной, а fallback спасает, если переменной нет.
Для новичка: Пишите fallback в библиотечных компонентах, которые могут жить вне вашего приложения.
Аналогия: Если основной ключ не подошел, используем запасной.
Пример:
.badge {  color: var(--color-accent, #2563eb);  border-radius: var(--radius-m, 10px);}
🔎 Как это происходит на практике:
  • Компонент "badge" вынесли в отдельный пакет.
  • В новом проекте токен --color-accent пока не определен.
  • Fallback не дает компоненту сломаться визуально.
Характеристики:
  • ✅ повышает устойчивость UI-компонентов;
  • ✅ упрощает интеграцию между проектами;
  • ✅ снижает риск "прозрачного" текста и пустых стилей.
Когда использовать: В shared-компонентах и там, где токен может быть не объявлен.
Вывод: Fallback это базовая страховка для надежного CSS.

3. Scope и локальные переменные

Назначение: Переопределять глобальные токены точечно внутри конкретного блока.
Простыми словами: Вы можете дать одной карточке свой фон, не ломая остальные карточки.
Для новичка: Переменная, заданная в блоке, действует внутри него и его потомков.
Аналогия: Общие правила для здания и локальные правила для одной комнаты.
Пример:
.card {  --card-bg: #ffffff;  background: var(--card-bg);} .card--accent {  --card-bg: #eef2ff;}
🔎 Как это происходит на практике:
  • Есть базовая карточка курса.
  • Для промо-карточки нужен другой фон.
  • Вместо дублирования стилей переопределяют только локальную переменную.
Характеристики:
  • ✅ минимум дублирования CSS;
  • ✅ удобно для модификаторов;
  • ✅ сохраняется читаемость структуры.
Когда использовать: Для вариаций компонента (accent, warning, compact, premium).
Вывод: Scope делает тему гибкой без хаоса в классах.

4. Темы через data-theme и переопределение токенов

Назначение: Переключать визуальный режим интерфейса без переписывания всех компонентов.
Простыми словами: Компоненты остаются теми же, меняется только набор значений переменных.
Для новичка: Лучше переключать тему на уровне html или body, а не в каждом компоненте отдельно.
Аналогия: Сменить фильтр цвета для всей сцены одним переключателем.
Пример:
:root {  --bg: #ffffff;  --text: #111111;  --accent: #2563eb;} [data-theme="dark"] {  --bg: #0b0f19;  --text: #f9fafb;  --accent: #60a5fa;} body {  background: var(--bg);  color: var(--text);}
🔎 Как это происходит на практике:
  • На странице есть toggle "Light / Dark".
  • JS меняет data-theme у <html>.
  • Компоненты автоматически получают новые цвета через токены.
Характеристики:
  • ✅ быстрое переключение темы;
  • ✅ минимальные изменения в CSS компонентов;
  • ✅ проще поддерживать несколько брендов.
Когда использовать: Для светлой/темной темы, white-label проектов и брендовых палитр.
Вывод: Тема это переопределение токенов, а не копия всей таблицы стилей.

5. prefers-color-scheme и системная тема

Назначение: Учитывать системные предпочтения пользователя автоматически.
Простыми словами: Если у пользователя в системе темная тема, сайт может подстроиться сам.
Для новичка: Это хороший базовый слой, даже если у вас есть ручной переключатель.
Аналогия: Интерфейс подстраивает освещение под режим комнаты.
Пример:
@media (prefers-color-scheme: dark) {  :root {    --bg: #0b0f19;    --text: #f9fafb;  }}
🔎 Как это происходит на практике:
  • Пользователь открывает сайт ночью с включенной dark-темой ОС.
  • Базовая палитра автоматически уходит в темный режим.
  • UX кажется "родным" и менее утомляющим для глаз.
Характеристики:
  • ✅ работает без JS;
  • ✅ повышает комфорт;
  • ✅ улучшает доступность.
Когда использовать: Как минимум для автоматической базовой темы в любом современном интерфейсе.
Вывод: Системная тема это ожидание пользователя, а не редкий бонус.

6. Именование токенов и структура темы

Назначение: Держать токены понятными и стабильными в долгой поддержке.
Простыми словами: Название токена должно сразу объяснять смысл, а не конкретный цвет.
Для новичка: Лучше --color-text-primary, чем --blue-500, если токен отвечает за роль в интерфейсе.
Аналогия: Подписанные ящики на складе быстрее находить и сложнее перепутать.
Пример:
:root {  --color-surface: #ffffff;  --color-text-primary: #111111;  --color-text-secondary: #6b7280;  --color-action-primary: #2563eb;}
🔎 Как это происходит на практике:
  • В проект приходит новый разработчик.
  • По именам токенов он быстро понимает, что за что отвечает.
  • На ревью меньше споров и случайных замен.
Характеристики:
  • ✅ предсказуемость для команды;
  • ✅ легче масштабировать дизайн-систему;
  • ✅ меньше "магических" значений в коде.
Когда использовать: Всегда, когда токены растут больше 10-15 штук.
Вывод: Хорошее именование токенов экономит месяцы поддержки.

7. calc(), clamp() и динамические токены

Назначение: Строить гибкие размеры на основе базовых переменных.
Простыми словами: Один токен может управлять целым ритмом отступов и типографики.
Для новичка: Начинайте с одного базового spacing-токена и умножайте его через calc().
Аналогия: Один модуль конструктора, из которого собираются разные размеры.
Пример:
:root {  --space: 8px;  --font-base: 16px;} .section {  padding: calc(var(--space) * 3);} h1 {  font-size: clamp(1.5rem, 2vw, 2.25rem);}
🔎 Как это происходит на практике:
  • Команда решает увеличить "плотность" интерфейса.
  • Меняют базовый --space с 8px на 6px.
  • Большинство отступов автоматически перестраивается.
Характеристики:
  • ✅ единый масштаб размеров;
  • ✅ меньше ручных правок;
  • ✅ проще адаптивность.
Когда использовать: Для систем отступов, размеров элементов и типографики.
Вывод: Динамические токены делают тему управляемой, а не статичной.

8. Переключение темы в runtime и сохранение выбора

Назначение: Дать пользователю управлять темой и помнить его выбор между сессиями.
Простыми словами: Человек выбрал темную тему, перезагрузил страницу, тема осталась.
Для новичка: CSS отвечает за токены, JS обычно отвечает за изменение data-theme и сохранение в localStorage.
Аналогия: Запомнить любимый режим освещения и включать его автоматически.
Пример:
html[data-theme="light"] { color-scheme: light; }html[data-theme="dark"] { color-scheme: dark; }
const root = document.documentElement;const next = root.dataset.theme === "dark" ? "light" : "dark";root.dataset.theme = next;localStorage.setItem("theme", next);
🔎 Как это происходит на практике:
  • На сайте есть кнопка "Сменить тему".
  • При клике меняется data-theme.
  • Выбор пользователя сохраняется и применяется при следующем входе.
Характеристики:
  • ✅ улучшает персонализацию;
  • ✅ поддерживает долгие пользовательские сессии;
  • ✅ снижает повторные настройки при каждом посещении.
Когда использовать: Если продуктом пользуются регулярно и тема влияет на комфорт чтения.
Вывод: Runtime-переключение завершает систему тем и делает ее удобной для пользователя.

Must-know факты (часто спрашивают и часто путают)

  • CSS-переменные вычисляются в runtime и могут переопределяться по каскаду.
  • var(--token, fallback) защищает компонент от отсутствующих токенов.
  • Глобальные и локальные токены должны быть согласованы по именованию.
  • Тема должна менять токены, а не дублировать стили всех компонентов.
  • prefers-color-scheme полезен даже при ручном переключателе темы.
  • Нельзя оставлять в компоненте "жесткие" цвета, если проект работает на токенах.
  • Для сложных систем нужны semantic-токены, а не только raw-цвета.
  • Смена темы должна тестироваться на контраст и читаемость.
Вывод: Понимание этих пунктов отличает "просто CSS" от зрелой темы на продакшене.

Сравнение подходов

ПодходЧто меняетсяПлюсыРиск
Жесткие значения в каждом компонентеКаждый селектор отдельноБыстрый старт на маленьком экранеБыстрое накопление хаоса
Глобальные токены в :rootЕдиные переменныеЦентрализованная поддержкаНужна дисциплина именования
Локальные переопределения токеновКонтекст компонентаГибкие вариацииЛегко запутаться без правил
Тема через data-themeНабор токеновМасштабируемая смена темНужна проверка контраста и UX

Часто спрашивают на собеседованиях

  1. В чем ключевое отличие CSS-переменных от препроцессорных переменных?
  2. Когда использовать глобальный токен, а когда локальный?
  3. Зачем нужен fallback в var()?
  4. Как правильно строить светлую/темную тему через токены?
  5. Что выбрать: prefers-color-scheme или ручной переключатель?
  6. Какие naming-правила токенов работают в больших командах?
  7. Какие ошибки чаще всего ломают тему на продакшене?
Вывод: На интервью обычно проверяют архитектурное мышление, а не только синтаксис var().

Типичные ошибки

Ошибка 1: Дублирование raw-значений

Неправильно: Писать один и тот же hex-цвет в десятках мест.
Правильно: Вынести значение в токен и использовать через var().
Почему: Иначе смена темы и рефакторинг становятся дорогими.

Ошибка 2: Нет fallback в shared-компоненте

Неправильно: Полагаться на токен, который может отсутствовать.
Правильно: Добавлять резервное значение во втором аргументе var().
Почему: Это защищает компонент от визуальной поломки вне основного проекта.

Ошибка 3: Тема реализована копипастой стилей

Неправильно: Создавать отдельный набор дублирующих правил под dark mode.
Правильно: Переопределять только токены темы.
Почему: Код остается компактным и проще поддерживается.

Ошибка 4: Плохое именование токенов

Неправильно: Использовать непонятные или смешанные названия (--blue1, --text2).
Правильно: Использовать semantic-имена по роли (--color-text-primary).
Почему: Иначе команда теряет скорость и ошибается при масштабировании.

Ошибка 5: Игнорирование prefers-color-scheme

Неправильно: Не учитывать системные настройки пользователя.
Правильно: Добавить media-query или синхронизацию с ручным переключателем.
Почему: Пользователь ожидает согласованное поведение интерфейса.

Ошибка 6: Смешение токенов и "магических" чисел

Неправильно: Часть отступов через токены, часть через случайные px.
Правильно: Строить ритм от базовых токенов и calc().
Почему: Только так поддерживается единая система пространства.

Best Practices

  • Введите минимальный набор core-токенов и фиксируйте его в документации.
  • Держите semantic-токены между компонентом и raw-палитрой.
  • Проверяйте контраст в обеих темах как часть Definition of Done.
  • Для компонентной библиотеки используйте fallback в критичных токенах.
  • Разделяйте ответственность: CSS-токены за стили, JS за переключение режима.
  • Не добавляйте новый токен, пока не уверены, что он решает повторяющуюся задачу.

Заключение

CSS-переменные и темы это базовый уровень зрелого фронтенда.
Они дают контроль, ускоряют изменения и позволяют развивать интерфейс без постоянного "перекраивания" всей кодовой базы.

Ключевые мысли

  • Переменные это система управления стилями, а не просто синтаксис.
  • Тема должна строиться на переопределении токенов.
  • Качество темы определяется не только красотой, но и устойчивостью, доступностью и поддерживаемостью.
🎯

Проверьте знания

Закрепите материал — пройдите тест по теме «CSS: переменные и темы»

Пройти тест →