---
title: "你自己的设计系统"
description: "在你的产品中只保留一个层文件。一条命令就能为你发布的每个平台写出令牌和代码，并且都在同一个拉取请求里。"
url: "https://tanrei.chnotchy.com/zh-cn/getting-started/your-design-system/"
last_updated: 2026-10-23T21:16:42.000Z
---

# 你自己的设计系统

> 在你的产品中只保留一个层文件。一条命令就能为你发布的每个平台写出令牌和代码，并且都在同一个拉取请求里。

产品的外观改变时，Tanrei 的组件从不改变。改变的是一个**层**：一个小型 JSON 文件，它为共享词汇设定值，并声明你的调色板。`@tanrei/cli` 把这一个文件变成各平台所需的一切。

> [!WARNING]
> **预发布**
>
> `0.1.0-next.0` 已以 dist-tag `next` 发布到 npm：在 `0.1.0` 之前，请安装 `@tanrei/cli@next`和`@tanrei/mcp@next`。在 0.x 预发布期间不保持向后兼容：任何一个预发布都可能破坏任何东西，事先没有弃用期，破坏了什么写在它的 changeset 里。

## 安装

### 从 npm

把命令行工具作为开发依赖添加到你的产品中；本页上的每一条 `npx tanrei …` 命令运行的都是它。它带有 `tanrei studio` 所提供的 Theme Studio。

```sh
npm install --save-dev @tanrei/cli
```

MCP 服务器无需安装：代理启动它时，`npx` 会取来 `@tanrei/mcp`，注册方法见本页后文。

### 从源码

要使用 `main` 上的源码，请在仓库的克隆中构建这两个包，并把打包出的文件作为开发依赖安装到你的产品中：

```sh
git clone https://github.com/chnotchy/tanrei.git
cd tanrei
pnpm install
pnpm --filter @tanrei/studio build:app   # Theme Studio，由 `tanrei studio` 提供
pnpm --filter @tanrei/cli --filter @tanrei/mcp build
pnpm --filter @tanrei/cli --filter @tanrei/mcp pack --pack-destination ../tanrei-packages
```

```sh
# 在你的产品中：路径指向写入这些包的文件夹
npm install --save-dev path/to/tanrei-packages/tanrei-cli-*.tgz path/to/tanrei-packages/tanrei-mcp-*.tgz
```

至于 MCP 服务器，请改为注册已安装的副本：在你的产品文件夹中运行 `claude mcp add tanrei -- npx tanrei-mcp`。

## 从一个层到每个平台

