主题系统
自定义网站外观和颜色主题
主题系统
Sistine Starter 提供强大的主题系统,支持多种颜色方案和深色/浅色模式切换。
核心特性
✅ 深色/浅色模式自动切换 ✅ 5 种内置主题颜色 ✅ 完整的 Tailwind CSS 集成 ✅ 用户偏好持久化 ✅ 系统级主题检测
主题架构
1. CSS 变量系统
位置: app/globals.css
所有颜色都通过 CSS 变量定义,支持动态切换:
:root {
/* 背景和前景 */
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
/* 卡片 */
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
/* 主色调(按钮、链接等) */
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
/* 次要色调 */
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
/* 其他颜色 */
--muted: 0 0% 89.5%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.5%;
--input: 0 0% 89.5%;
--ring: 0 0% 3.9%;
--navbar: 0 0% 100%;
--hover: 0 0% 96.1%;
}
/* 深色模式 */
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 8.8%;
--card-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
/* ... 所有颜色反转 ... */
}
2. Tailwind 配置
位置: tailwind.config.ts
import type { Config } from "tailwindcss";
export default {
darkMode: "class", // 使用类选择器模式
theme: {
extend: {
colors: {
border: "hsl(var(--border) / <alpha-value>)",
background: "hsl(var(--background) / <alpha-value>)",
foreground: "hsl(var(--foreground) / <alpha-value>)",
card: {
DEFAULT: "hsl(var(--card) / <alpha-value>)",
foreground: "hsl(var(--card-foreground) / <alpha-value>)",
},
primary: {
DEFAULT: "hsl(var(--primary) / <alpha-value>)",
foreground: "hsl(var(--primary-foreground) / <alpha-value>)",
},
// ... 其他颜色
},
},
},
plugins: [],
} satisfies Config;
3. Next.js Themes 集成
位置: app/layout.tsx
import { ThemeProvider } from "next-themes";
export default function RootLayout({ children }) {
return (
<html>
<body>
<ThemeProvider
attribute="class"
defaultTheme="light"
enableSystem
enableColorScheme
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
);
}
配置说明:
attribute="class"
- 通过 HTML class 切换主题defaultTheme="light"
- 默认浅色主题enableSystem
- 检测系统偏好enableColorScheme
- 支持 color-scheme CSS 属性disableTransitionOnChange
- 禁用切换时的过渡动画
使用主题
在组件中使用
使用标准的 Tailwind 类名,无需额外操作:
// components/card.tsx
export function Card() {
return (
<div className="bg-card text-card-foreground border border-border rounded-lg p-4">
<h2 className="text-foreground font-semibold">卡片标题</h2>
<p className="text-muted-foreground">卡片内容</p>
</div>
);
}
获取当前主题
'use client';
import { useTheme } from "next-themes";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
return (
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
{theme === 'dark' ? '🌙' : '☀️'}
</button>
);
}
条件样式
'use client';
import { useTheme } from "next-themes";
export function ThemedComponent() {
const { theme } = useTheme();
return (
<div className={theme === 'dark' ? 'dark-specific-class' : 'light-specific-class'}>
内容
</div>
);
}
颜色自定义
修改主题颜色
编辑 globals.css
中的 CSS 变量:
:root {
/* 将主色改为蓝色 */
--primary: 217 91% 60%; /* 浅色模式蓝色 */
--primary-foreground: 0 0% 100%;
}
.dark {
/* 深色模式蓝色 */
--primary: 217 91% 70%;
--primary-foreground: 0 0% 0%;
}
创建新主题
添加新的主题类到 globals.css
:
/* 绿色主题 */
.theme-green {
--primary: 142 71% 45%;
--primary-foreground: 0 0% 100%;
--accent: 142 71% 45%;
--accent-foreground: 0 0% 100%;
}
.theme-green.dark {
--primary: 142 71% 55%;
--primary-foreground: 0 0% 0%;
}
/* 红色主题 */
.theme-red {
--primary: 0 84% 60%;
--primary-foreground: 0 0% 100%;
}
.theme-red.dark {
--primary: 0 84% 65%;
--primary-foreground: 0 0% 0%;
}
实现主题选择器
'use client';
import { useTheme } from "next-themes";
import { useEffect } from "react";
const THEMES = [
{ id: 'theme-default', label: '默认' },
{ id: 'theme-green', label: '绿色' },
{ id: 'theme-red', label: '红色' },
{ id: 'theme-blue', label: '蓝色' },
];
export function ThemeSelector() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useEffect(false);
useEffect(() => setMounted(true), []);
if (!mounted) return null;
return (
<div className="flex gap-2">
{THEMES.map(t => (
<button
key={t.id}
onClick={() => setTheme(t.id)}
className={`px-3 py-1 rounded ${
theme === t.id
? 'bg-primary text-primary-foreground'
: 'bg-muted text-muted-foreground'
}`}
>
{t.label}
</button>
))}
</div>
);
}
HSL 颜色格式
Sistine Starter 使用 HSL(色调、饱和度、亮度)颜色格式:
/* 格式: H S% L% */
--primary: 217 91% 60%;
H S L
/* 这等于 */
hsl(217, 91%, 60%)
优势:
- 易于理解和修改
- 深色/浅色模式只需改变 L 值
- 支持 CSS 自定义属性的透明度
示例变换:
/* 原始颜色 */
--primary: 217 91% 60%;
/* 创建更浅的版本(用于 hover) */
--primary-light: 217 91% 70%;
/* 创建更深的版本 */
--primary-dark: 217 91% 50%;
/* 创建禁用状态(降低饱和度) */
--primary-disabled: 217 50% 60%;
深色模式最佳实践
1. 确保可读性
:root {
/* 浅色模式 */
--foreground: 0 0% 3.9%; /* 深灰 */
--muted-foreground: 0 0% 45%; /* 中灰 */
}
.dark {
/* 深色模式 */
--foreground: 0 0% 98%; /* 浅灰 */
--muted-foreground: 0 0% 60%; /* 中灰 */
}
2. 图片适配
export function ThemedImage() {
const { theme } = useTheme();
return (
<img
src={theme === 'dark' ? '/image-dark.png' : '/image-light.png'}
alt="Description"
/>
);
}
3. 避免过度对比
/* ❌ 避免 */
.dark {
--background: 0 0% 0%; /* 纯黑 */
--foreground: 0 0% 100%; /* 纯白 */
}
/* ✅ 推荐 */
.dark {
--background: 0 0% 3.9%; /* 几乎黑 */
--foreground: 0 0% 98%; /* 几乎白 */
}
常见问题
1. 主题切换不生效
原因:
- 组件是服务端组件 (需要
'use client'
) - Next.js Themes 未正确初始化
- CSS 变量未正确定义
解决:
'use client'; // 必需!
import { useTheme } from "next-themes";
export function MyComponent() {
const { theme, setTheme } = useTheme();
// ...
}
2. 刷新页面主题重置
原因: 主题偏好未持久化
解决:
<ThemeProvider
attribute="class"
defaultTheme="light"
storageKey="theme-preference" // 添加存储 key
>
{children}
</ThemeProvider>
3. 深色模式下文字看不清
原因: 颜色对比度不足
解决:
.dark {
/* 确保前景色足够亮 */
--foreground: 0 0% 95%; /* 不是 98% */
--muted-foreground: 0 0% 65%; /* 不是 55% */
}
主题生成工具
使用以下工具生成和优化主题颜色:
- shadcn/ui Theme: https://www.shadcn-vue.com/docs/theming.html
- UIColors: https://uicolors.app/
- Tailwind Color Generator: https://www.twind.dev/