Темизация

MDX (Markdown for JSX) предоставляет гибкость работы с контентом, позволяя интегрировать React-компоненты прямо в Markdown. Одной из ключевых возможностей современных интерфейсов является темизация, которая позволяет адаптировать визуальное оформление под светлую, тёмную или кастомные темы. MDX не содержит встроенной системы темизации, поэтому её реализация обычно строится на сочетании React-контекста, CSS-in-JS и динамического выбора стилей.


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

Первым шагом является создание контекста темы. Контекст позволяет передавать текущую тему во все компоненты MDX без необходимости пробрасывать пропсы вручную.

import React, { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

export const useTheme = () => useContext(ThemeContext);

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

  • ThemeContext предоставляет глобальное состояние темы.
  • toggleTheme позволяет динамически переключать темы.
  • useTheme упрощает доступ к текущей теме в компонентах MDX.

Динамическое применение стилей

MDX-компоненты могут менять стили в зависимости от текущей темы. Для этого часто используют CSS-in-JS библиотеки, такие как styled-components или @emotion/react.

Пример с styled-components:

import styled from 'styled-components';
import { useTheme } from './ThemeProvider';

const ThemedButton = styled.button`
  background-color: ${({ theme }) => (theme === 'light' ? '#fff' : '#333')};
  color: ${({ theme }) => (theme === 'light' ? '#000' : '#fff')};
  border: 1px solid ${({ theme }) => (theme === 'light' ? '#ccc' : '#555')};
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
`;

export const Button = () => {
  const { theme, toggleTheme } = useTheme();
  return <ThemedButton theme={theme} onCl ick={toggleTheme}>Сменить тему</ThemedButton>;
};

Особенности подхода:

  • Стили реагируют на текущее состояние темы через проп theme.
  • Поддержка динамических изменений происходит без перезагрузки страницы.
  • Этот метод легко масштабируется для сложных интерфейсов.

Интеграция темы с MDX

MDX позволяет использовать React-компоненты внутри Markdown. Это позволяет добавлять тематизированные элементы прямо в контент:

# Пример тематизированной кнопки

<Button />

При рендеринге компонента Button будет автоматически применяться текущая тема из контекста. Это означает, что одна и та же MDX-страница может корректно отображаться в светлой и тёмной теме, без необходимости дублировать контент.


Темизация для глобальных стилей

Для более масштабной темизации важно учитывать глобальные CSS-переменные. С их помощью можно менять цвета всего интерфейса в зависимости от темы:

import { createGlobalStyle } from 'styled-components';
import { useTheme } from './ThemeProvider';

const GlobalStyle = createGlobalStyle`
  body {
    background-color: ${({ theme }) => (theme === 'light' ? '#f9f9f9' : '#121212')};
    color: ${({ theme }) => (theme === 'light' ? '#000' : '#fff')};
    transition: background-color 0.3s, color 0.3s;
  }
`;

export const ThemedLayout = ({ children }) => {
  const { theme } = useTheme();
  return (
    <>
      <GlobalStyle theme={theme} />
      {children}
    </>
  );
};

Важные моменты:

  • transition обеспечивает плавное переключение темы.
  • Глобальные стили применяются ко всему контенту MDX.
  • Поддерживается масштабирование для сложных сайтов и документаций.

Темизация с использованием MDXProvider

MDXProvider позволяет замещать стандартные элементы Markdown на кастомные React-компоненты, поддерживающие тему:

import { MDXProvider } from '@mdx-js/react';
import { ThemedHeading, ThemedParagraph } from './ThemedComponents';

const components = {
  h1: ThemedHeading,
  p: ThemedParagraph,
};

export const ThemedMDX = ({ children }) => {
  return <MDXProvider components={components}>{children}</MDXProvider>;
};

Преимущества:

  • Позволяет централизованно управлять стилями всех заголовков, параграфов и других элементов.
  • Все компоненты автоматически получают контекст темы.
  • Обеспечивает единый визуальный стиль для MDX-документов.

Настройка кастомных тем

Можно определить несколько кастомных тем и переключаться между ними динамически:

const themes = {
  light: { background: '#fff', text: '#000', accent: '#0070f3' },
  dark: { background: '#121212', text: '#fff', accent: '#1db954' },
  solarized: { background: '#fdf6e3', text: '#657b83', accent: '#b58900' },
};

export const ThemeProvider = ({ children }) => {
  const [themeName, setThemeName] = useState('light');

  const toggleTheme = (newTheme) => setThemeName(newTheme);

  const theme = themes[themeName];

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

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


Итоговая архитектура

Для полной темизации MDX-проекта обычно используется следующая архитектура:

  1. ThemeProvider — хранит состояние темы.
  2. useTheme — хук для доступа к текущей теме.
  3. ThemedComponents — компоненты с динамическими стилями.
  4. GlobalStyle / CSS-переменные — управление глобальными стилями.
  5. MDXProvider — замена стандартных элементов на тематизированные компоненты.
  6. Динамическое переключение — возможность менять тему без перезагрузки страницы.

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