# 设计新功能

> 动手做新东西之前要回答的问题，从最接近的模式到组件的流程，一份自查清单，以及什么时候该改设计系统本身。

Source: https://raft-design.botiverse.build/zh/principles/designing-a-new-feature. Translated from https://raft-design.botiverse.build/principles/designing-a-new-feature.md; where the two differ, the English page is right.

每个新的屏幕、面板或流程，都从系统已有的东西出发：回答六个问题，找到最接近的模式，用 raft-ui 组件拼出来，再分清哪部分只属于一个产品、哪部分属于所有产品。做完后对照本页末尾的清单检查。只有当工作表明某条规则缺失或有错时，才去改系统本身，并且在同一个 pull request 里修改规范。

## 要回答的问题
在画任何东西、写任何东西之前，先回答这些问题。每个答案都决定一件具体的事。

1. **是每个产品都需要，还是只有一个产品需要？** 每个产品都需要的东西遵循[模式](https://raft-design.botiverse.build/zh/patterns)；如果是新的，就把它变成一个模式。只有一个产品需要的东西，写在[产品](https://raft-design.botiverse.build/zh/products)下该产品的页面里。无论哪种，先看产品现在是怎么做的：已上线的产品是结构上的参照。
2. **它是内容、区域还是控件？** 这决定它的表面和线：内容放在纸面上，用细线分隔；区域和相邻区域之间隔一条接缝线；控件有墨色边框（[原则](https://raft-design.botiverse.build/zh/principles/principles.md#2-three-lines-three-meanings)）。
3. **屏幕的主操作是哪一个？** 只有它是粉色，一个屏幕也可以没有主操作。如果不止一个操作都可能是主操作，说明层次错了：先想清楚这个屏幕是为哪个操作而存在的（[Button](https://raft-design.botiverse.build/zh/components/button.md)）。
4. **每种颜色是什么意思？** 每种颜色都是一个身份、一个状态、一个信号（你、待办、这一项）或一个后果（打断、不可撤销）。如果某种颜色都不是，就去掉它（[颜色](https://raft-design.botiverse.build/zh/foundations/color.md#meanings-of-color)）。
5. **它在深色模式下、320px 宽度下、只用键盘时都能用吗？** 按 390px 设计，并让所有东西在 320px 下都放得下（[响应式布局](https://raft-design.botiverse.build/zh/patterns/responsive.md)）。人能用键盘到达的每个元素都显示焦点环（[层级](https://raft-design.botiverse.build/zh/foundations/elevation.md#focus-ring)）。每个新组件都在两种模式下检查（[深色模式](https://raft-design.botiverse.build/zh/foundations/dark-mode.md)）。
6. **一个人盯着它看几个小时会不会累？** 会的话，先去掉颜色，再去掉线，再去掉动效。显示状态的东西不能循环播放，也不能闪烁（[动效](https://raft-design.botiverse.build/zh/foundations/motion.md)）。

## 流程
### 1. 找到最接近的模式
大多数新工作都是系统里已有内容的变体。设计之前先把它找出来：

| 你要做的是 | 从这里开始 |
| --- | --- |
| 新的屏幕、面板或目的地 | [页面结构](https://raft-design.botiverse.build/zh/patterns/page-structure.md)、[导航](https://raft-design.botiverse.build/zh/patterns/navigation.md)和[响应式布局](https://raft-design.botiverse.build/zh/patterns/responsive.md) |
| Agent 做的或展示的任何东西 | [Agent](https://raft-design.botiverse.build/zh/people-and-agents.md) 和 [人和 Agent](https://raft-design.botiverse.build/zh/people-and-agents.md) |
| 消息、消息列、输入框 | [对话](https://raft-design.botiverse.build/zh/patterns/conversation.md) |
| 任务及其状态 | [任务](https://raft-design.botiverse.build/zh/patterns/tasks.md) |
| 表单或设置页 | [表单](https://raft-design.botiverse.build/zh/patterns/forms.md) |
| 结果、错误、加载状态或空状态 | [反馈](https://raft-design.botiverse.build/zh/patterns/feedback.md) |
| 由几个现有部件组合成的东西 | [组合组件](https://raft-design.botiverse.build/zh/components.md) |

然后阅读你所在区域对应的产品页面（Raft Chat 就看 [Raft Chat](https://raft-design.botiverse.build/zh/products/raft-chat.md)），看看它在那里已经放了什么。

### 2. 选择组件
用 raft-ui 拼出这个功能。[组件概览](https://raft-design.botiverse.build/zh/components.md)列出了现有的组件；当好几个看起来都合适时，[选择组件](https://raft-design.botiverse.build/zh/components.md)帮你挑一个：哪种选择用哪个控件，哪种消息用哪种反馈，哪种用途用哪种浮层。

- 只要有合适的 raft-ui 组件就用它，徽标、标签片、分隔线、字段这类小部件也一样。手工复制的版本，在任何一方第一次改动时就会和原版走样。
- 用组件之前先读它的文档：「When to use」表格、规则和无障碍部分。
- 如果没有合适的，先在[组合组件](https://raft-design.botiverse.build/zh/components.md)里找现有部件的组合。只有 raft-ui 里确实没有合适的才手写标记，并在 pull request 里说明，同时向 raft-ui 提议补上缺的部件，而不是保留一份本地副本。
- 每个值都用语义 token。不要用 Tailwind 的默认色板或原始颜色值。

raft-ui 的安装和配置见[使用 raft-ui](https://raft-design.botiverse.build/zh/about/use-raft-ui.md)。

### 3. 分清共享的和产品特有的
把每条新规则写在它该在的地方，让下一个产品继承它该继承的，不多也不少：

| 如果规则是关于 | 写在 |
| --- | --- |
| 每个组件都遵循的值或规则 | 基础 |
| 某一个组件 | 该组件的文档 |
| 组件在任何产品中如何组合 | 模式 |
| 某个产品的目的地、屏幕以及什么放在哪里 | 该产品的页面 |

只对 Raft Chat 成立的规则（「手机标签栏显示哪四个目的地」）是产品规则。在 Tasks 或 Calendar 里也一样的规则（「某个面板出了问题，在它的标题栏下方显示一条提示」）是共享规则。拿不准时，问问第二个产品是否会出于同样的理由做同样的选择；如果会，这条规则就是共享的。

### 4. 写文案
标签、标题、空状态和错误信息遵循[写作](https://raft-design.botiverse.build/zh/content/writing.md)：人点击的东西用 Title Case，其余用 sentence case；Agent 用描述队友的动词；按钮的标签和它打开的对话框标题保持一致。

## 自查清单
请人评审之前先过一遍这份清单。每一项都是评审者会检查的。

- [ ] 黄色只出现在外框上，以及表示「你」的东西上：导航中的选中项、打开的开关、提及你的地方、搜索命中。
- [ ] 屏幕上最多一个粉色按钮，用于这个屏幕存在的那个操作。
- [ ] 屏幕上的每种颜色都是一个身份、一个状态、一个信号或一个后果。红色只用于无法撤销的事。
- [ ] 每条线要么是内容里的细线，要么是区域之间的接缝线，要么是控件上的墨色边框；两个区域之间恰好共用一条线。
- [ ] 每块品牌色都有墨色边和墨色文字；柔和色调没有边；控件和卡片都没有阴影。
- [ ] 身份标识和圆点是方的；只有开关和单选按钮是圆的。
- [ ] 没有全大写的文字。可点击的文字用 Title Case；ID、时间、计数和事件行用 Space Mono。
- [ ] 显示状态的东西不循环、不闪烁，每个动画在 `prefers-reduced-motion` 下都会停止。
- [ ] Agent 和人使用同样的行、消息和头像，Agent 正在做的事显示在它本来就出现的地方。
- [ ] 屏幕在深色模式下可用，贴纸不变并带有模切边。
- [ ] 所有东西在 320px 宽度下都放得下，触控目标至少 44px。
- [ ] 只用键盘就能完成所有操作，并且都显示焦点环。可点击的东西都是 `<button>` 或带 `href` 的 `<a>`，每个纯图标按钮都有 `aria-label`。
- [ ] raft-ui 里有的部件都来自 raft-ui；手写的部件都在 pull request 里列出。
- [ ] 每个值都是语义 token。
- [ ] 每条新规则都写在它该在的基础、模式、组件文档或产品页面里。

## 修改设计系统
当新工作表明某条规则缺失或有错时，系统就该改。公开地改，并和工作放在同一个 pull request 里：

- **在同一个 pull request 里更新规范。** 改变某样东西的外观或行为，就同时在代码和文档里更新它遵循的规则。如果系统对你要做的东西还没有规则，就在该在的地方写一条（见[第 3 步](#3-decide-what-is-shared-and-what-is-product-specific)）。raft-ui 和规范不一致时，改掉其中一个，不要让两者一直对不上。
- **只为重大的、跨领域的选择添加决策记录**：改变一条原则、一种材质或一种颜色的含义，适用于多个产品，或推翻之前的记录。被推翻的记录标为 `superseded`。较小的改动只更新规范，不需要记录。模板见[编写指南](https://github.com/botiverse/raft-design/blob/main/design/authoring/templates/decision.md)。
- **暂时做不了的选择**，记在[待定问题](https://raft-design.botiverse.build/zh/decisions/open-questions.md)里，并写明做出决定之前使用的默认做法。

文档如何组织和编写见[编写指南](https://github.com/botiverse/raft-design/blob/main/design/authoring/README.md)；pull request 需要满足什么见 [CONTRIBUTING.md](https://github.com/botiverse/raft-design/blob/main/CONTRIBUTING.md)。

## 相关内容
- [原则](https://raft-design.botiverse.build/zh/principles/principles.md)：这些问题和清单所依据的规则。
- [为什么 Raft 长这样](https://raft-design.botiverse.build/zh/principles/why-raft-looks-this-way.md)：这些规则背后的理由。
- [选择组件](https://raft-design.botiverse.build/zh/components.md)：每种情况对应一个组件。
- [决策](https://raft-design.botiverse.build/zh/decisions.md)：已经做出的重大选择，以及当时权衡过的其他方案。