1. **创建一个层**，可以从默认系统出发，也可以从某个预设出发。

   ```bash
   npx tanrei init acme --extends tanrei
   ```

   这会写出 `design/acme.system.json` 和 `tanrei.json`。更喜欢用滑块？那就在 [Theme Studio](https://tanrei.chnotchy.com/zh-cn/foundations/theme-studio/) 里调整这个层，然后把它下载下来。

2. **在 `tanrei.json` 中指明每项输出写到哪里。** 只列出你要发布的平台。

   ```json
   {
     "system": "design/acme.system.json",
     "kotlinPackage": "com.acme.design",
     "outputs": {
       "tokens": "design/tokens/acme",
       "web": "apps/web/src/styles",
       "swiftui": "apps/ios/Acme/Design",
       "compose": "apps/android/app/src/main/kotlin/com/acme/design",
       "flutter": "apps/flutter/lib/design",
       "react-native": "apps/mobile/src/design"
     }
   }
   ```

3. **构建。**

   ```bash
   npx tanrei build
   ```

   提交生成的结果。把 `npx tanrei build --check` 加入 CI：它不写任何文件，只要已提交的输出过时就会失败。

4. **使用。** 生成的文件包含的是你的系统的**值**；类型来自库本身，所以词汇一旦变化，构建就会失败，而不是悄悄地发生漂移。

   | 平台 | 你得到 | 你要写 |
   | --- | --- | --- |
   | Web | `acme.css` | `<html data-tanrei-system="acme">` |
   | SwiftUI | `TanreiSystem+Acme.swift` | `.tanreiTheme(TanreiTheme(system: .acme))` |
   | Compose | `TanreiSystemAcme.kt` | `TanreiTheme(system = TanreiSystem.Acme) { … }` |
   | Flutter | `acme_system.g.dart` | `TanreiTheme(data: TanreiThemeData.platform(context, system: acmeSystem), …)` |
   | React Native | `acme.ts` | `<TanreiProvider system={acme}>` |

## 一处修改，一个拉取请求

在层中修改一个值，运行 `tanrei build`，每个平台的文件就在同一次提交中一起变化。把所有文字调大一点，只需要一行：

```json
"tanrei.dial.type.base": { "$value": { "value": 17, "unit": "px" }, "reason": "在一臂之外阅读。" }
```

层可以对一个值做四件事：设定调节量、缩放令牌、重新指向别名、附带理由地固定一个值。关于这四件事，关于如何声明调色板（例如品牌色，或测验中的“正确”与“错误”），以及在仅靠取值不够时如何添加令牌，参见[设计系统与定制](https://tanrei.chnotchy.com/zh-cn/foundations/design-systems/)。

## 想控制多深都可以，始终是同一个文件

定制是分层次的，每个层次编辑的都是同一个 `<id>.system.json`：

| 你想要 | 你使用 |
| --- | --- |
| 快速完成设置 | **Theme Studio → 左侧栏**：外观的九行（品牌色、灰色、字体、尺寸、圆角、圆角平滑、图标、按下时的反馈、焦点样式），每一行点一下就能改，可以锁定，也可以随机搭配 |
| 按组调整 | **Studio → 左侧栏的“更多”**：全部八个调节量、所有调色板、各个组件、所有行为 |
| 在图形界面中逐个调整令牌 | **Studio → 检查 → 令牌**：搜索整个词汇；每个令牌只提供对它合法的编辑，并显示有哪些值跟随它 |
| 编辑源码 | **Studio → 检查 → 源码**，或你自己的编辑器：JSON 会依照 schema 并由引擎进行校验 |
| 一个不存在的令牌 | 添加它（见[设计系统与定制](https://tanrei.chnotchy.com/zh-cn/foundations/design-systems/)）：序列中的一级、一行就能声明的 Button 尺寸或变体，或者你自己的令牌 |

```bash
npx tanrei studio
```

会**针对你的层文件**打开 Theme Studio：你所做的编辑会随时保存到该文件（可以像其他改动一样用 git 审阅），同时在编辑器中所做的修改绝不会被覆盖，“Build”则会运行 `tanrei build`。它只监听 `127.0.0.1`，打印出的链接带有一次性密钥。本站上的 Studio 是同一个工具，只是用导入和导出代替了文件。

## 用你的系统检查代码

```bash
npx tanrei audit src
```

会读取产品的代码——脚本（JS、TS、JSX）、标记（HTML、Vue、Svelte、Astro）和样式表（CSS、Sass、Less）——并报告你的系统没有的东西，每一处都给出文件、行和列，以及应改写成什么：

- 系统没有的组件的元素——页面、模板、JSX 或字符串中的 `<tanrei-fab-menu>`、导入的 React 包装组件，或指向它的 CSS 选择器——并附上系统为同一用途提供的组件、拥有它的系统，以及把它纳入时要在你的层里写的那一行；
- 从不适合该系统的入口导入——在默认系统的产品中导入 `@tanrei/web/m3-like` 或 `@tanrei/react/all`——并附上适合的入口；
- 直接写出的、系统去掉了的 `variant="…"`，并附上系统提供的取值。

检查依据的是产品的系统（`tanrei.json`），或 `--system` 指定的系统：`m3-like` 这样的内置系统，或一个层文件。可以传入文件、目录，或用引号括起的 glob（`'src/**/*.{ts,tsx,html,css}'`）；读取目录时跳过 `node_modules` 和隐藏目录。`--format json` 以数据形式输出结果。发现问题时以退出码 1 结束，因此可以在 CI 中与 `tanrei build --check` 并列运行；无法运行时退出码为 2。MCP 服务器的 `check_usage` 做的是同一项检查。

它读的是文本，而不是运行中的页面：注释会被跳过，但放在变量里的 variant，或由片段拼成的标签名，是看不到的。

## Tailwind CSS 与 shadcn/ui

在 `outputs` 中加入 `"tailwind"` 或 `"shadcn"`。这两个文件都只是把**名称映射**到 Tanrei 的自定义属性上，因此主题、对比度级别和实时调节量在底层照常工作。

```css
@import "tailwindcss";
@import "@tanrei/web/tokens.css";
@import "./styles/acme.css";
@import "./styles/acme.tailwind.css"; /* bg-primary · text-on-primary · rounded-medium · p-200 · text-heading-4 */
```

## 分享一个系统：与人、与工具、与 AI 代理

- `tokens` 输出是一个自包含的 **DTCG 2025.10** 打包文件，带有标准的解析器。任何设计令牌工具都能读取它；生成它的层文件随包一同携带，每个分组还在 `$extensions["dev.tanrei.llm"]` 中带有面向代理的使用指引。

- 其中还有 `components.json`，即你的系统提供的组件：每个组件都写有它的元素（或类）、React 中的名称以及注册它的模块；系统没有的组件也列在其中，并注明是哪一层把它排除的、应改用什么。读取它的代理只会推荐你的产品能用的组件。Tanrei 自带的每个系统也各有一份：`@tanrei/web/systems/<id>.components.json`。

- `@tanrei/mcp` 是一个 [Model Context Protocol](https://modelcontextprotocol.io/) 服务器。在你的仓库中工作的代理可以搜索词汇、查看某个值的来历、比较不同的系统、**提议一项扩展**（新的 Button 尺寸、新的一级、你自己的令牌）并看到由此得到的值，还可以**在写入之前校验对层的修改**。它同时会被告知规则：通过层来修改值；扩展词汇只能添加，并且要附带理由；每个固定的值都要给出原因；绝不触碰阈值。至于界面本身，它根据系统的组件清单（components manifest）和规范作答：**该系统有哪些组件**（没有的组件该用什么代替）、一个组件的 props、事件和行为规则，以及**你的代码里有哪些元素、导入和变体是该系统没有的**，连同它们的位置——与 `tanrei audit` 做的是同一项检查。

  在你的产品文件夹中，从 npm 注册它（其他客户端的设置见[与 AI 代理一起使用 Tanrei](https://tanrei.chnotchy.com/zh-cn/getting-started/ai-agents/)）：

  ```bash
  claude mcp add tanrei -- npx -y @tanrei/mcp
  ```

> [!WARNING]
> **无障碍**
>
> 对比度阈值和结构性检查都是常量。在你的层中，违反是一条警告，但绝不会悄无声息：它由 `tanrei build` 打印出来，写入 `a11y-report.json`，并标注在每个生成文件的顶部。在中、高对比度下，请以零警告为目标：这些方案之所以存在，是因为有人明确要求了更高的对比度。内置预设本身在任何方案下都以对其所模仿系统的保真度来衡量，而不是以 Tanrei 的无障碍准则来衡量；它们的测量值只是参考信息。你在预设之上构建的层仍然是你的产品，它继承来的问题同样会收到警告。
