> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vibestrap.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# 加回暗黑模式

> Vibestrap 默认只有浅色主题。下面是五步加回 light/dark 切换的配方。

Vibestrap 故意**只发浅色主题**。理由：双主题维护成本高，营销页作为
单主题更有定见，对标的高端脚手架（Stripe、Loops、Resend、Clerk、
tuwa.ai）也都不支持暗黑。

但很多开发者真的有暗黑偏好。如果你的成品需要，按下面五步加回。

## Step 1 — 重新安装 `next-themes`

```bash theme={null}
pnpm add next-themes
```

## Step 2 — 用 `ThemeProvider` 包起来

编辑 `src/components/providers.tsx`：

```tsx theme={null}
'use client';

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ThemeProvider } from 'next-themes';
import { useState } from 'react';
import { Toaster } from 'sonner';

export function Providers({ children }: { children: React.ReactNode }) {
  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: { staleTime: 60 * 1000, refetchOnWindowFocus: false },
        },
      })
  );
  return (
    <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
      <QueryClientProvider client={queryClient}>
        {children}
        <Toaster position="top-center" richColors />
      </QueryClientProvider>
    </ThemeProvider>
  );
}
```

注意：`<Toaster>` 上的 `theme="light"` 要去掉——sonner 会自动跟随
`next-themes`。

## Step 3 — 在 `globals.css` 里加回 `.dark` 块

在 `src/app/globals.css` 顶部（`@plugin` 之后）加 variant 指令：

```css theme={null}
@custom-variant dark (&:is(.dark *));
```

然后在 `:root { ... }` 之后加一段 `.dark { ... }`，定义反色调色板。
基于编辑式 cream-mono 基线的话，大概是这样：

```css theme={null}
.dark {
  --background: oklch(0.16 0.01 85);
  --foreground: oklch(0.96 0.012 85);
  --card: oklch(0.20 0.012 85);
  --card-foreground: oklch(0.96 0.012 85);
  --popover: oklch(0.20 0.012 85);
  --popover-foreground: oklch(0.96 0.012 85);
  --primary: oklch(0.96 0.012 85);
  --primary-foreground: oklch(0.16 0.01 85);
  --secondary: oklch(0.26 0.012 85);
  --secondary-foreground: oklch(0.96 0.012 85);
  --muted: oklch(0.26 0.012 85);
  --muted-foreground: oklch(0.7 0.005 80);
  --accent: oklch(0.26 0.012 85);
  --accent-foreground: oklch(0.96 0.012 85);
  --border: oklch(1 0 0 / 10%);
  --input: oklch(1 0 0 / 14%);
  --ring: oklch(0.55 0.2 250);
}
```

按你的皮肤微调——上面是 cream-mono 编辑式默认值的对应反相。

## Step 4 — 恢复切换组件

新建 `src/components/layout/theme-toggle.tsx`：

```tsx theme={null}
'use client';

import { Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import { Button } from '@/components/ui/button';

export function ThemeToggle() {
  const { setTheme, resolvedTheme } = useTheme();
  const next = resolvedTheme === 'dark' ? 'light' : 'dark';
  return (
    <Button
      variant="ghost"
      size="icon"
      aria-label="Toggle theme"
      onClick={() => setTheme(next)}
    >
      <Sun className="rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
      <Moon className="absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
    </Button>
  );
}
```

挂到 `src/components/layout/header.tsx`：

```tsx theme={null}
import { ThemeToggle } from './theme-toggle';
// …
<div className="flex items-center gap-1">
  <LocaleSwitcher />
  <ThemeToggle />
  <UserNav />
</div>
```

## Step 5 — 在需要的地方补回 `dark:` Tailwind variant

Vibestrap 中原本用过 `dark:` 补色的位置：

* Markdown 长文页——在 `<div className="prose ...">`
  外层加回 `dark:prose-invert`。涉及
  `src/app/[locale]/(marketing)/{terms,privacy,refund,license,about,changelog,blog/[slug]}/page.tsx`
  几个文件。
* `src/components/ai/*` 的状态色——`text-emerald-600` 等需要配对
  `dark:text-emerald-400` 才能在暗色下读得舒服。
* `src/components/ui/alert.tsx`——destructive 变体的可读性。
* `src/components/turnstile-field.tsx`——用 `resolvedTheme` 传给
  Turnstile widget，让它的内嵌 UI 跟随主题。

可以用这条命令找需要补的位置：

```bash theme={null}
grep -rn "text-emerald-600\|text-amber-600\|prose " src/
```

…然后在每条旁边加上对应的 `dark:*` 类。

## 验收

五步走完跑一遍闸：

```bash theme={null}
pnpm typecheck && pnpm lint && pnpm check:i18n && pnpm build
```

浏览器里切换主题确认：

* 营销页两种主题都干净。
* 状态色 / accent 在暗色下可读。
* Sonner toast 跟主题。
* Turnstile widget 颜色一致。

## 我们为什么不发

继承的维护税：每次改组件都得问"暗色下也行吗"。多数买家面向的消费
受众不会在营销页切暗黑。编辑式 / cream-mono 美学（Vibestrap 的默认
皮）本就是浅色为主。

如果你照上面走完发现哪里有漏洞，告诉我们一声——我们改这页文档，而不
是把暗黑模式加回主分支。
