Переключение между темами

MUI (Material-UI) предоставляет мощный механизм работы с темами, который позволяет управлять внешним видом компонентов приложения централизованно. Основным инструментом является объект theme, создаваемый с помощью функции createTheme. Важнейшей особенностью является возможность переключения между различными темами, например, светлой и тёмной.

Пример создания базовой темы:

import { createTheme } from '@mui/material/styles';

const lightTheme = createTheme({
  palette: {
    mode: 'light',
    primary: {
      main: '#1976d2',
    },
    secondary: {
      main: '#dc004e',
    },
  },
});

const darkTheme = createTheme({
  palette: {
    mode: 'dark',
    primary: {
      main: '#90caf9',
    },
    secondary: {
      main: '#f48fb1',
    },
  },
});

Использование ThemeProvider для внедрения темы

Для применения темы в приложении используется компонент ThemeProvider из MUI. Он оборачивает корневой компонент или любую часть дерева компонентов, передавая созданную тему через контекст.

import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import App from './App';

function Root({ theme }) {
  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <App />
    </ThemeProvider>
  );
}

CssBaseline автоматически синхронизирует базовые стили с текущей темой, включая фоновый цвет, цвет текста и типографику.

Переключение тем с использованием состояния

Для динамического переключения тем применяют React state. Обычно создаётся состояние, которое хранит текущий режим (light или dark) и изменяет его по событию пользователя, например, клику на кнопку.

import { useState } from 'react';
import { ThemeProvider, createTheme, Button } from '@mui/material';
import CssBaseline from '@mui/material/CssBaseline';
import App from './App';

function Root() {
  const [mode, setMode] = useState('light');

  const toggleTheme = () => {
    setMode(prevMode => (prevMode === 'light' ? 'dark' : 'light'));
  };

  const theme = createTheme({
    palette: {
      mode: mode,
      primary: {
        main: mode === 'light' ? '#1976d2' : '#90caf9',
      },
      secondary: {
        main: mode === 'light' ? '#dc004e' : '#f48fb1',
      },
    },
  });

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <Button onCl ick={toggleTheme}>
        Переключить тему
      </Button>
      <App />
    </ThemeProvider>
  );
}

Кастомизация палитры и компонентов

MUI позволяет не только менять режим (light/dark), но и тонко настраивать цвета отдельных компонентов через palette и components в объекте темы.

const customTheme = createTheme({
  palette: {
    mode: 'dark',
    primary: {
      main: '#ff9800',
    },
    background: {
      default: '#121212',
      paper: '#1d1d1d',
    },
  },
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          borderRadius: 12,
          textTransform: 'none',
        },
      },
    },
  },
});

Важно: переопределения в components работают для всех компонентов данного типа в пределах темы.

Оптимизация производительности при смене темы

Создание новой темы на каждый рендер может быть дорогим. Для оптимизации применяют useMemo, чтобы объект темы пересоздавался только при изменении режима.

import { useMemo } from 'react';

const theme = useMemo(() => createTheme({
  palette: { mode },
}), [mode]);

Это предотвращает ненужные перерасчёты стилей и повторные рендеры дочерних компонентов.

Хранение предпочтений пользователя

Для сохранения выбранной темы между сессиями часто используют localStorage:

useEffect(() => {
  const savedMode = localStorage.getItem('themeMode');
  if (savedMode) setMode(savedMode);
}, []);

useEffect(() => {
  localStorage.setItem('themeMode', mode);
}, [mode]);

Таким образом, приложение автоматически применяет последнюю выбранную тему при повторном открытии.

Сочетание с системными настройками

MUI позволяет учитывать системный режим через useMediaQuery:

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

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

const [mode, setMode] = useState(prefersDarkMode ? 'dark' : 'light');

Это обеспечивает синхронизацию темы приложения с настройками ОС пользователя.

Динамическая типографика и отступы

Переключение тем может включать не только цвета, но и размеры, шрифты и отступы через typography и spacing:

const theme = createTheme({
  palette: { mode },
  typography: {
    h1: { fontSize: mode === 'light' ? '2rem' : '2.2rem' },
  },
  spacing: factor => factor * (mode === 'light' ? 8 : 10),
});

Это позволяет создавать полностью адаптивный интерфейс, подстраивающийся под контекст темы.

Интеграция с компонентами MUI

Большинство компонентов MUI автоматически реагируют на изменение темы, включая Button, AppBar, Paper, TextField. Компоненты наследуют цвета, отступы и типографику из текущей темы. Для случаев, когда требуется более детальное управление стилями, используют sx и styled:

import { styled } from '@mui/material/styles';
import Button from '@mui/material/Button';

const CustomButton = styled(Button)(({ theme }) => ({
  backgroundColor: theme.palette.primary.main,
  color: theme.palette.getContrastText(theme.palette.primary.main),
  '&:hover': {
    backgroundColor: theme.palette.primary.dark,
  },
}));

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