计算机网站建设与推广,单位网站建设流程,卓越科技建站无锡做网站,在网站上发消息做宣传第 5 章#xff1a;主题#xff08;Theme#xff09;系统 —— Light / Dark / System 三主题完整实现现代前端应用几乎都需要支持深色模式#xff08;Dark Mode#xff09;。但在企业级后台系统中#xff0c;主题模式不仅仅是“切换颜色”这么简单#xff1a;必须支持 …第 5 章主题Theme系统 —— Light / Dark / System 三主题完整实现现代前端应用几乎都需要支持深色模式Dark Mode。但在企业级后台系统中主题模式不仅仅是“切换颜色”这么简单必须支持Light / Dark / System三种模式必须与Tailwind 的 darkModeclass完整配合必须能在所有组件中生效包括 shadcn/ui必须能够被用户手动选择并持久保存必须支持系统主题跟随 prefers-color-scheme 必须兼容 SSR / CSR本项目是 CSR无需额外工作本章将带你从 0 构建一个完整的主题系统next-themes初始化全局主题上下文主题切换组件 ThemeSwitcher在 Tailwind 中实现自动 dark classshadcn 风格主题色生效用 Zustand 添加可选主题状态支持未来扩展将主题持久化到 localStorage完成后你的项目将具备媲美现代 SaaS 的主题能力。5.1 为什么选择 next-themesnext-themes 是当前业界最优秀的主题切换库特性说明支持 Light / Dark / System自动识别系统主题自动管理 HTML class与 Tailwind 完美组合可以存储用户选择localStorage 自动持久化与 shadcn/ui 原生兼容官方推荐组合零配置 / 零侵入只需要包裹 Provider5.2 在main.tsx 中集成ThemeProvider在main.tsx中添加import { ThemeProvider } from next-themes; ThemeProvider attributeclass App / /ThemeProvider完整的main.tsx代码import { ThemeProvider } from next-themes; import { StrictMode } from react; import { createRoot } from react-dom/client; import ./index.css; import App from ./App.tsx; import ./i18n; createRoot(document.getElementById(root)!).render( StrictMode ThemeProvider attributeclass App / /ThemeProvider /StrictMode, );attributeclass是关键会在html上添加classlightclassdarkTailwind 的dark:工具类依赖这个行为。5.3 创建主题切换组件ThemeSwitcher在src/components/ThemeSwitcher.tsx创建import { useTheme } from next-themes; const themes [ { value: light, label: Light }, { value: dark, label: Dark }, { value: system, label: System }, ]; export const ThemeSwitcher () { const { theme, setTheme } useTheme(); return ( select classNameborder px-2 py-1 rounded-md value{theme || system} onChange{(e) setTheme(e.target.value)} {themes.map((item) ( option key{item.value} value{item.value} {item.label} /option ))} /select ); };用ShadCn实现import { useTheme } from next-themes; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from /components/ui/select; const themes [ { value: light, label: Light }, { value: dark, label: Dark }, { value: system, label: System }, ]; export const ThemeSwitcher () { const { theme, setTheme } useTheme(); return ( Select value{theme || system} onValueChange{(value) setTheme(value)} SelectTrigger classNamew-[100px] SelectValue placeholderSelect theme / /SelectTrigger SelectContent {themes.map((item) ( SelectItem key{item.value} value{item.value} {item.label} /SelectItem ))} /SelectContent /Select ); };特点主题显示当前主题切换主题setTheme(light)自动写入 localStoragethemexxx与 Tailwind 的 dark class 自动联动5.4 在 App 中测试主题切换修改src/App.tsximport { ThemeSwitcher } from ./components/ThemeSwitcher; import { LanguageSwitcher } from ./components/LanguageSwitcher; export default function App() { return ( div classNamep-4 space-y-6 div classNameflex items-center space-x-4 LanguageSwitcher / ThemeSwitcher / /div h1 classNametext-2xl font-boldTheme System Ready/h1 div classNamep-4 rounded-md border bg-card text-card-foreground This box will change when you toggle theme. /div /div ); }App.tsx完整代码import { Search, User, Settings, Plus } from lucide-react; import { useTranslation } from react-i18next; import { Button } from /components/ui/button; import { LanguageSwitcher } from ./components/LanguageSwitcher; import { ThemeSwitcher } from ./components/ThemeSwitcher; function App() { const { t } useTranslation(); return ( div classNameflex min-h-screen flex-col items-center justify-center gap-4 space-x-3 p-4 divApp Initialized/div Button默认按钮/Button Button sizesm小/Button Button sizelg大/Button Button variantoutline描边按钮/Button IconDemo / CreateButton / LanguageSwitcher / h1 classNametext-2xl font-bold{t(dashboard.title)}/h1 p{t(dashboard.welcome, { name: 龙傲天 })}/p div{t(common.language)}/div ThemeSwitcher / h1 classNametext-2xl font-boldTheme System Ready/h1 div classNamerounded-md border bg-card p-4 text-card-foreground This box will change when you toggle theme. /div /div ); } function IconDemo() { return ( div classNameflex items-center gap-4 Search classNameh-5 w-5 text-muted-foreground / User classNameh-5 w-5 text-blue-500 / Settings classNameh-5 w-5 text-green-500 / /div ); } function CreateButton() { return ( Button Plus classNamemr-2 h-4 w-4 / 新建 /Button ); } export default App;运行pnpm dev你将看到一段文本随主题变色shadcn 组件卡片风格自动同 theme 变化5.5 Tailwind 的 darkMode核心原理解释我们在tailwind.config.cjs中设置darkMode: class为什么是class因为next-themes 会给 HTML 标签动态加.dark或.lightTailwind 会根据这个 class 去应用 dark 主题下的所有样式全局 CSS、组件、UI 元素都可自动响应例如使用div classNamebg-white dark:bg-black/div在 Light 模式下 → white在 Dark 模式下 → black不需要 JS 代码切换样式。这就是现代 dark mode 的最佳实践。5.6 主题持久化实现由 next-themes 自动处理next-themes 会自动在 localStorage 中维护themelight三种值modelocalStorage 值LightthemelightDarkthemedarkSystemlocalStorage 不存储或themesystem浏览器刷新 → 仍然保持上一次选择的主题。无需自己写 Zustand。5.7 为全局组件注入主题变量未来可扩展你可以在theme.css中加入更多主题变量src/styles/theme.css:root { --radius: 0.5rem; --primary: 240 5.9% 10%; --primary-foreground: 0 0% 98%; } .dark { --primary: 0 0% 98%; --primary-foreground: 240 5.9% 10%; }然后在 Tailwind 中使用div classNamebg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] 主题变量测试 /div高级主题自定义将在后面高级章节介绍。5.8 在 shadcn/ui 中主题自动生效因为 shadcn 使用 CSS Variables如 --background所有主题颜色会自动在 dark mode 中切换。例如div classNamebg-background text-foreground p-4 rounded-md shadcn color system working /div你会看到Light 模式浅色背景Dark 模式深色背景无需任何额外配置。5.9 和 i18n 一起工作你可以在 中放置header classNameflex items-center justify-between border-b p-4 LanguageSwitcher / ThemeSwitcher / /header这将成为一个多语言 多主题控制中心。5.10 常见问题与工程实践经验Q1为什么 UI 在加载时会闪烁因为主题还未从 localStorage 恢复。解决方法官方推荐在index.html添加script (function() { const theme localStorage.getItem(theme); if (theme dark) { document.documentElement.classList.add(dark); } })(); /scriptQ2如何让组件内部样式自动响应主题使用classNametext-foreground bg-backgroundshadcn 默认变量即可。Q3主题切换时如何过渡在tailwind.css添加html { transition: background-color 0.2s ease, color 0.2s ease; }5.11 本章小结本章我们完成了✔ 使用 next-themes 集成主题系统✔ 支持 Light / Dark / System 三种模式✔ 添加主题切换组件 ThemeSwitcher✔ Tailwind 的 darkMode 完整生效✔ shadcn/ui 风格自动适配主题✔ 主题持久化✔ UI 响应主题示例展示现在你的项目已经具备完整主题系统配合 Tailwind 与 shadcn 的现代化设计动态响应 CSS Variable 体系可扩展、可持久保存的主题体系