Components
- 手风琴
- 提示
- 警告对话框
- 宽高比
- 头像
- 徽章
- 面包屑导航
- 按钮
- 按钮组
- 日历 Calendar
- 卡片
- Carousel
- 图表 Chart
- 复选框
- 折叠面板
- 组合框
- 命令
- 上下文菜单
- 数据表格 Data Table
- 日期选择器 Date Picker
- 对话框 Dialog
- 抽屉
- 下拉菜单
- Empty
- 字段
- 悬停卡片
- 输入
- 输入组
- 输入 OTP
- 项目
- Kbd
- 标签
- 菜单栏
- 原生选择框
- 导航菜单 Navigation Menu
- 分页
- 弹出框
- 进度 Progress
- 单选框组
- 可调整大小
- 滚动区域 Scroll Area
- 选择框
- 分隔符 Separator
- 侧边栏 Sheet
- 侧边栏 Sidebar
- 骨架屏
- 滑块
- Sonner
- 加载指示器 Spinner
- 开关
- 表格
- 标签页 Tabs
- 文本域
- 吐司
- 切换按钮 Toggle
- 切换组
- 提示 Tooltip
- 排版
修改你的 tailwind.css 文件
在你的 tailwind.css 文件中添加 :root[class~="dark"]。这将允许你在 html 元素上使用 dark 类来应用暗模式样式。
.dark,
:root[class~="dark"] {
...;
}安装 remix-themes
首先安装 remix-themes:
pnpm add remix-themes
创建会话存储和主题会话解析器
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 添加到你的根布局中。
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>
)
}添加一个 action 路由
在 /routes/action.set-theme.ts 中创建一个文件。确保你将文件名传递给 ThemeProvider 组件。该路由用于在用户更改主题时,将首选主题存储到会话存储中。
import { createThemeAction } from "remix-themes"
import { themeSessionResolver } from "./sessions.server"
export const action = createThemeAction(themeSessionResolver)添加模式切换按钮
在你的网站上放置一个模式切换按钮,以在浅色和深色模式之间切换。
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] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
<span className="sr-only">切换主题</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme(Theme.LIGHT)}>
浅色
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme(Theme.DARK)}>
深色
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}