Customization

主题配置

更改品牌颜色,配置明暗模式,切换多主题预设,以及创建自定义 oklch 主题。

Nebutra-Sailor 的主题系统由三个独立的层级组合而成:

层级职责
运行时 Token@nebutra/tokensCSS 变量——颜色、间距、字体排版
明暗模式@nebutra/tokens(next-themes)ThemeProvider + useTheme
多主题预设@nebutra/theme6 个 oklch 预设,通过 data-theme 属性应用

编辑 CSS 变量

所有设计 token 存储在单一文件中,可直接编辑:

packages/design/tokens/styles.css

关键 Token 参考

:root {
  --brand-primary:   var(--blue-9);    /* 主要 CTA、链接、图表 */
  --brand-accent:    var(--cyan-9);    /* 强调色、成功高亮 */
  --brand-tertiary:  #8b5cf6;          /* 基础设施、第三层数据可视化 */
  --brand-gradient:  135deg, var(--blue-9) 0%, var(--cyan-9) 100%;
}
:root {
  --neutral-1:  #ffffff;   /* 应用背景 */
  --neutral-2:  #f8fafc;   /* 微妙背景 */
  --neutral-7:  #d1d5db;   /* 默认边框 */
  --neutral-11: #374151;   /* 次要文本 */
  --neutral-12: #0a0a0a;   /* 主要文本 */
}
:root {
  --status-danger:  #ef4444;              /* 错误、重大变更 */
  --status-warning: #f59e0b;              /* 改进项、待处理 */
  --status-success: #10b981;              /* 修复、已完成 */
  --status-info:    var(--brand-primary); /* 信息提示 */
}
:root {
  /* 12 阶 Radix 风格蓝色色阶 */
  --blue-1:  #eff6ff;
  --blue-9:  #0033FE;   /* ← 品牌主色 */
  --blue-12: #0a1628;

  /* 12 阶青色色阶 */
  --cyan-1:  #ecfeff;
  --cyan-9:  #0BF1C3;   /* ← 品牌强调色 */
  --cyan-12: #031f19;
}

使用调色板生成器重新品牌化

更改整个品牌调色板最快的方式是使用生成器脚本。它会重写 packages/design/tokens/styles.css 中的所有 token 别名:

node scripts/generate-palette.mjs --primary=#7C3AED --secondary=#F59E0B

--primary(主品牌色)和 --secondary(强调色)传入任意有效的十六进制颜色值。

pnpm --filter @nebutra/landing dev
# 或
pnpm --filter @nebutra/web dev

热重载并不总能处理 styles.css 的变更;完整重启更为稳妥。

pnpm --filter @nebutra/storybook dev

导航至 Design TokensBrand Colors,确认新调色板在所有色阶中均正确渲染。

手动重新品牌化示例

如果您更倾向于手动编辑 token:

/* packages/design/tokens/styles.css */
:root {
  --brand-primary:   #7C3AED;   /* 紫色 */
  --brand-accent:    #F59E0B;   /* 琥珀色 */
  --brand-gradient:  135deg, #7C3AED 0%, #F59E0B 100%;

  /* 同时更新源色阶 */
  --blue-9:  #7C3AED;
  --cyan-9:  #F59E0B;
}

编辑中性色阶时,务必同时更新明色模式(:root)和暗色模式(.dark / [data-theme])两个代码块。跳过暗色模式代码块会导致深色模式下的对比度问题。

明暗模式

明暗模式由 @nebutra/tokens 重新导出的 next-themes 管理。

配置

ThemeProvider 已在 apps/web/src/app/layout.tsxapps/landing/src/app/layout.tsx 中配置完毕,无需额外设置。

// apps/web/src/app/layout.tsx
import { ThemeProvider } from "@nebutra/tokens";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh" suppressHydrationWarning>
      <body>
        <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

以编程方式切换主题

"use client";
import { useTheme } from "@nebutra/tokens";

