1 Commits

Author SHA1 Message Date
sanjidarimi 370f93e1f7 refactor(theme):valid localstroage and organized theme codes 2026-06-17 21:25:44 +06:00
4 changed files with 94 additions and 62 deletions
+3 -62
View File
@@ -1,63 +1,4 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
import { ThemeContextType } from "@/types/theme.interface";
import { createContext } from "react";
type ThemeMode = 'light' | 'dark';
type AccentTheme = 'blue' | 'purple' | 'green';
interface ThemeContextType {
mode: ThemeMode;
accent: AccentTheme;
setMode: (mode: ThemeMode) => void;
setAccent: (accent: AccentTheme) => void;
toggleMode: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [mode, setModeState] = useState<ThemeMode>('dark');
const [accent, setAccentState] = useState<AccentTheme>('blue');
// Initialize theme from localStorage
useEffect(() => {
const savedMode = localStorage.getItem('techzaa-mode') as ThemeMode;
const savedAccent = localStorage.getItem('techzaa-accent') as AccentTheme;
if (savedMode) setModeState(savedMode);
if (savedAccent) setAccentState(savedAccent);
}, []);
// Apply theme classes to document
useEffect(() => {
const root = document.documentElement;
// Apply dark/light mode
root.classList.remove('light', 'dark');
root.classList.add(mode);
// Apply accent theme
root.classList.remove('theme-blue', 'theme-purple', 'theme-green');
root.classList.add(`theme-${accent}`);
// Save to localStorage
localStorage.setItem('techzaa-mode', mode);
localStorage.setItem('techzaa-accent', accent);
}, [mode, accent]);
const setMode = (newMode: ThemeMode) => setModeState(newMode);
const setAccent = (newAccent: AccentTheme) => setAccentState(newAccent);
const toggleMode = () => setModeState(prev => prev === 'dark' ? 'light' : 'dark');
return (
<ThemeContext.Provider value={{ mode, accent, setMode, setAccent, toggleMode }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
export const ThemeContext = createContext<ThemeContextType | null>(null);
+15
View File
@@ -0,0 +1,15 @@
import { ThemeContext } from "@/contexts/ThemeContext";
import { ThemeContextType } from "@/types/theme.interface";
import { useContext } from "react";
export function useTheme(): ThemeContextType {
const context = useContext(ThemeContext);
if (!context) {
throw new Error(
"useTheme must be used within a ThemeProvider"
);
}
return context;
}
+66
View File
@@ -0,0 +1,66 @@
import { ThemeContext } from "@/contexts/ThemeContext";
import { ThemeMode, AccentTheme } from "@/types/theme.interface";
import { useState, useEffect, useCallback, useMemo } from "react";
const STORAGE_KEYS = {
mode: "techzaa-mode",
accent: "techzaa-accent",
} as const;
const isValidMode = (value: unknown): value is ThemeMode =>
value === "light" || value === "dark";
const isValidAccent = (value: unknown): value is AccentTheme =>
value === "blue" || value === "purple" || value === "green";
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [mode, setMode] = useState<ThemeMode>(() => {
if (typeof window === "undefined") return "dark";
const stored = localStorage.getItem(STORAGE_KEYS.mode);
return isValidMode(stored) ? stored : "dark";
});
const [accent, setAccent] = useState<AccentTheme>(() => {
if (typeof window === "undefined") return "blue";
const stored = localStorage.getItem(STORAGE_KEYS.accent);
return isValidAccent(stored) ? stored : "blue";
});
useEffect(() => {
if (typeof document === "undefined") return;
const root = document.documentElement;
root.classList.remove("light", "dark");
root.classList.add(mode);
root.classList.remove("theme-blue", "theme-purple", "theme-green");
root.classList.add(`theme-${accent}`);
localStorage.setItem(STORAGE_KEYS.mode, mode);
localStorage.setItem(STORAGE_KEYS.accent, accent);
}, [mode, accent]);
const toggleMode = useCallback(() => {
setMode((prev) => (prev === "dark" ? "light" : "dark"));
}, []);
const value = useMemo(
() => ({
mode,
accent,
setMode,
setAccent,
toggleMode,
}),
[mode, accent, toggleMode],
);
return (
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
);
}
+10
View File
@@ -0,0 +1,10 @@
export type ThemeMode = "light" | "dark";
export type AccentTheme = "blue" | "purple" | "green";
export interface ThemeContextType {
mode: ThemeMode;
accent: AccentTheme;
setMode: React.Dispatch<React.SetStateAction<ThemeMode>>;
setAccent: React.Dispatch<React.SetStateAction<AccentTheme>>;
toggleMode: () => void;
}