Реалізація Theme Provider для динамічної зміни тем

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація Theme Provider для динамічної зміни тем
Середня
від 1 робочого дня до 3 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація Theme Provider для динамічної смени тем

Тема сайту — це набір дизайн-токенів: кольори, типографіка, відступи, радіусивай тіні. Theme Provider — механізм, який робить ці токени доступними для всіх компонентів та дозволяє переключатися між ними без перезавантаження сторінки.

Два підходи: CSS Custom Properties (нативні CSS-змінні) або Context + CSS-in-JS. Перший — швидший, простіший, незалежний від фреймворку. Другий — більше гнучкості для динамічних токенів.

Підхід на CSS Custom Properties

Тема живе в CSS, JavaScript тільки переключає клас або атрибут на <html>:

/* themes.css */
:root,
[data-theme='light'] {
  --color-bg: #ffffff;
  --color-text: #0f172a;
  --color-primary: #2563eb;
  --radius-md: 8px;
  --font-sans: 'Inter', system-ui, sans-serif;
}

[data-theme='dark'] {
  --color-bg: #0f172a;
  --color-text: #f1f5f9;
  --color-primary: #3b82f6;
}

ThemeProvider

type ThemeId = 'light' | 'dark' | 'system'

interface ThemeContextValue {
  theme: ThemeId
  resolvedTheme: 'light' | 'dark'
  setTheme: (theme: ThemeId) => void
  themes: ThemeId[]
}

const ThemeContext = createContext<ThemeContextValue | null>(null)

const STORAGE_KEY = 'app-theme'
const THEMES: ThemeId[] = ['system', 'light', 'dark']

function getSystemTheme(): 'light' | 'dark' {
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}

function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setThemeState] = useState<ThemeId>(() => {
    if (typeof window === 'undefined') return 'system'
    return (localStorage.getItem(STORAGE_KEY) as ThemeId) ?? 'system'
  })

  const resolvedTheme = useMemo<'light' | 'dark'>(() => {
    if (theme === 'system') return getSystemTheme()
    return theme as 'light' | 'dark'
  }, [theme])

  // Застосовуємо тему до <html>
  useEffect(() => {
    const root = document.documentElement
    root.setAttribute('data-theme', resolvedTheme)

    // Колір строки браузера (Chrome Mobile)
    const metaThemeColor = document.querySelector('meta[name="theme-color"]')
    const colors: Record<string, string> = {
      light: '#ffffff',
      dark: '#0f172a',
    }
    metaThemeColor?.setAttribute('content', colors[resolvedTheme])
  }, [resolvedTheme])

  const setTheme = (newTheme: ThemeId) => {
    setThemeState(newTheme)
    localStorage.setItem(STORAGE_KEY, newTheme)
  }

  return (
    <ThemeContext.Provider value={{ theme, resolvedTheme, setTheme, themes: THEMES }}>
      {children}
    </ThemeContext.Provider>
  )
}

export const useTheme = () => {
  const context = useContext(ThemeContext)
  if (!context) throw new Error('useTheme must be used within ThemeProvider')
  return context
}

Використання в компонентах

function ThemeSwitcher() {
  const { theme, themes, setTheme } = useTheme()

  return (
    <select value={theme} onChange={(e) => setTheme(e.target.value as ThemeId)}>
      {themes.map((t) => (
        <option key={t} value={t}>{t}</option>
      ))}
    </select>
  )
}