# 深色模式

> 在深色界面中使用 React 颜色选择器：默认跟随系统，也可以固定为某一种主题，或者与应用中已有的基于 class 的主题切换保持一致。

Source: https://chroma-panel.jscrate.dev/zh/react/recipes/dark-mode
Last updated: 2026-09-21

默认情况下，选择器跟随操作系统的设置。一旦你的应用有了自己的主题切换，问题就来了：两者会不一致。比如有人在浅色的桌面系统上以深色模式浏览你的网站，打开选择器看到的却是一个白色面板。把应用最终生效的主题交给选择器，这个问题就解决了。

```tsx
"use client";

import { ColorInput } from "chroma-panel";
import "chroma-panel/style.css";
import { useTheme } from "next-themes";

function panelTheme(value: string | undefined) {
  if (value === "dark") return "dark" as const;
  if (value === "light") return "light" as const;
  return undefined;
}

export function ThemedColorInput() {
  const { resolvedTheme } = useTheme();

  return (
    <ColorInput
      name="brand"
      defaultValue="#3366cc"
      theme={panelTheme(resolvedTheme)}
    />
  );
}
```

`theme` 只接受 `"dark"` 和 `"light"`，而 `resolvedTheme` 的类型是 `string | undefined`，所以上面的类型收窄并不是多此一举。返回 `undefined` 也是有意义的：不传 `theme` 时，面板会回到跟随系统偏好的状态。在 provider 还没解析出主题之前，这就是最合理的选择。

`ChromaPanel` 上也有同一个 prop，两者用法完全相同。它和面板的其他 prop 放在一起的情况，请参阅 [ChromaPanel 参考文档](https://chroma-panel.jscrate.dev/zh/react/components/chroma-panel)。

## 跟随系统

这是默认行为。面板的背景、边框、文字和强调色都以 `light-dark()` 成对声明，面板根元素上声明了 `color-scheme: light dark`，浏览器会根据用户的偏好选择对应的一侧。不用传任何参数，不涉及水合，首次绘制也不会闪烁，因为这个选择是在 CSS 中完成的，而不是在 JavaScript 中。

## 固定为一种主题

如果周围的界面不会变，就直接传入 `theme`。比如在深色编辑器外框中的选择器，即使整个应用是浅色的，也应该保持深色：

```tsx
<ChromaPanel theme="dark" defaultValue="#3366cc" />
```

## 样式表如何决定主题

`theme` 会以 `data-cp-theme` 属性的形式出现在面板根元素上，值为 `dark` 或 `light`。样式表随后把这个根元素的 `color-scheme` 设为对应的单一值，于是每个 `light-dark()` 令牌都会取这一侧的值。如果什么都不传，就不会有这个属性，`color-scheme: light dark` 保持不变，由用户偏好决定。

宽度小于 640px 时，选择器会变成底部面板，它的样式是单独匹配的，所以遮罩和面板背景会跟随其中的面板，而不会各行其是。

由于这个属性就在面板根元素上，面板进入弹出层时它也会跟着走。弹出层渲染在 `document.body` 中，而不在你的布局里，因此作用于某个外层元素的深色样式影响不到它，而针对 `.cp-root` 的选择器可以。

## 修改深色配色

这个包的样式规则都放在 `@layer chroma-panel` 级联层中，而不在任何层中的 CSS 优先级高于层内的规则。所以你自己的样式表可以替换任意令牌，不必和选择器优先级较劲：

```css
.cp-root {
  --cp-surface: light-dark(#ffffff, #0b0b0f);
  --cp-accent: light-dark(#2d7ff9, #7aa2ff);
}
```

把覆盖值也写成 `light-dark()` 对，就能在一条规则里同时写好两侧，自动切换也能照常工作。完整的令牌列表见[主题](https://chroma-panel.jscrate.dev/zh/react/handbook/theming)页面；在 Next.js App Router 中使用选择器的方法，见 [Next.js 页面](https://chroma-panel.jscrate.dev/zh/react/frameworks/next-js)。

> **两种变深色的方式**
>
> `theme` 只作用于面板本身。`--cp-` 自定义属性决定面板具体用哪些颜色。用 `theme`
> 选择深浅；如果内置的深色不是你想要的深色，就用这些自定义属性。
