Использование кастомных свойств для динамического изменения тем

Динамические темы на кастомных свойствах: как сделать это красиво и не сойти с ума

Присаживайся, наливай кофе. Сегодня поговорим о вещи, которая стабильно прилетает в таски на любом живом проекте. Рано или поздно к тебе приходит тимлид или дизайнер и говорит: «Нам нужна темная тема. И еще розовая для промо-кампании. И чтобы пользователь мог сам выбирать акцентный цвет в личном кабинете. И чтоб все это переключалось без перезагрузки страницы, плавно и без костылей».

Если первая мысль, которая у тебя возникает — это создать десять разных CSS-файлов или обмазаться классами типа .dark-theme на каждом кнопке, выдохни. Мы живем в 2026 году. У нас есть кастомные свойства (они же CSS-переменные), и они решают эту задачу изящно, в несколько строк кода. Давай разберем, как сделать архитектуру тем гибкой, масштабируемой и приятной в поддержке.

Как мы страдали раньше

Вспомни (или содрогнись, если не застал), как мы выкручивались во времена расцвета SASS и LESS. Мы создавали переменные вроде $brand-color: #3b82f6;. Но препроцессоры компилируются на сервере или в процессе сборки. В браузере они превращаются в обычный статичный CSS. Динамики там ноль.

Чтобы сделать темную тему, нам приходилось писать тонны дублирующего кода:

/* Старый кошмар */
.button {
  background-color: #ffffff;
  color: #333333;
}

body.dark-theme .button {
  background-color: #1a1a1a;
  color: #ffffff;
}

Представь масштаб трагедии, когда проект разрастается до сотен компонентов. Ты получаешь бесконечные простыни переопределений, воюешь со специфичностью селекторов и постоянно забываешь покрасить какую-нибудь мелкую иконку. Кстати, если ты подзабыл базовые принципы работы с современными переменными, обязательно почитай о том, почему переменные (CSS Variables) — это основа масштабируемого дизайна.

Как делать правильно в 2026 году

Современный подход строится на разделении структуры и темы. Твои компоненты вообще не должны знать, какого они цвета. Они должны знать только одно: «я использую переменную --bg-primary для фона».

Мы объявляем базовые семантические переменные на уровне :root, а затем просто переопределяем их значения в зависимости от атрибута (например, data-theme), который вешаем на тег html или body. Это позволяет переключать темы одной строчкой на JavaScript.

Более того, мы можем использовать медиа-запросы для автоматического определения системных настроек пользователя (светлая/темная тема в ОС), а JS использовать только для ручного переопределения.

Если хочешь пойти еще дальше и делать сложные анимации переходов или строго типизировать значения, загляни в наш гайд по строгой типизации CSS-переменных с правилом @property. Ну а для классического гибкого переключения тем нам хватит стандартных кастомных свойств.

Готовый сниппет кода

Вот рабочий, чистый шаблон, который ты можешь забрать в свой проект прямо сейчас. Здесь настроена светлая тема по умолчанию, автоматическая темная тема под системные настройки и ручной переключатель на JavaScript.

<!-- HTML -->
<!DOCTYPE html>
<html lang="ru" data-theme="auto">
<head>
  <meta charset="UTF-8">
  <title>Dynamic Themes</title>
</head>
<body>
  <div class="card">
    <h1>Привет, мидл!</h1>
    <p>Переключай тему и смотри, как магия CSS делает всю грязную работу.</p>
    <button id="theme-toggle" class="btn">Сменить тему</button>
  </div>
</body>
</html>
/* CSS */
/* 1. Семантические переменные для светлой темы */
:root {
  --bg-app: #f4f4f9;
  --bg-card: #ffffff;
  --text-main: #1a1a1a;
  --accent: #3b82f6;
  --border-color: #e5e7eb;
}

/* 2. Автоматическая темная тема (через системные настройки) */
@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] {
    --bg-app: #0f172a;
    --bg-card: #1e293b;
    --text-main: #f8fafc;
    --accent: #60a5fa;
    --border-color: #334155;
  }
}

/* 3. Принудительная темная тема при ручном переключении */
:root[data-theme="dark"] {
  --bg-app: #0f172a;
  --bg-card: #1e293b;
  --text-main: #f8fafc;
  --accent: #60a5fa;
  --border-color: #334155;
}

/* 4. Стилизация компонентов (никакого хардкода цветов!) */
body {
  background-color: var(--bg-app);
  color: var(--text-main);
  font-family: system-ui, sans-serif;
  transition: background-color 0.3s ease, color 0.3s ease;
  display: grid;
  place-items: center;
  min-height: 100vh;
  margin: 0;
}

.card {
  background-color: var(--bg-card);
  border: 1px solid var(--border-color);
  padding: 2rem;
  border-radius: 12px;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  max-width: 400px;
  transition: background-color 0.3s ease, border-color 0.3s ease;
}

.btn {
  background-color: var(--accent);
  color: #ffffff;
  border: none;
  padding: 0.75rem 1.5rem;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 600;
  transition: filter 0.2s ease;
}

.btn:hover {
  filter: brightness(1.1);
}
// JavaScript для переключения темы
const toggleBtn = document.getElementById('theme-toggle');
const htmlElement = document.documentElement;

toggleBtn.addEventListener('click', () => {
  const currentTheme = htmlElement.getAttribute('data-theme');
  let newTheme = 'light';

  if (currentTheme === 'light' || currentTheme === 'auto') {
    newTheme = 'dark';
  }

  htmlElement.setAttribute('data-theme', newTheme);
  // Опционально сохраняем выбор пользователя в localStorage
  localStorage.setItem('user-theme', newTheme);
});

Частая ошибка новичков

Самый частый факап, который я вижу на код-ревью у ребят, только пришедших на проект — это буквальное (литеральное) именование переменных.

Никогда. Слышишь? Никогда не пиши вот так:

/* ТАК ДЕЛАТЬ НЕ НАДО */
:root {
  --white: #ffffff;
  --black: #000000;
}

[data-theme="dark"] {
  --white: #000000; /* Взрыв мозга гарантирован */
  --black: #ffffff;
}

Когда у тебя в темной теме переменная --white начинает возвращать черный цвет, код превращается в театр абсурда. Вся команда начнет плеваться уже на второй день поддержки.

Правило простое: именуй кастомные свойства по их роли в интерфейсе (семантически), а не по конкретному значению. Вместо цвета пиши его назначение: --bg-primary, --text-muted, --border-active. Тогда при смене темы тебе нужно будет поменять только значения, а логика останется кристально чистой.

На этом всё. Внедряй кастомные свойства, береги нервы коллег и пиши красивый CSS!

🔥 Больше фишек, готовых сниппетов и передовых подходов к CSS мы публикуем в нашем Telegram-канале. Подписывайтесь, чтобы не пропустить!

🚀 Прокачай свой код

Готовые CSS-сниппеты, разбор продвинутых фишек и эксклюзивные материалы — в нашем Telegram-канале.

Подписаться
error: Content is protected !!
Прокрутить вверх