Урок 13. Домашнее задание: тёмная тема проекта
⚡ Кратко
Добавить в проект курса полноценную тёмную тему на семантических токенах плюс переключатель на три положения без мигания.
Задание
- описать в
@theme inlineнабор ролей: фон, текст, поверхность карточки, приглушённый текст, граница, акцент; - задать значения для светлой и тёмной темы;
- перевести все ранее собранные страницы проекта на токены;
- сделать переключатель «светлая / тёмная / системная» с сохранением выбора;
- убрать вспышку светлой темы при загрузке.
Подготовка окружения
cd tailwind-course
npm run dev
Пошаговое решение
Шаг 1. Роли и значения
/* src/index.css */
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
@theme inline {
--color-surface: var(--surface);
--color-surface-foreground: var(--surface-foreground);
--color-brand: var(--brand);
}
:root {
--surface: oklch(1 0 0);
--surface-foreground: oklch(0.21 0.01 265);
--brand: oklch(0.55 0.21 264);
}
.dark {
--surface: oklch(0.21 0.01 265);
--surface-foreground: oklch(0.98 0 0);
--brand: oklch(0.62 0.19 264);
}
Начните с шести токенов — этого достаточно. Расширять будете по мере появления новых поверхностей.
Шаг 2. Замена цветов в разметке
Пройдитесь по компонентам и замените bg-white →
bg-card, text-slate-900 → text-foreground,
text-slate-500 → text-muted-foreground,
border-slate-200 → border-border.
Шаг 3. Переключатель
// 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')
Кнопки переключателя должны показывать текущее состояние — например,
подсветкой активного варианта через aria-pressed.
Шаг 4. Проверка
Откройте каждую страницу в обеих темах. Особое внимание — полям формы, отключённым кнопкам, состояниям наведения и таблицам с зеброй.
Как проверить
- При перезагрузке страницы вспышки нет ни в одной теме.
- В классах компонентов не осталось
dark:(кроме особых случаев вроде теней). - Контраст основного текста в тёмной теме не ниже 4.5:1.
- Режим «системная» реагирует на смену настройки ОС без перезагрузки.
Ориентир
Мой проект
Карточка на токенах
Выглядит правильно в обеих темах.
<div class="space-y-3 rounded-xl border border-border bg-background p-4">
<div class="flex items-center justify-between">
<h3 class="text-sm font-semibold text-foreground">Мой проект</h3>
<div class="inline-flex rounded-lg border border-border p-0.5 text-xs">
<button class="rounded-md px-2 py-1 text-muted-foreground">☀️</button>
<button class="rounded-md bg-primary px-2 py-1 text-primary-foreground">🌙</button>
<button class="rounded-md px-2 py-1 text-muted-foreground">💻</button>
</div>
</div>
<div class="rounded-lg border border-border bg-card p-3 text-card-foreground">
<p class="text-sm font-medium">Карточка на токенах</p>
<p class="mt-1 text-xs text-muted-foreground">Выглядит правильно в обеих темах.</p>
</div>
<button class="w-full rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:opacity-90">Основное действие</button>
</div>