Автоматическое определение системной темы

MUI (Material-UI) предоставляет встроенные механизмы для автоматического определения и применения темы интерфейса в зависимости от настроек операционной системы пользователя. Это особенно полезно для реализации тёмного и светлого режима, который подстраивается под системные предпочтения.

Определение системной темы с useMediaQuery

MUI использует хук useMediaQuery для отслеживания предпочтений пользователя. Основное правило — использовать CSS-медиа-запрос prefers-color-scheme.

Пример базовой реализации:

import { useMemo } from 'react';
import { createTheme, ThemeProvider, useMediaQuery } from '@mui/material';

function App() {
  const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');

  const theme = useMemo(
    () =>
      createTheme({
        palette: {
          mode: prefersDarkMode ? 'dark' : 'light',
        },
      }),
    [prefersDarkMode],
  );

  return (
    <ThemeProvider theme={theme}>
      {/* Компоненты приложения */}
    </ThemeProvider>
  );
}

export default App;

Ключевые моменты:

  • useMediaQuery('(prefers-color-scheme: dark)') возвращает true, если система настроена на тёмный режим.
  • useMemo используется для предотвращения лишнего пересоздания темы при каждом рендере.
  • ThemeProvider обеспечивает передачу темы всем компонентам MUI.

Динамическая реакция на изменения темы

Система пользователя может изменять тему на лету (например, смена времени суток на iOS или Android). Хук useMediaQuery автоматически обновляет значение, что позволяет темам переключаться динамически без перезагрузки страницы.

const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)', {
  noSsr: true,
});

Опция noSsr: true необходима, если приложение рендерится на сервере, чтобы избежать рассинхронизации темы между сервером и клиентом.

Расширение темы с учетом системной палитры

MUI позволяет создавать кастомизированные палитры, которые меняются в зависимости от системной темы:

const getDesignTokens = (mode) => ({
  palette: {
    mode,
    ...(mode === 'light'
      ? {
          primary: { main: '#1976d2' },
          background: { default: '#f5f5f5' },
        }
      : {
          primary: { main: '#90caf9' },
          background: { default: '#121212' },
        }),
  },
});

const theme = useMemo(() => createTheme(getDesignTokens(prefersDarkMode ? 'dark' : 'light')), [prefersDarkMode]);

Преимущества такого подхода:

  • Разделение палитр по режимам повышает читабельность кода.
  • Обеспечивает согласованность стиля компонентов при переключении тем.

Интеграция с локальными настройками пользователя

Для улучшения UX можно комбинировать системную тему и пользовательский выбор. Например, сохранять предпочтение в localStorage и использовать его как приоритет над системной темой:

const storedMode = localStorage.getItem('themeMode');
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const mode = storedMode || (prefersDarkMode ? 'dark' : 'light');

Такой подход обеспечивает:

  • Автоматическое определение темы при первом заходе.
  • Сохранение пользовательских предпочтений при последующих посещениях.

Использование CssBaseline для согласованности стилей

MUI рекомендует применять компонент CssBaseline совместно с темой для корректного отображения фона и цвета текста на уровне всего документа:

import { CssBaseline } from '@mui/material';

<ThemeProvider theme={theme}>
  <CssBaseline />
  {/* Приложение */}
</ThemeProvider>

CssBaseline автоматически подстраивает цвета под выбранный режим (light или dark) и устраняет дефолтные стили браузера, обеспечивая единый визуальный стиль.

Настройка системных медиазапросов для кастомных компонентов

Для компонентов, не использующих палитру MUI напрямую, можно применять хук useMediaQuery для изменения стилей вручную:

const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');

const styles = {
  container: {
    backgroundColor: prefersDarkMode ? '#121212' : '#fff',
    color: prefersDarkMode ? '#fff' : '#000',
  },
};

Это позволяет расширять поддержку системной темы на любые элементы интерфейса, даже если они не являются стандартными компонентами MUI.

Итоговые рекомендации

  • Использовать useMediaQuery('(prefers-color-scheme: dark)') для определения системного режима.
  • Создавать тему через createTheme с разделением на light и dark палитры.
  • Применять useMemo для оптимизации производительности при динамическом изменении темы.
  • Интегрировать с localStorage для сохранения пользовательских предпочтений.
  • Использовать CssBaseline для согласованного применения темы ко всему приложению.
  • Для нестандартных компонентов применять useMediaQuery напрямую для управления стилями.

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