fix(site): 官网页头去掉深浅切换按钮(未挂 ThemeProvider 会运行时报错)
ThemeToggle 依赖 useTheme/ThemeProvider,但按'官网默认浅色'计划没搬那套 provider → useTheme must be used within ThemeProvider 运行时崩。删掉切换按钮 + 无用的 theme-provider.tsx。 顺带 index.html 换中性标题 + favicon 指向官网 logo。排查确认无其它 Context/Provider 依赖、 logo 路径 /brand/ 正确。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,34 +1,17 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Link, NavLink } from 'react-router-dom'
|
import { Link, NavLink } from 'react-router-dom'
|
||||||
import { Menu, Moon, Sun, X } from 'lucide-react'
|
import { Menu, X } from 'lucide-react'
|
||||||
import { useTheme } from '@/components/theme-provider'
|
|
||||||
import { LogoMark } from '@/components/logo'
|
import { LogoMark } from '@/components/logo'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
// 官网默认浅色,不搬深浅切换(ThemeProvider 未挂载)。
|
||||||
|
|
||||||
const NAV_ITEMS = [
|
const NAV_ITEMS = [
|
||||||
{ label: '产品', to: '/', end: true },
|
{ label: '产品', to: '/', end: true },
|
||||||
{ label: '功能', to: '/#features' },
|
{ label: '功能', to: '/#features' },
|
||||||
{ label: '架构', to: '/#architecture' },
|
{ label: '架构', to: '/#architecture' },
|
||||||
]
|
]
|
||||||
|
|
||||||
function ThemeToggle() {
|
|
||||||
const { resolved, setTheme } = useTheme()
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
aria-label={resolved === 'dark' ? '切换到浅色主题' : '切换到深色主题'}
|
|
||||||
onClick={() => setTheme(resolved === 'dark' ? 'light' : 'dark')}
|
|
||||||
className="flex size-8 items-center justify-center rounded-full border border-hairline-strong text-ink-2 transition-colors hover:border-accent hover:text-accent-ink"
|
|
||||||
>
|
|
||||||
{resolved === 'dark' ? (
|
|
||||||
<Sun className="size-4" />
|
|
||||||
) : (
|
|
||||||
<Moon className="size-4" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SiteHeader() {
|
export function SiteHeader() {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
@@ -70,7 +53,6 @@ export function SiteHeader() {
|
|||||||
|
|
||||||
<nav className="hidden items-center gap-7 md:flex">
|
<nav className="hidden items-center gap-7 md:flex">
|
||||||
{links}
|
{links}
|
||||||
<ThemeToggle />
|
|
||||||
<Link
|
<Link
|
||||||
to="/download"
|
to="/download"
|
||||||
className="rounded-[7px] bg-accent px-4 py-1.5 text-[13px] font-medium text-ground transition-opacity hover:opacity-90"
|
className="rounded-[7px] bg-accent px-4 py-1.5 text-[13px] font-medium text-ground transition-opacity hover:opacity-90"
|
||||||
@@ -80,7 +62,6 @@ export function SiteHeader() {
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="flex items-center gap-3 md:hidden">
|
<div className="flex items-center gap-3 md:hidden">
|
||||||
<ThemeToggle />
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={open ? '关闭菜单' : '打开菜单'}
|
aria-label={open ? '关闭菜单' : '打开菜单'}
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
import {
|
|
||||||
createContext,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
type ReactNode,
|
|
||||||
} from 'react'
|
|
||||||
|
|
||||||
type Theme = 'light' | 'dark' | 'system'
|
|
||||||
|
|
||||||
interface ThemeContextValue {
|
|
||||||
theme: Theme
|
|
||||||
resolved: 'light' | 'dark'
|
|
||||||
setTheme: (t: Theme) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const ThemeContext = createContext<ThemeContextValue | null>(null)
|
|
||||||
|
|
||||||
const STORAGE_KEY = 'sundynix-theme'
|
|
||||||
|
|
||||||
function systemTheme(): 'light' | 'dark' {
|
|
||||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
||||||
? 'dark'
|
|
||||||
: 'light'
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ThemeProvider({ children }: { children: ReactNode }) {
|
|
||||||
const [theme, setThemeState] = useState<Theme>(
|
|
||||||
() => (localStorage.getItem(STORAGE_KEY) as Theme) ?? 'system',
|
|
||||||
)
|
|
||||||
const [resolved, setResolved] = useState<'light' | 'dark'>(() =>
|
|
||||||
theme === 'system' ? systemTheme() : theme,
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const apply = () => {
|
|
||||||
const r = theme === 'system' ? systemTheme() : theme
|
|
||||||
setResolved(r)
|
|
||||||
document.documentElement.classList.toggle('dark', r === 'dark')
|
|
||||||
}
|
|
||||||
apply()
|
|
||||||
const mq = window.matchMedia('(prefers-color-scheme: dark)')
|
|
||||||
mq.addEventListener('change', apply)
|
|
||||||
return () => mq.removeEventListener('change', apply)
|
|
||||||
}, [theme])
|
|
||||||
|
|
||||||
const setTheme = (t: Theme) => {
|
|
||||||
localStorage.setItem(STORAGE_KEY, t)
|
|
||||||
setThemeState(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThemeContext.Provider value={{ theme, resolved, setTheme }}>
|
|
||||||
{children}
|
|
||||||
</ThemeContext.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useTheme() {
|
|
||||||
const ctx = useContext(ThemeContext)
|
|
||||||
if (!ctx) throw new Error('useTheme must be used within ThemeProvider')
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user