From a1d98bd255f6488e329d186dea1ddc7310c85be3 Mon Sep 17 00:00:00 2001 From: Hermann_Kitio Date: Fri, 17 Apr 2026 23:55:41 +0300 Subject: [PATCH] =?UTF-8?q?feat(design-system):=20ThemeProvider=20+=20useT?= =?UTF-8?q?heme=20=E2=80=94=20toggle=20dark/light=20(Sprint=200.5=20=C3=A9?= =?UTF-8?q?tape=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/providers.tsx | 35 ++++++++++++++++++++++++++++++----- src/shared/hooks/useTheme.ts | 8 ++++++++ src/shared/lib/theme.ts | 26 ++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 src/shared/hooks/useTheme.ts create mode 100644 src/shared/lib/theme.ts diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 1a6feff..915515d 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -1,14 +1,39 @@ +import { useState, useEffect } from 'react' import { QueryClientProvider } from '@tanstack/react-query' import { BrowserRouter } from 'react-router-dom' import { queryClient } from '@/shared/lib/query-client' +import { + ThemeContext, + type Theme, + getInitialTheme, + applyTheme, + persistTheme, +} from '@/shared/lib/theme' import { AppRouter } from './router' +function ThemeProvider({ children }: { children: React.ReactNode }) { + const [theme, setThemeState] = useState(getInitialTheme) + + useEffect(() => { + applyTheme(theme) + persistTheme(theme) + }, [theme]) + + function setTheme(t: Theme) { + setThemeState(t) + } + + return {children} +} + export function Providers() { return ( - - - - - + + + + + + + ) } diff --git a/src/shared/hooks/useTheme.ts b/src/shared/hooks/useTheme.ts new file mode 100644 index 0000000..45a8206 --- /dev/null +++ b/src/shared/hooks/useTheme.ts @@ -0,0 +1,8 @@ +import { useContext } from 'react' +import { ThemeContext } from '@/shared/lib/theme' + +export function useTheme() { + const ctx = useContext(ThemeContext) + if (!ctx) throw new Error('useTheme must be used within ThemeProvider') + return ctx +} diff --git a/src/shared/lib/theme.ts b/src/shared/lib/theme.ts new file mode 100644 index 0000000..aedd752 --- /dev/null +++ b/src/shared/lib/theme.ts @@ -0,0 +1,26 @@ +import { createContext } from 'react' + +export type Theme = 'light' | 'dark' + +export interface ThemeContextValue { + theme: Theme + setTheme: (t: Theme) => void +} + +export const ThemeContext = createContext(null) + +const STORAGE_KEY = 'expria-theme' + +export function getInitialTheme(): Theme { + const stored = localStorage.getItem(STORAGE_KEY) + if (stored === 'light' || stored === 'dark') return stored + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' +} + +export function applyTheme(theme: Theme): void { + document.documentElement.classList.toggle('dark', theme === 'dark') +} + +export function persistTheme(theme: Theme): void { + localStorage.setItem(STORAGE_KEY, theme) +}