为你的 Remix 应用添加暗黑模式。
暗黑模式
修改你的 tailwind.css 文件
将 :root[class~="dark"] 添加到你的 tailwind.css 文件中。这将允许你在 HTML 元素上使用 dark 类来应用暗黑模式样式。
app/tailwind.css
.dark,:root[class~="dark"] {...;}
安装 remix-themes
首先安装 remix-themes:
pnpm npmyarn bun
pnpm add remix-themes
创建会话存储和主题会话解析器
app/sessions.server.tsx
import { createThemeSessionResolver } from "remix-themes"// 如果未设置 process.env.NODE_ENV,可以默认为 'development'const isProduction = process.env.NODE_ENV === "production"const sessionStorage = createCookieSessionStorage({cookie: {name: "theme",path: "/",httpOnly: true,sameSite: "lax",secrets: ["s3cr3t"],// 只有在生产环境中设置 domain 和 secure...(isProduction? { domain: "your-production-domain.com", secure: true }: {}),},})export const themeSessionResolver = createThemeSessionResolver(sessionStorage)
设置 Remix 主题
将 ThemeProvider 添加到你的根布局中。
app/root.tsx
import clsx from "clsx"import { PreventFlashOnWrongTheme, ThemeProvider, useTheme } from "remix-themes"import { themeSessionResolver } from "./sessions.server"// 使用 loader 从会话存储中返回主题export async function loader({ request }: LoaderFunctionArgs) {const { getTheme } = await themeSessionResolver(request)return {theme: getTheme(),}}// 用 ThemeProvider 包裹你的应用。// `specifiedTheme` 是存储在会话存储中的主题。// `themeAction` 是用于在会话存储中更改主题的操作名称。export default function AppWithProviders() {const data = useLoaderData<typeof loader>()return (<ThemeProvider specifiedTheme={data.theme} themeAction="/action/set-theme"><App /></ThemeProvider>)}export function App() {const data = useLoaderData<typeof loader>()const [theme] = useTheme()return (<html lang="en" className={clsx(theme)}><head><meta charSet="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><Meta /><PreventFlashOnWrongTheme ssrTheme={Boolean(data.theme)} /><Links /></head><body><Outlet /><ScrollRestoration /><Scripts /><LiveReload /></body></html>)}
添加操作路由
在 /routes/action.set-theme.ts 创建一个文件。确保将文件名传递给 ThemeProvider 组件。此路由用于在用户更改主题时将首选主题存储到会话存储中。
app/routes/action.set-theme.ts
import { createThemeAction } from "remix-themes"import { themeSessionResolver } from "./sessions.server"export const action = createThemeAction(themeSessionResolver)
添加模式切换
在你的站点上添加一个模式切换按钮,以在光明模式和暗黑模式之间切换。
components/mode-toggle.tsx
import { Moon, Sun } from "lucide-react"import { Theme, useTheme } from "remix-themes"import { Button } from "./ui/button"import {DropdownMenu,DropdownMenuContent,DropdownMenuItem,DropdownMenuTrigger,} from "./ui/dropdown-menu"export function ModeToggle() {const [, setTheme] = useTheme()return (<DropdownMenu><DropdownMenuTrigger asChild><Button variant="ghost" size="icon"><Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" /><Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" /><span className="sr-only">切换主题</span></Button></DropdownMenuTrigger><DropdownMenuContent align="end"><DropdownMenuItem onClick={() => setTheme(Theme.LIGHT)}>光明模式</DropdownMenuItem><DropdownMenuItem onClick={() => setTheme(Theme.DARK)}>暗黑模式</DropdownMenuItem></DropdownMenuContent></DropdownMenu>)}
