# 主题系统 > **文档版本**:v1.1 > **最后更新**:2025-01-18 --- ## 目录 1. [技术方案](#1-技术方案) 2. [主题 Store](#2-主题-store) 3. [Theme Provider](#3-theme-provider) 4. [防止主题闪烁](#4-防止主题闪烁) 5. [主题切换组件](#5-主题切换组件) --- ## 1. 技术方案 使用 CSS 变量 + class 切换实现主题系统,支持深色、亮色和跟随系统。 --- ## 2. 主题 Store ```typescript // src/stores/themeStore.ts import { create } from "zustand"; import { persist } from "zustand/middleware"; type Theme = "dark" | "light" | "system"; type ResolvedTheme = "dark" | "light"; interface ThemeState { theme: Theme; resolvedTheme: ResolvedTheme; setTheme: (theme: Theme) => void; } // 获取系统主题偏好 const getSystemTheme = (): ResolvedTheme => { if (typeof window === "undefined") return "dark"; return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; }; // 应用主题到 DOM const applyTheme = (theme: ResolvedTheme) => { const root = document.documentElement; root.classList.remove("light", "dark"); root.classList.add(theme); }; export const useThemeStore = create()( persist( (set, get) => ({ theme: "dark", resolvedTheme: "dark", setTheme: (theme) => { const resolvedTheme = theme === "system" ? getSystemTheme() : theme; applyTheme(resolvedTheme); set({ theme, resolvedTheme }); }, }), { name: "theme-store", onRehydrateStorage: () => (state) => { if (state) { const resolvedTheme = state.theme === "system" ? getSystemTheme() : state.theme; applyTheme(resolvedTheme); state.resolvedTheme = resolvedTheme; } }, }, ), ); // 监听系统主题变化 if (typeof window !== "undefined") { window .matchMedia("(prefers-color-scheme: dark)") .addEventListener("change", (e) => { const { theme, setTheme } = useThemeStore.getState(); if (theme === "system") { setTheme("system"); // 重新计算 resolvedTheme } }); } ``` --- ## 3. Theme Provider ```tsx // src/app/providers/ThemeProvider.tsx import { useEffect } from "react"; import { useThemeStore } from "@/stores/themeStore"; interface ThemeProviderProps { children: React.ReactNode; defaultTheme?: "dark" | "light" | "system"; } export function ThemeProvider({ children, defaultTheme = "dark", }: ThemeProviderProps) { const { theme, setTheme } = useThemeStore(); // 初始化主题 useEffect(() => { // 防止首次加载时的闪烁 const savedTheme = localStorage.getItem("theme-store"); if (!savedTheme) { setTheme(defaultTheme); } }, []); // 监听系统主题变化 useEffect(() => { if (theme !== "system") return; const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); const handleChange = () => setTheme("system"); mediaQuery.addEventListener("change", handleChange); return () => mediaQuery.removeEventListener("change", handleChange); }, [theme, setTheme]); return <>{children}; } ``` --- ## 4. 防止主题闪烁 在 `index.html` 的 `` 中添加内联脚本: ```html ``` --- ## 5. 主题切换组件 ```tsx // src/components/common/ThemeToggle.tsx import { Moon, Sun, Monitor } from "lucide-react"; import { useThemeStore } from "@/stores/themeStore"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { useTranslation } from "react-i18next"; export function ThemeToggle() { const { t } = useTranslation(); const { theme, setTheme, resolvedTheme } = useThemeStore(); return ( setTheme("light")}> {t("settings.theme.light")} setTheme("dark")}> {t("settings.theme.dark")} setTheme("system")}> {t("settings.theme.system")} ); } ``` --- ## 6. Tailwind 深色模式配置 确保 `tailwind.config.js` 中配置了 class 策略: ```javascript // tailwind.config.js module.exports = { darkMode: ["class"], // 使用 class 策略 // ... }; ``` --- ## 相关文档 - [样式方案](./07-styling.md) - [状态管理](./04-state-management.md) - [开发规范](./13-dev-standards.md) --- **最后更新**:2025-01-18 | **版本**:v1.1