Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Sass и пользовательские свойства для тем

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

Темы в карте

Каждая тема — это карта из имён токенов и цветов. Создайте styles/themes/_themes.scss:

// styles/themes/_themes.scss
$themes: (
  "light": (
    "bg": #ffffff,
    "text": #1f2937,
    "accent": #2563eb,
  ),
  "dark": (
    "bg": #111827,
    "text": #f9fafb,
    "accent": #60a5fa,
  ),
);

Генерация пользовательских свойств

Миксин принимает одну тему и выводит пользовательское свойство для каждого токена. Имя свойства собирает интерполяция --#{$name}:

// styles/themes/_index.scss
@use "sass:map";
@use "themes";
 
@mixin theme-vars($theme) {
  @each $name, $value in $theme {
    --#{$name}: #{$value};
  }
}
 
:root {
  @include theme-vars(map.get(themes.$themes, "light"));
}
 
[data-theme="dark"] {
  @include theme-vars(map.get(themes.$themes, "dark"));
}

Светлая тема попадает в :root — она действует по умолчанию. Тёмная включается, когда у элемента есть атрибут data-theme="dark".

Компоненты используют var()

Компоненты больше не знают цветов — они читают пользовательские свойства. Эти значения вычисляются в браузере, поэтому следуют текущей теме:

// styles/components/_cards.scss
.card {
  padding: 16px;
  color: var(--text);
  background: var(--bg);
  border: 2px solid var(--accent);
}
// styles/main.scss
@use "themes";
@use "components/cards";
:root {
  --bg: #ffffff;
  --text: #1f2937;
  --accent: #2563eb;
}
 
[data-theme=dark] {
  --bg: #111827;
  --text: #f9fafb;
  --accent: #60a5fa;
}
 
.card {
  padding: 16px;
  color: var(--text);
  background: var(--bg);
  border: 2px solid var(--accent);
}

Sass убрал кавычки в [data-theme=dark] — для браузера это тот же селектор.

Переключение темы

Один атрибут на <html> переключает все цвета. Скрипт только меняет атрибут, все цвета остаются в CSS:

<style>
  :root {
    --bg: #ffffff;
    --text: #1f2937;
    --accent: #2563eb;
  }
 
  [data-theme=dark] {
    --bg: #111827;
    --text: #f9fafb;
    --accent: #60a5fa;
  }
 
  .card {
    padding: 16px;
    color: var(--text);
    background: var(--bg);
    border: 2px solid var(--accent);
  }
</style>
 
<div class="card">
  <p>Карточка следует за темой.</p>
  <button type="button" id="toggle">Сменить тему</button>
</div>
 
<script>
  document.querySelector('#toggle').addEventListener('click', () => {
    const root = document.documentElement;
    root.dataset.theme = root.dataset.theme === 'dark' ? 'light' : 'dark';
  });
</script>

Нажмите кнопку: фон, текст и рамка карточки меняются разом, без повторной компиляции.

Кто что делает

ЗадачаИнструмент
Хранить темы и токены, проверять именаКарты и функции Sass
Сгенерировать переменные для каждой темы@each в Sass
Переключить тему на страницеПользовательские свойства CSS
Прочитать цвет в компонентеvar(--accent)

Цветовые функции Sass здесь тоже помогают: например, оттенок для наведения можно вычислить через color.adjust() для каждой темы ещё при составлении карты.

Частая ошибка

Функции Sass не умеют работать с var(). color.adjust(var(--accent), $lightness: -10%) останавливается с ошибкой: при компиляции var(--accent) — просто текст, его цвет известен только в браузере. Вычисляйте оттенки в Sass до того, как они станут пользовательскими свойствами, или используйте функции CSS вроде color-mix(), которые работают в браузере.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.