Tanrei 是如何构建的
两个唯一可信来源、规格先行的工作流,以及让五个平台、文档和状态矩阵保持一致的漂移检查。
Tanrei 从一个仓库把一套设计系统交付到五个平台。要让这种方式行得通,仓库本身必须让人很难出错:在一处所做的修改,要么传播到所有地方,要么明明白白地失败。本页介绍做到这一点的那套机制。
只有两个目录包含决策:
| 目录 | 内容 | 格式 |
|---|---|---|
tokens/ |
每一个设计值 | Design Tokens Community Group 格式,2025.10 |
specs/ |
每一个组件的契约 | JSON,由 specs/component.schema.json 校验 |
其余一切都是推导出来的:为各平台生成的令牌文件、各平台的实现、这份文档中的表格和状态矩阵。当推导出的东西与唯一可信来源不一致时,按定义,错的是推导出的东西。
组件规格与平台无关。它记录组件的构成、属性、插槽、事件和状态;以带编号的需求写成的行为;无障碍契约,包括角色、键盘操作表和标注方式;它所绑定的令牌分组;一组测试样例(fixtures),即每个平台都以相同 ID 截取快照的基准渲染;以及一个 platforms 块,把中立的名称映射到各平台的惯用写法。
行为以 EARS 形式书写,这是一种受约束的句式,便于据此编写测试:
WHEN the user activates the button by pointer, touch, Enter or Space
THE button SHALL emit click exactly once.每条需求都有一个类似 BTN-1 的 ID,各平台上的测试都引用这些 ID。
- 提议。 编写或修改规格,必要时也修改令牌。新组件和破坏性变更由维护者在修改规格之前决定。
- 校验。
pnpm validate检查 schema、对令牌做 lint,并针对每个主题和对比度级别验证对比度不变式。 - 生成。
pnpm generate重新构建令牌输出、脚手架和状态矩阵。 - 实现。 在规格列出的每个平台上实现,并附带单元测试、快照测试和无障碍测试。
- 检查。
pnpm check按顺序运行全部检查,包括漂移检查,并且必须全部通过。 - 记录。 添加 changeset。所有包的版本同步推进,因为对等本身就是产品。
任何平台的公开接口,都不得在规格没有在同一次提交中做出相同修改的情况下发生变化。
- 重新生成的差异。 CI 会重新生成所有派生文件,只要结果与已提交的内容不同就失败。手工改动生成的文件是留不住的。
- 对等检查。 对规格声明的每个平台,检查器都要求有实现、有测试、每个测试样例都有快照,并且有该组件的英文和日文文档页面。站点的其他各语言必须有哪些页面,由文档站点另行检查。状态矩阵由同样的事实计算得出,因此它所声称的不可能多于实际存在的。
- 对比度不变式。 对比度不达标的配色方案根本不会被输出。
- 快照纪律。 快照出现差异就是一项发现。只有当视觉变化正是规格或令牌变更的预期结果时,基准图才会更新。
对等意味着相同的令牌、等效的视觉结果、相同的状态和相同的无障碍语义,并不意味着完全相同的 API。Compose 接受 enabled,Flutter 用值为 null 的 onPressed 来禁用按钮,SwiftUI 使用 .disabled(_:)。每一处对应关系都写在规格的 platforms 块中,并显示在每个组件页面上。
同样的规则同时约束人和 AI 代理,并且其中尽可能多的规则由工具强制执行,而不是依靠信任。
- 分层的规则。 根目录一份简短的
AGENTS.md写明项目的根本规则;嵌套的文件为令牌、规格和各个包补充本地规则。嵌套的规则可以在根规则之上追加,但绝不能削弱它们。 - 分道。 平台相关的工作被限制在该平台的包内。实现 SwiftUI 的代理没有理由去碰 Compose。
- 钩子。 对生成目录的编辑、
--no-verify以及更新快照基准图的操作,都在工具层面被拦截;会话结束时还会运行漂移检查。 - 技能。 添加组件或令牌这类反复出现的流程,被写成按步骤执行的技能,因此每次的做法都一样。
- 不做发布。 向注册表发布由维护者决定。任何自动化的执行者都不得进行发布。
注意
这份文档也遵循同样的规则。状态、令牌参考和组件规格表格都在构建时由仓库数据渲染而来。只有正文是手写的。