Перейти к основному содержанию

Темная тема

Этот документ объясняет реализацию темной темы в @ui8kit/core, включая механизм провайдера тем, управление состоянием, стратегии сохранения и интеграцию с Tailwind CSS. Для общих вопросов тематизации и стилизации вариантов см. Система вариантов. Для паттернов стилизации конкретных компонентов см. Лучшие практики.

Назначение и область применения

Система темной темы обеспечивает автоматическое переключение тем с сохранением пользовательских предпочтений и определением темной темы на уровне системы. Реализация построена на паттерне провайдера React Context, который управляет состоянием темы, применяет CSS-классы к корню документа и синхронизирует предпочтения с localStorage. Источники: src/themes/providers/ThemeProvider.tsx1-93

Архитектура ThemeProvider

Компонент ThemeProvider оборачивает приложение и управляет всем состоянием, связанным с темой, через React Context. Он принимает объект theme, соответствующий типу ThemeBase, и предоставляет значения темы и функции управления через контекст.
API браузера

Управление состоянием

Компонент ThemeProvider

Уровень приложения

читает из

читает из

читает из

записывает в

обновляет

Корень приложения

Дочерние компоненты

ThemeProvider

ThemeContext

Хуки useState

Хуки useEffect

isDarkMode: boolean

prefersReducedMotion: boolean

theme: ThemeBase

localStorage
ключ ui:dark

matchMedia
prefers-color-scheme

matchMedia
prefers-reduced-motion

document.documentElement
classList.toggle('dark')
Диаграмма: Поток данных ThemeProvider и интеграция с API браузера Провайдер инициализирует состояние из трех источников в порядке приоритета:
  1. Значение из localStorage по ключу "ui:dark" (если присутствует)
  2. Системное предпочтение через window.matchMedia('(prefers-color-scheme: dark)')
  3. Значение по умолчанию false
Источники: src/themes/providers/ThemeProvider.tsx25-44 src/themes/providers/ThemeProvider.tsx70-83

Инициализация и сохранение состояния темной темы

Состояние темной темы следует определенному потоку инициализации и сохранения, который обеспечивает сохранение пользовательских предпочтений между сеансами, при этом учитывая системные предпочтения, когда явное предпочтение отсутствует.
Поток обновления

Поток инициализации

Да

Нет

Да

Нет

Монтирование ThemeProvider

Проверка localStorage
window.localStorage.getItem('ui:dark')

stored !== null?

Парсинг сохраненного значения
stored === '1' → true
stored === '0' → false

Проверка системного предпочтения
window.matchMedia('(prefers-color-scheme: dark)')

matchMedia
поддерживается?

Получить значение .matches

По умолчанию false

setIsDarkMode(value)

Изменение состояния isDarkMode

document.documentElement.classList.toggle('dark', isDarkMode)

document.documentElement.style.colorScheme = isDarkMode ? 'dark' : 'light'

localStorage.setItem('ui:dark', isDarkMode ? '1' : '0')
Диаграмма: Поток инициализации и синхронизации состояния темной темы Логика инициализации использует ленивую функцию-инициализатор состояния для вычисления начального значения один раз при монтировании. Механизм сохранения сохраняет значения как строки "1" (темная) или "0" (светлая) в localStorage по ключу "ui:dark". Источники: src/themes/providers/ThemeProvider.tsx28-35 src/themes/providers/ThemeProvider.tsx37-44

Интерфейс ThemeContextValue

ThemeContext предоставляет типизированный интерфейс для доступа к состоянию темы и функциям управления. Компоненты получают доступ к этому контексту через хук useTheme.
СвойствоТипОписание
themeT extends ThemeBaseОбъект конфигурации темы
roundedT['rounded']Варианты скругления углов из темы
buttonSizeT['buttonSize']Варианты размеров кнопок из темы
isDarkModebooleanТекущее состояние темной темы
isNavFixedboolean | undefinedОпциональное состояние фиксированной навигации
prefersReducedMotionbooleanПредпочтение пользователя по уменьшению движения
toggleDarkMode() => voidФункция для переключения темной темы
setDarkMode(value: boolean) => voidФункция для явной установки темной темы
Тип ThemeBase определяет минимальную структуру, необходимую для объектов тем:
export type ThemeBase = {
  name: string;
  rounded: Record<string, any> & { default: any };
  buttonSize: Record<string, any> & { default: any };
  isNavFixed?: boolean;
};
Источники: src/themes/providers/ThemeProvider.tsx4-10 src/themes/providers/ThemeProvider.tsx12-21

Использование хука useTheme

Компоненты получают доступ к контексту темы через хук useTheme, который обеспечивает использование внутри ThemeProvider и возвращает типизированное значение контекста. Паттерн использования хука:
import { useTheme } from '@ui8kit/core';

