diff --git a/dashboard/src/app/AppProviders.tsx b/dashboard/src/app/AppProviders.tsx
index ccd872e..8e1fc9f 100644
--- a/dashboard/src/app/AppProviders.tsx
+++ b/dashboard/src/app/AppProviders.tsx
@@ -2,16 +2,16 @@
import type { ReactNode } from 'react'
+import { ThemeProvider } from '@components/theme'
import { LanguageProvider } from '@i18n/LanguageProvider'
import { UserProvider } from '@lib/userStore'
-import { ThemeProvider } from '@components/theme'
export function AppProviders({ children }: { children: ReactNode }) {
return (
-
-
- {children}
-
-
+
+
+ {children}
+
+
)
}
diff --git a/dashboard/src/app/demo/theme/page.tsx b/dashboard/src/app/demo/theme/page.tsx
index a67dc81..035bf6f 100644
--- a/dashboard/src/app/demo/theme/page.tsx
+++ b/dashboard/src/app/demo/theme/page.tsx
@@ -17,7 +17,7 @@ const COLOR_TOKENS: Array<{ key: keyof ThemeTokens['colors']; label: string }> =
]
export default function ThemeShowcasePage() {
- const { theme, tokens } = useTheme()
+ const { resolvedTheme, tokens } = useTheme()
const { colors } = tokens
return (
@@ -26,7 +26,7 @@ export default function ThemeShowcasePage() {
Theme system
主题演示与调试
- 当前主题:{theme}。使用下方卡片可以即时切换主题,并查看核心设计 token 的取值情况,帮助校验多主题下的界面一致性。
+ 当前主题:{resolvedTheme}。使用下方卡片可以即时切换主题,并查看核心设计 token 的取值情况,帮助校验多主题下的界面一致性。
diff --git a/dashboard/src/components/Footer.tsx b/dashboard/src/components/Footer.tsx
index 6e0d62e..672eafb 100644
--- a/dashboard/src/components/Footer.tsx
+++ b/dashboard/src/components/Footer.tsx
@@ -1,10 +1,11 @@
'use client'
import { BookOpen, Github, Globe, Link, MessageCircle, Sparkles, Moon, Sun } from 'lucide-react'
-import { useTheme } from '@theme/useTheme'
+import { useThemeStore } from '@components/theme'
export default function Footer() {
- const { isDark, toggleTheme } = useTheme()
+ const isDark = useThemeStore((state) => state.isDark)
+ const toggleTheme = useThemeStore((state) => state.toggleTheme)
const socials = [
{ label: 'GitHub', icon: Github, href: 'https://github.com/CloudNativeSuite/' },
{ label: 'Docs', icon: BookOpen, href: '#' },
diff --git a/dashboard/src/components/theme/ThemeProvider.tsx b/dashboard/src/components/theme/ThemeProvider.tsx
index f2e1beb..2c2a856 100644
--- a/dashboard/src/components/theme/ThemeProvider.tsx
+++ b/dashboard/src/components/theme/ThemeProvider.tsx
@@ -1,21 +1,9 @@
'use client'
-import { createContext, useContext, useEffect, useMemo } from 'react'
+import { useEffect } from 'react'
import { getThemeDefinition, useThemeStore } from './store'
-import type { ThemeName, ThemePreference, ThemeTokens } from './types'
-
-interface ThemeContextValue {
- theme: ThemeName
- preference: ThemePreference
- tokens: ThemeTokens
- colorScheme: 'light' | 'dark'
- availableThemes: ThemeName[]
- setPreference: (preference: ThemePreference) => void
- toggleTheme: () => void
-}
-
-const ThemeContext = createContext(undefined)
+import type { ThemeName, ThemePreference } from './types'
function applyTheme(theme: ThemeName) {
if (typeof document === 'undefined') {
@@ -26,6 +14,7 @@ function applyTheme(theme: ThemeName) {
const root = document.documentElement
root.dataset.theme = definition.name
root.style.setProperty('color-scheme', definition.colorScheme)
+ root.classList.toggle('dark', definition.colorScheme === 'dark')
const body = document.body
if (body) {
@@ -54,13 +43,7 @@ export interface ThemeProviderProps {
}
export function ThemeProvider({ children, initialPreference = 'system' }: ThemeProviderProps) {
- const theme = useThemeStore((state) => state.theme)
- const preference = useThemeStore((state) => state.preference)
- const tokens = useThemeStore((state) => state.tokens)
- const colorScheme = useThemeStore((state) => state.colorScheme)
- const availableThemes = useThemeStore((state) => state.availableThemes)
- const setPreference = useThemeStore((state) => state.setPreference)
- const toggleTheme = useThemeStore((state) => state.toggleTheme)
+ const resolvedTheme = useThemeStore((state) => state.resolvedTheme)
const hydrate = useThemeStore((state) => state.hydrate)
const setSystemTheme = useThemeStore((state) => state.setSystemTheme)
@@ -69,8 +52,8 @@ export function ThemeProvider({ children, initialPreference = 'system' }: ThemeP
}, [hydrate, initialPreference])
useEffect(() => {
- applyTheme(theme)
- }, [theme])
+ applyTheme(resolvedTheme)
+ }, [resolvedTheme])
useEffect(() => {
if (typeof window === 'undefined') {
@@ -88,26 +71,5 @@ export function ThemeProvider({ children, initialPreference = 'system' }: ThemeP
}
}, [setSystemTheme])
- const value = useMemo(
- () => ({
- theme,
- preference,
- tokens,
- colorScheme,
- availableThemes,
- setPreference,
- toggleTheme,
- }),
- [availableThemes, colorScheme, preference, setPreference, theme, tokens, toggleTheme],
- )
-
- return {children}
-}
-
-export function useThemeContext() {
- const context = useContext(ThemeContext)
- if (!context) {
- throw new Error('useThemeContext must be used within a ThemeProvider')
- }
- return context
+ return <>{children}>
}
diff --git a/dashboard/src/components/theme/index.ts b/dashboard/src/components/theme/index.ts
index b24772e..1bee39e 100644
--- a/dashboard/src/components/theme/index.ts
+++ b/dashboard/src/components/theme/index.ts
@@ -1,5 +1,6 @@
-export { ThemeProvider, useThemeContext } from './ThemeProvider'
+export { ThemeProvider } from './ThemeProvider'
export { useTheme } from './useTheme'
+export { useThemeStore } from './store'
export { lightTheme } from './light'
export { darkTheme } from './dark'
export type { ThemeDefinition, ThemeName, ThemePreference, ThemeTokens } from './types'
diff --git a/dashboard/src/components/theme/store.ts b/dashboard/src/components/theme/store.ts
index 14d344c..03249a8 100644
--- a/dashboard/src/components/theme/store.ts
+++ b/dashboard/src/components/theme/store.ts
@@ -47,28 +47,30 @@ function persistPreference(preference: ThemePreference) {
window.localStorage.setItem(STORAGE_KEY, preference)
}
-function buildThemeState(preference: ThemePreference, theme: ThemeName): ThemeState {
- const definition = themeRegistry[theme]
+function buildThemeState(themePreference: ThemePreference, resolvedTheme: ThemeName): ThemeState {
+ const definition = themeRegistry[resolvedTheme]
return {
- preference,
- theme,
+ theme: themePreference,
+ resolvedTheme,
tokens: definition.tokens,
colorScheme: definition.colorScheme,
availableThemes,
+ isDark: resolvedTheme === 'dark',
}
}
export type ThemeState = {
- preference: ThemePreference
- theme: ThemeName
+ theme: ThemePreference
+ resolvedTheme: ThemeName
tokens: ThemeTokens
colorScheme: 'light' | 'dark'
availableThemes: ThemeName[]
+ isDark: boolean
}
export type ThemeActions = {
hydrate: (initialPreference: ThemePreference) => void
- setPreference: (preference: ThemePreference) => void
+ setTheme: (theme: ThemePreference) => void
toggleTheme: () => void
setSystemTheme: (theme: ThemeName) => void
}
@@ -80,21 +82,22 @@ export const useThemeStore = create((set, get) => ({
const resolvedTheme = preference === 'system' ? resolveSystemTheme() : preference
set(buildThemeState(preference, resolvedTheme))
},
- setPreference: (preference) => {
+ setTheme: (preference) => {
const resolvedTheme = preference === 'system' ? resolveSystemTheme() : preference
persistPreference(preference)
set(buildThemeState(preference, resolvedTheme))
},
toggleTheme: () => {
- const nextTheme = get().theme === 'light' ? 'dark' : 'light'
- get().setPreference(nextTheme)
+ const { resolvedTheme, setTheme, theme } = get()
+ const nextTheme = resolvedTheme === 'dark' ? 'light' : 'dark'
+ setTheme(theme === 'system' ? nextTheme : (theme === 'dark' ? 'light' : 'dark'))
},
- setSystemTheme: (theme) => {
+ setSystemTheme: (resolvedTheme) => {
set((state) => {
- if (state.preference !== 'system') {
+ if (state.theme !== 'system') {
return state
}
- return buildThemeState('system', theme)
+ return buildThemeState('system', resolvedTheme)
})
},
}))
diff --git a/dashboard/src/components/theme/useTheme.ts b/dashboard/src/components/theme/useTheme.ts
index b82d98d..a4fd3d3 100644
--- a/dashboard/src/components/theme/useTheme.ts
+++ b/dashboard/src/components/theme/useTheme.ts
@@ -1,6 +1,4 @@
-import { useMemo } from 'react'
-
-import { useThemeContext } from './ThemeProvider'
+import { useThemeStore } from './store'
import type { ThemeName, ThemePreference, ThemeTokens } from './types'
export interface UseThemeResult {
@@ -9,18 +7,30 @@ export interface UseThemeResult {
tokens: ThemeTokens
colorScheme: 'light' | 'dark'
availableThemes: ThemeName[]
- setPreference: (preference: ThemePreference) => void
+ setTheme: (preference: ThemePreference) => void
toggleTheme: () => void
isDark: boolean
+ resolvedTheme: ThemeName
}
export function useTheme(): UseThemeResult {
- const context = useThemeContext()
- return useMemo(
- () => ({
- ...context,
- isDark: context.theme === 'dark',
- }),
- [context],
- )
+ const theme = useThemeStore((state) => state.theme)
+ const resolvedTheme = useThemeStore((state) => state.resolvedTheme)
+ const tokens = useThemeStore((state) => state.tokens)
+ const colorScheme = useThemeStore((state) => state.colorScheme)
+ const availableThemes = useThemeStore((state) => state.availableThemes)
+ const setTheme = useThemeStore((state) => state.setTheme)
+ const toggleTheme = useThemeStore((state) => state.toggleTheme)
+ const isDark = useThemeStore((state) => state.isDark)
+
+ return {
+ theme,
+ resolvedTheme,
+ tokens,
+ colorScheme,
+ availableThemes,
+ setTheme,
+ toggleTheme,
+ isDark,
+ }
}
diff --git a/dashboard/src/modules/extensions/builtin/user-center/account/ThemePreferenceCard.tsx b/dashboard/src/modules/extensions/builtin/user-center/account/ThemePreferenceCard.tsx
index a02348c..ff11bad 100644
--- a/dashboard/src/modules/extensions/builtin/user-center/account/ThemePreferenceCard.tsx
+++ b/dashboard/src/modules/extensions/builtin/user-center/account/ThemePreferenceCard.tsx
@@ -3,7 +3,7 @@
import { useMemo } from 'react'
import Card from '../components/Card'
-import { darkTheme, lightTheme, useTheme } from '@components/theme'
+import { darkTheme, lightTheme, useThemeStore } from '@components/theme'
import type { ThemeDefinition, ThemeName, ThemePreference } from '@components/theme'
const THEME_OPTIONS: Array<{
@@ -111,7 +111,9 @@ function ThemePreview({ definition, active }: { definition: ThemeDefinition; act
}
export default function ThemePreferenceCard() {
- const { preference, theme, setPreference } = useTheme()
+ const preference = useThemeStore((state) => state.theme)
+ const theme = useThemeStore((state) => state.resolvedTheme)
+ const setTheme = useThemeStore((state) => state.setTheme)
const normalizedPreference = useMemo(() => preference ?? 'system', [preference])
@@ -133,7 +135,7 @@ export default function ThemePreferenceCard() {