Sistine Starter

主题系统

自定义网站外观和颜色主题

主题系统

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% */
}

主题生成工具

使用以下工具生成和优化主题颜色:

  1. shadcn/ui Theme: https://www.shadcn-vue.com/docs/theming.html
  2. UIColors: https://uicolors.app/
  3. Tailwind Color Generator: https://www.twind.dev/

下一步