Урок 13. Справочник: директивы и классы темы

📁 Раздел: Состояния и темы ⏱️ Время изучения: ~50 мин 🎯 Сложность: Средняя

⚡ Кратко

dark: — вариант тёмной темы, @custom-variant — способ привязать его к классу, @theme inline — семантические токены.

Директивы

ЗаписьЧто делает
dark:bg-slate-900утилита в тёмной теме
@custom-variant dark (&:where(.dark, .dark *));тема по классу .dark
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));тема по data-атрибуту
@theme { … }токены темы
@theme inline { … }токен подставляется как var(--…) — значение можно менять в рантайме
Зачем нужен inline: без него утилита ссылается на var(--color-surface), а значение фиксируется в момент сборки. С inline утилита получает var(--surface) — ту самую переменную, которую вы переопределяете в .dark. Только так тема переключается без пересборки.

Полезные классы для тёмной темы

КлассЗачем
dark:bg-slate-900основной фон
dark:text-slate-100основной текст
dark:border-slate-700границы светлее фона
dark:ring-white/10замена тени
dark:shadow-noneубрать бесполезную тень
dark:bg-white/5лёгкая подсветка поверхности
dark:opacity-90приглушить яркую картинку
scheme-light-darkсообщить браузеру о поддержке обеих тем (полосы прокрутки, системные элементы)
⚠️ Проверить по документации: набор утилит scheme-* для свойства color-scheme появился в v4 — уточните доступные значения в разделе Color scheme.

Набор ролей для токенов

ТокенРоль
--background / --foregroundфон страницы и основной текст
--card / --card-foregroundповерхность карточки
--popover / --popover-foregroundвсплывающие слои
--primary / --primary-foregroundосновное действие
--secondary, --muted, --accentвторостепенные поверхности
--destructiveопасное действие
--border, --input, --ringграницы, поля, фокус

Это набор shadcn/ui — он проверен практикой и покрывает обычное приложение. Полностью разбирается в уроке 19.

Переключатель: минимальный код

// src/theme.ts — переключение темы и запоминание выбора
const KEY = 'theme'

export function applyTheme(theme: 'light' | 'dark' | 'system') {
  const isDark =
    theme === 'dark' ||
    (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)

  document.documentElement.classList.toggle('dark', isDark)
  localStorage.setItem(KEY, theme)
}

// вызывать как можно раньше, до первой отрисовки
applyTheme((localStorage.getItem(KEY) as 'light' | 'dark' | 'system') ?? 'system')
<!-- index.html: скрипт до отрисовки, чтобы не было мигания -->
<script>
  const saved = localStorage.getItem('theme') ?? 'system'
  const dark = saved === 'dark' ||
    (saved === 'system' && matchMedia('(prefers-color-scheme: dark)').matches)
  document.documentElement.classList.toggle('dark', dark)
</script>