function ThemeToggle() {
  const { isDarkMode, toggleDarkMode } = useTheme();
  
  return (
    <button onClick={toggleDarkMode}>
      {isDarkMode ? '☀️' : '🌙'}
    </button>
  );
}
Реализация хука выбрасывает ошибку при использовании вне контекста провайдера, обеспечивая правильную настройку на уровне корня приложения. Источники: src/themes/providers/ThemeProvider.tsx87-93 README.md235-249

Реализация переключения темной темы

Библиотека предоставляет два метода для управления состоянием темной темы:

Функция toggleDarkMode

Инвертирует текущее состояние темной темы:
const { toggleDarkMode } = useTheme();
// Вызов toggleDarkMode() переключает между светлой и темной темой

Функция setDarkMode

Устанавливает темную тему в конкретное значение:
const { setDarkMode } = useTheme();
// setDarkMode(true)  → Принудительно включить темную тему
// setDarkMode(false) → Принудительно включить светлую тему
Обе функции запускают поток сохранения, который синхронно обновляет DOM и localStorage. Источники: src/themes/providers/ThemeProvider.tsx78-79 README.md240-248

Доступность: поддержка уменьшения движения

ThemeProvider определяет и отслеживает системное предпочтение пользователя prefers-reduced-motion через тот же паттерн медиа-запроса, который используется для определения темной темы.
Мониторинг во время выполнения

Инициализация

Очистка

Размонтирование компонента

removeEventListener

Монтирование компонента

window.matchMedia('prefers-reduced-motion: reduce')

setPrefersReducedMotion(matches)

Обработчик событий MediaQueryList

Обработчик события изменения

setPrefersReducedMotion(e.matches)
Диаграмма: Определение и мониторинг предпочтения уменьшения движения Реализация включает fallback для старых браузеров, использующих addListener/removeListener вместо addEventListener/removeEventListener. Компоненты могут получить доступ к этому значению через контекст для условного отключения анимаций. Источники: src/themes/providers/ThemeProvider.tsx46-68

Интеграция с Tailwind CSS

Система темной темы интегрируется с Tailwind CSS через класс .dark, применяемый к document.documentElement. Это требует конфигурации Tailwind с darkMode: 'class':
// tailwind.config.js
module.exports = {
  darkMode: 'class',
  // ... другая конфигурация
}
Когда isDarkMode имеет значение true, провайдер добавляет класс dark к корневому элементу, активируя модификаторы варианта dark: в Tailwind:
<div className="bg-white dark:bg-slate-900 text-black dark:text-white">
  Контент адаптируется к темной теме
</div>
Провайдер также устанавливает document.documentElement.style.colorScheme в "dark" или "light", что сигнализирует браузеру о предпочтениях пользователя для нативных элементов форм и полос прокрутки. Источники: src/themes/providers/ThemeProvider.tsx37-44 README.md219-233

Конфигурация темы

Приложения должны обернуть свой корневой компонент в ThemeProvider и передать объект темы. Библиотека экспортирует три предварительно настроенные темы:
Экспорт темыМодульНазвание темы
modernUITheme@ui8kit/core”modernUI”
skyOSTheme@ui8kit/core”skyOS”
lesseUITheme@ui8kit/core”lesseUI”
Паттерн настройки:
import { ThemeProvider, modernUITheme } from '@ui8kit/core';

export function App() {
  return (
    <ThemeProvider theme={modernUITheme}>
      <YourApp />
    </ThemeProvider>
  );
}
Каждый объект темы должен удовлетворять ограничению типа ThemeBase со свойствами name, rounded и buttonSize. Пользовательские темы могут быть созданы путем реализации этого интерфейса. Источники: src/themes/index.ts1-9 README.md223-233

Соображения по рендерингу на стороне сервера

Реализация включает безопасные для SSR проверки с использованием защитных условий typeof window !== 'undefined' и typeof document === 'undefined' для предотвращения ошибок во время выполнения при серверном рендеринге. Вычисление начального состояния избегает доступа к API браузера до фазы клиентской гидратации. Паттерн ленивого инициализатора состояния обеспечивает:
  1. Логика инициализации состояния выполняется только один раз при монтировании
  2. Доступ к API браузера безопасно защищен
  3. Окружения SSR получают разумные значения по умолчанию (false для темной темы)
Обработка ошибок оборачивает операции с localStorage в блоки try-catch, чтобы предотвратить блокировку приложения в окружениях с ограниченным доступом к хранилищу. Источники: src/themes/providers/ThemeProvider.tsx28-35 src/themes/providers/ThemeProvider.tsx37-44 src/themes/providers/ThemeProvider.tsx46-49