export function ThemeToggle() {
  const { theme, setTheme } = useTheme();

  return (
    <button
      type="button"
      onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
      aria-label="切换深色模式"
      className="rounded-md p-2 focus:outline-none focus:ring-2 focus:ring-[var(--brand-primary)]"
    >
      {theme === "dark" ? "浅色" : "深色"}
    </button>
  );
}

已解析的主题

当用户未明确选择主题时,useTheme 返回 "system"。使用 resolvedTheme 获取实际计算值:

const { resolvedTheme } = useTheme();
// → "light" | "dark"

多主题预设

@nebutra/theme 提供 6 个基于 oklch(感知均匀色彩空间)构建的即用主题预设。每个预设通过 <html> 上的 data-theme 属性应用完整的 token 覆盖。

预设风格特点
nebutra蓝/青——Nebutra 品牌色(默认)
dark-dense信息密度高,色调低饱和
minimal接近单色,留白充足
vibrant高饱和,活泼,面向消费者
ocean深蓝与青蓝,沉静专注

切换预设

// 静态方式——在 layout 中设置
<html data-theme="nebutra">

// 动态方式——用户偏好存储在 cookie 或 localStorage 中
"use client";
import { useState } from "react";

const PRESETS = ["nebutra", "dark-dense", "minimal", "vibrant", "ocean"] as const;

export function ThemePresetSelector() {
  const [preset, setPreset] = useState<string>("default");

  function applyPreset(name: string) {
    document.documentElement.setAttribute("data-theme", name);
    setPreset(name);
  }

  return (
    <div className="flex gap-2">
      {PRESETS.map((name) => (
        <button
          key={name}
          type="button"
          onClick={() => applyPreset(name)}
          className={`rounded px-3 py-1 text-sm ${preset === name ? "bg-[var(--brand-primary)] text-white" : "bg-neutral-2"}`}
        >
          {name}
        </button>
      ))}
    </div>
  );
}

创建自定义主题预设

packages/design/theme/themes.css

复制一个现有的预设代码块并修改 oklch 值。主题名称必须为小写短横线命名。

/* packages/design/theme/themes.css */
[data-theme="forest"] {
  --brand-primary:   oklch(45% 0.18 145);   /* 森林绿 */
  --brand-accent:    oklch(75% 0.15 80);    /* 暖金色 */
  --brand-gradient:  135deg,
    oklch(45% 0.18 145) 0%,
    oklch(75% 0.15 80) 100%;

  --neutral-1:  oklch(99% 0.005 145);
  --neutral-12: oklch(12% 0.02 145);
}
<html data-theme="forest">

打开 Storybook 并检查 Design Tokens 版块,验证对比度比率符合 WCAG AA 标准。

oklch 值的格式为 oklch(亮度% 色度 色相)。可使用 oklch.com 等工具选取感知均匀的颜色。

字体排版

字体通过 next/font 加载并注入为 CSS 变量,无外部字体请求。

/* packages/design/tokens/styles.css */
:root {
  --font-sans: var(--font-geist-sans), "Geist", system-ui, sans-serif;       /* UI 文本 */
  --font-mono: var(--font-geist-mono), "Geist Mono", ui-monospace, monospace; /* 代码、指标 */
}

如需更换字体族,请更新应用 layout 中的 next/font 配置,然后同步更新 packages/design/tokens/styles.css 中的 token 回退栈。

Storybook Design Tokens 参考

Storybook 的 Design Tokens 版块会直观渲染每个 token:

pnpm --filter @nebutra/storybook dev
# → http://localhost:6006 → Design Tokens

可用版块:

  • 品牌颜色(蓝色 + 青色 12 阶色阶)
  • 语义调色板(中性色阶)
  • 品牌渐变
  • 状态颜色
  • 字体排版比例
  • 动效预设
  • 阴影/层级系统

How is this guide?

目录