# TFDS 全局设计规范（组件库未覆盖场景的兜底约束）

> **作用域**：当 `components.index.json` 没有覆盖你要生成的内容时（例如自定义信息卡、状态条、KPI 块、装饰性区块、统计模块、面包屑、徽章组合），**必须**严格遵守本文件给出的 token 与组合规则。**禁止**自由发挥颜色 / 间距 / 字号 / 圆角 / 阴影 / 动效。
>
> **优先级**（与 SKILL.md 一致）：组件索引 > SKILL.md > LAYOUT_RULES.md > 本文件 > DESIGN_PRINCIPLES.md > 模型自身审美。

---

## 0.0 快查索引（按问题找章节，再读细节）

> 本文件较长。**生成前**用这张表找 Token，**生成后**直接跑 §14 自检 Checklist。如果是组件契约级问题（Card / FormTitle / Agent 等），先去 `components.index.json` 或 `DESIGN_PRINCIPLES.md §4.x`。

| 你的问题 / 信号 | 直接跳到 |
| --- | --- |
| 颜色硬编码 `#000 / #333 / text-black / bg-gray-*` 怎么改？ | §1.1 中性色 Token |
| 品牌色（青绿）只能用在哪些位置？hover / active / link 用哪个 token？ | §1.2 品牌色 |
| 成功 / 警告 / 危险 / 信息 4 档语义色怎么用？能不能大面积铺？ | §1.3 语义色 |
| **AI 渐变能不能用在当前模块？背景 / 边框 / 图标 / 文字分别用哪个 token？** | **§1.4 渐变色 Token（AI 专用）** |
| 字号到底有哪 9 档？为什么不能用 `text-[15px]`？ | §2.1 字号 Token |
| 字重为什么写 `font-bold` 不生效？Tailwind v4 推荐写法？ | §2.2 字重 Token |
| 行高黄金组合（14px → 20px / 16px → 24px）？ | §2.3 行高 Token |
| **白卡↔白卡到底是 8px 还是 16px？横向 vs 纵向哪个用哪个？** | **§3 间距 Token + 顶部「横向 vs 纵向决策线」** |
| 表单 / 筛选栏的 Input / Select 宽度规则？为什么有的满宽有的固定宽？ | §3.1 表单控件宽度与对齐铁律 |
| 长内容控件（TextArea / JSON / 代码）的高度节奏？ | §3.2 卡片内长内容控件的高度节奏 |
| 圆角只能取哪 9 档？白卡默认圆角？ | §4 圆角 Token |
| 白卡能不能加 border / outline / ring / shadow？灰底页面外框的 border 算违规吗？ | §5、§5.5 阴影 + 白卡描边铁律 |
| 过渡动画为什么不能 `transition-all`？duration 三档怎么选？ | §6 过渡 / 动效规则 |
| 自定义按钮 / a 标签焦点环怎么写才不丢失键盘可达性？ | §7 焦点可达性 |
| 文本会被裁切 / 一字一行 / truncate 不生效？ | §8 文本自适应 |
| 图标到底用 `<Icon name="..." />` 还是 lucide？尺寸怎么选？ | §9 全局图标规则 |
| Tabs 默认 size 为什么是 SM？什么时候才用 MD/LG？ | §9.5 Tabs 尺寸默认规则 |
| 状态徽标 / 计数标签必须用 `<Tag />` 吗？手写 `<span>` 算违规吗？ | §10 状态徽标 / Tag 规则 |
| 表格 / 表单 / 日志能不能放在渐变 / 浅彩色背景上？ | §10.5 密集内容背景规则 |
| 文本品牌色链接怎么写？能不能裸 `<a className="text-blue-...">`？ | §11 链接规则 |
| 空状态 / 无权限 / 加载中页面用什么组件？文案约定？ | §12 空状态 / 错误页 / 加载中 |
| 组件库没覆盖时，KPI / 自定义信息卡怎么手写？ | §13 自定义信息卡 / KPI 块 |
| **页面生成完毕，交付前要逐项扫一遍什么？** | **§14 AI 自检 Checklist（必跑）** |

⚠️ 写完页面前必跑 **§14 AI 自检 Checklist**；如果是整页骨架问题，再叠加 `LAYOUT_RULES.md §11 自适应自检 Checklist`。

---

## 0. TFDS Inspect 标识契约

> **本节是后续所有 Token 规则的前提**：以下 §1–§14 的颜色 / 字号 / 间距 / 圆角 / 阴影 / 动效约束之所以重要，是因为我们要保证 DOM 在 Inspect 检查时仍能被识别为 TFDS 组件。**Inspect 契约不达标，Token 用得再准也没用**（AI 会被判定为“手搓伪组件”）。

TFDS 组件不只是视觉样式，也必须在 DOM 上保留可检查的组件身份。平台 npm 生成的页面，在浏览器 DevTools / Inspect / 检查元素悬浮提示中，应能通过 `.tfds-*` 专有类名或 `data-tfds-component` 判断该区域是否真正调用了 TFDS 组件。

### 0.1 组件根节点

- 所有已存在的 TFDS 组件必须通过组件 import 调用，根节点应保留 `.tfds-*` 类名，例如 `.tfds-tag`、`.tfds-button`、`.tfds-input`、`.tfds-card`、`.tfds-table`、`.tfds-chat-input`。
- 页面模板根节点必须保留 `.tfds-page-*` 类名，例如 `.tfds-page-base-frame`、`.tfds-page-customer-service-workspace`、`.tfds-page-chat-home`、`.tfds-page-im-conversation`。
- 子结构可同时使用 `data-tfds-component="Component.Part"` 标识更细层级；自动化或人工检查时，`.tfds-*` 与 `data-tfds-component` 都是合法 TFDS 身份标识。

### 0.2 禁止伪组件

- 如果 `components.index.json` 中已有对应组件，禁止用裸 `div/span/button/input/textarea/select` + Tailwind class 模拟该组件；这种写法在 Inspect 中不会出现 `.tfds-*` 标识，视为没有严格执行 TFDS 组件规范。
- 禁止仅复制颜色、圆角、字号、间距后冒充组件，例如 `<span className="rounded bg-...">标签</span>` 不能替代 `<Tag />`，`<button className="...">` 不能替代 `<Button />`。
- 自定义区块只有在组件库确实没有覆盖时才允许使用普通 DOM；即便如此，也必须使用 TFDS token，并优先给业务根节点补充清晰的 `data-tfds-component` 或业务语义 class，方便检查来源。

---

## 1. 颜色 Token

### 1.1 中性色（结构色 = blueGrey 系，11 档）


| 用途   | CSS 变量                                        | 实色                    | 适用场景               |
| ---- | --------------------------------------------- | --------------------- | ------------------ |
| 主文字  | `var(--color-foreground)`                     | `#182230`             | 标题、正文主信息           |
| 次文字  | `var(--color-foreground-secondary)`           | `#475467`             | 描述、辅助说明            |
| 弱文字  | `var(--color-foreground-muted)`               | `#667085`             | 时间戳、计数、placeholder |
| 禁用文字 | `var(--color-foreground-disabled)`            | `#98A2B3`             | 禁用态文字              |
| 反色文字 | `var(--color-foreground-inverse)`             | `#FFFFFF`             | 深底/品牌底上的文字         |
| 页面底色 | `var(--color-blueGrey-200)`                   | `#F2F4F7`             | 整页最外层灰底            |
| 弱底色  | `var(--color-blueGrey-100)`                   | `#F9FAFB`             | hover 背景、禁用背景、二级容器 |
| 卡片白底 | `var(--color-surface)` / `var(--color-white)` | `#FFFFFF`             | 业务白卡               |
| 默认描边 | `var(--color-border-default)`                 | `#E4E7EC`             | 输入框、卡片描边           |
| 强描边  | `var(--color-border-strong)`                  | `#D0D5DD`             | hover/focus 描边     |
| 细线   | `var(--color-border-default)`              | `#E4E7EC` | 列表分隔线、表格行线         |


⛔ **禁止**：

- ❌ 写死十六进制：`#000`、`#333`、`#ccc`、`#fff`
- ❌ Tailwind 任意值：`text-[#000]`、`bg-[#fafafa]`、`border-[#e0e0e0]`
- ❌ Tailwind 默认 gray：`border-black`、`text-black`、`bg-gray-`*、`text-gray-`*（gray ≠ blueGrey，色相偏冷蓝）


| ❌ Bad                                          | ✅ Good                                                                                           |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `<div className="text-black border-gray-300">` | `<div style={{ color: 'var(--color-foreground)', borderColor: 'var(--color-border-default)' }}>` |
| `<p className="text-[#666]">`                  | `<p style={{ color: 'var(--color-foreground-secondary)' }}>`                                     |
| `<span className="bg-gray-100">`               | `<span style={{ background: 'var(--color-blueGrey-100)' }}>`                                     |


### 1.2 品牌色（brand 系，主操作 / 主链接）


| 角色                 | 变量                                                | 实色        |
| ------------------ | ------------------------------------------------- | --------- |
| 品牌主色               | `var(--color-primary)` / `var(--color-brand-500)` | `#56D3BC` |
| 品牌强调（hover/active） | `var(--color-brand-600)`                          | `#32C4AC` |
| 品牌深色（链接、文本品牌）      | `var(--color-brand-700)`                          | `#18B49D` |
| 品牌浅底（tag/标签底）      | `var(--color-brand-50)`                           | `#EAFAF6` |


**品牌色仅用于**：主行动按钮（Button primary）、链接（`<Button variant="text-brand" />`）、品牌 Tag、Avatar AI 渐变。

⛔ **禁止**：用品牌色当普通分割线、装饰性边框、整页背景、图标（除了 hiai logo）。

### 1.3 语义色（状态反馈，4 档）


| 角色  | 变量                     | 用途         |
| --- | ---------------------- | ---------- |
| 成功  | `var(--color-success)` | 上线、通过、成功提示 |
| 警告  | `var(--color-warning)` | 风险、待办      |
| 危险  | `var(--color-danger)`  | 删除、错误、阻塞   |
| 信息  | `var(--color-info)`    | 中性信息       |


⛔ **禁止**：

- 用语义色当背景大面积铺设（除非 Toast/Alert，已有组件）
- 状态徽标手写：`<span className="bg-red-100 text-red-600">失败</span>` ❌ → 必须 `<Tag variant="danger">失败</Tag>` ✅

### 1.4 渐变色 Token（AI / 智能能力专用）

> **⚡ TL;DR 决策线（先答完三句话再决定能不能用）**
>
> 1. **当前模块是否明确属于 AI 能力**（AI 推荐 / 智能生成 / 自动分析 / AI 辅助 / AI 入口 / AI 标识）？→ **否：一律不使用 `var(--gradient-ai-*)`**，回到 §1.1 中性色 / §1.2 品牌色。
> 2. 是的话，**这块面积是大背景、还是边框、还是小图标 / 小文字**？
>    - 大背景 → 只能 `ai-fill-1`（弱感知）
>    - 边框 → `ai-fill-2`（中弱）
>    - 小图标 / 小元素 → `ai-fill-3`（强）
>    - AI 标识文字 → `ai-fill-text` + `background-clip: text`
> 3. **是否在密集内容区**（表格 / 表单 / 日志 / 代码 / 长文本编辑器）？→ **是：禁止铺渐变背景**，密集内容必须保持白底（详见 §10.5）。
>
> ⛔ 三个最常见的非 AI 误用：把 `ai-fill-1` 当“好看的浅渐变背景”铺普通卡片；把 `ai-fill-text` 当“好看的标题颜色”给非 AI 标题；用 `ai-fill-3` 做大面积按钮 / 卡片背景。

渐变色是 TFDS 中表达 AI 能力的专属视觉语言，用来提示“智能推荐、自动生成、智能分析、AI 辅助、自动化洞察”等能力。它不是普通装饰色，不能为了丰富画面而随意使用。生成自定义区块时，必须优先使用 `var(--gradient-*)` 变量，禁止重新手写相似 `linear-gradient(...)`。

#### 1.4.1 使用原则

- **语义先行**：只有内容、入口或状态明确与 AI / 智能能力相关时，才能使用 AI 渐变。
- **层级递进**：背景用 `ai-fill-1`，边界用 `ai-fill-2`，图标和局部高强调元素用 `ai-fill-3`，AI 文字统一用 `ai-fill-text`。
- **弱背景、强点缀**：大面积区域只能使用低感知的 `ai-fill-1`；高饱和的 `ai-fill-3` 只能用于小面积图形。
- **内容可读**：正文、表单、表格、日志、代码等高密度内容区不得铺高饱和渐变；文字必须保持足够对比度。
- **一致调用**：所有 AI 渐变必须通过 token 调用，避免出现相似但不一致的自定义渐变。

#### 1.4.2 Token 语义表

| Token | CSS 变量 | UX 语义 | 视觉强度 | 适用场景 | 禁用场景 |
| --- | --- | --- | --- | --- | --- |
| dark | `var(--gradient-dark)` | AI 深色品牌氛围层 | 强 | 深色 AI Hero、AI 封面、智能能力主视觉、深色空状态 | 普通业务卡片、密集表单、表格背景、浅色页面的局部小容器 |
| ai-fill-1 | `var(--gradient-ai-fill-1)` | AI 弱感知背景层 | 弱 | AI 类背景色、AI 推荐卡片背景、AI 提示区、智能建议列表、生成中结果容器、AI Tag 底色 | 边框、图标、文字、主按钮、大面积页面底色之外的非 AI 区块 |
| ai-fill-2 | `var(--gradient-ai-fill-2)` | AI 轻量边界层 | 中弱 | AI 类浅色边框、卡片渐变描边、AI 区块 hover 边缘高光、输入框 AI 能力边界 | 大面积背景、文字颜色、图标填充、强行动按钮 |
| ai-fill-3 | `var(--gradient-ai-fill-3)` | AI 高强调元素层 | 强 | AI 相关图标、小圆点、头像光点、按钮内小图形、生成/刷新图标、激活态小元素 | 大面积背景、表单/表格容器、长文本背景、普通装饰插画 |
| ai-fill-text | `var(--gradient-ai-fill-text)` | AI 文字强调层 | 中强 | 所有 AI 色文字字体颜色：AI 标识、AI 推荐、智能生成、自动分析、AI 能力标题或短强调文案 | 普通标题、普通链接、正文段落、非 AI 文案、长段落文字 |

#### 1.4.3 渐变值定义

| Token | CSS 定义 |
| --- | --- |
| dark | `linear-gradient(74deg, rgba(134,255,197,0.4) 1.5%, transparent 50%), linear-gradient(250deg, rgba(255,158,44,0.28) 11%, transparent 52%), linear-gradient(101deg, #34423B 14%, #16141F 104%)` |
| ai-fill-1 | `linear-gradient(90deg, rgba(230, 247, 244, 1) 0%, rgba(239, 246, 255, 1) 55%, rgba(243, 245, 255, 1) 90%, rgba(252, 243, 255, 1) 100%)` |
| ai-fill-2 | `linear-gradient(-45deg, rgba(254, 224, 253, 1) 0%, rgba(233, 218, 255, 1) 25%, rgba(213, 225, 255, 1) 48%, rgba(213, 243, 248, 1) 83%, rgba(213, 247, 242, 1) 100%)` |
| ai-fill-3 | `linear-gradient(-45deg, rgba(255, 153, 248, 1) 0%, rgba(181, 131, 255, 1) 25%, rgba(114, 156, 255, 1) 48%, rgba(117, 218, 231, 1) 83%, rgba(115, 230, 204, 1) 100%)` |
| ai-fill-text | `linear-gradient(135deg, rgba(22, 218, 203, 1) 0%, rgba(49, 134, 212, 1) 20%, rgba(111, 88, 227, 1) 70%, rgba(231, 116, 221, 1) 100%)` |

#### 1.4.4 推荐组合

- **AI 推荐卡片**：`ai-fill-1` 做背景，`ai-fill-2` 做 1px 渐变边框，标题中的“AI 推荐”用 `ai-fill-text`。
- **AI 输入能力入口**：普通输入框保持白底，AI 状态或 hover 边界可用 `ai-fill-2`，右侧 AI 图标用 `ai-fill-3`。
- **AI 状态标签**：Tag 背景优先使用 `ai-fill-1`，短文案可使用 `ai-fill-text`，不要把整个 Tag 做成高饱和 `ai-fill-3`。
- **AI 图标按钮**：按钮容器仍遵循 Button 组件规范，按钮内小图标或光点可用 `ai-fill-3`。
- **AI 深色主视觉**：只有 Hero、封面、空状态主视觉可使用 `dark`，并配合白色或反色文字。

#### 1.4.5 标准示例

**AI 背景色（ai-fill-1）**：

```jsx
<div
  style={{
    background: 'var(--gradient-ai-fill-1)',
    borderRadius: 'var(--radius-lg)',
  }}
>
  AI 推荐内容
</div>
```

**AI 浅色边框（ai-fill-2）**：

```jsx
<div
  style={{
    border: '1px solid transparent',
    background:
      'linear-gradient(var(--color-surface), var(--color-surface)) padding-box, var(--gradient-ai-fill-2) border-box',
    borderRadius: 'var(--radius-lg)',
  }}
>
  AI 辅助卡片
</div>
```

**AI 图标 / 小元素（ai-fill-3）**：

```jsx
<span
  aria-hidden
  style={{
    display: 'inline-block',
    width: 16,
    height: 16,
    background: 'var(--gradient-ai-fill-3)',
    borderRadius: 'var(--radius-full)',
  }}
/>
```

**AI 文字字体颜色（ai-fill-text）**：

```jsx
<span
  style={{
    background: 'var(--gradient-ai-fill-text)',
    WebkitBackgroundClip: 'text',
    backgroundClip: 'text',
    WebkitTextFillColor: 'transparent',
  }}
>
  AI 推荐
</span>
```

#### 1.4.6 反例与修正

| ❌ Bad | ✅ Good |
| --- | --- |
| `<div style={{ background: 'var(--gradient-ai-fill-3)' }}>表格</div>` | 表格保持 `var(--color-surface)`，仅 AI 入口图标使用 `var(--gradient-ai-fill-3)` |
| `<span style={{ color: 'var(--gradient-ai-fill-text)' }}>AI 推荐</span>` | 使用 `background: var(--gradient-ai-fill-text)` + `backgroundClip: 'text'` |
| `<div style={{ background: 'linear-gradient(90deg, #E6F7F4, #FCF3FF)' }}>` | `<div style={{ background: 'var(--gradient-ai-fill-1)' }}>` |
| 普通业务标题使用 `ai-fill-text` | 只有 AI 相关短文案使用 `ai-fill-text`，普通标题用 `var(--color-foreground)` |
| 卡片背景和图标都用 `ai-fill-3` | 卡片背景用 `ai-fill-1`，图标小面积用 `ai-fill-3` |

⛔ **禁止**：

- ❌ 自己硬写 `linear-gradient(...)` 复刻 AI 渐变；应改用 `var(--gradient-ai-fill-*)` 或 `var(--gradient-dark)`。
- ❌ 把 AI 渐变当普通装饰色铺满密集内容区（表格、表单、日志、代码、长文本编辑器）。
- ❌ 用 `ai-fill-3` 做大面积背景；它只适合图标、按钮图形、激活态小元素。
- ❌ 用普通 `color: var(--gradient-ai-fill-text)` 设置文字渐变；必须配合 `backgroundClip: 'text'` 与 `WebkitTextFillColor: 'transparent'`。
- ❌ 为非 AI 场景使用 AI 渐变，避免用户误解该模块具备智能能力。

---

## 2. 字体 Token

### 2.1 字号（共 9 档，全部列出）


| Token         | 实际值             | Tailwind 等价 | 用途                             |
| ------------- | --------------- | ----------- | ------------------------------ |
| `--text-xs`   | 12px / 0.75rem  | `text-xs`   | 时间戳、计数、辅助说明、Tag size=s         |
| `--text-sm`   | 14px / 0.875rem | `text-sm`   | **正文默认**、表单 label、按钮文案         |
| `--text-base` | 16px / 1rem     | `text-base` | 强调正文、`FormTitle level-1`、卡片摘要  |
| `--text-lg`   | 18px / 1.125rem | `text-lg`   | 少量强调信息，非 `FormTitle` 主路径       |
| `--text-xl`   | 20px / 1.25rem  | `text-xl`   | `FormTitle form`（页面总标题 / 工作台名） |
| `--text-2xl`  | 24px / 1.5rem   | `text-2xl`  | 页面 hero 标题、KPI 大数              |
| `--text-3xl`  | 30px / 1.875rem | `text-3xl`  | 营销/空状态主标题                      |
| `--text-4xl`  | 36px / 2.25rem  | `text-4xl`  | 数据可视化大数（极少用）                   |
| `--text-5xl`  | 48px / 3rem     | `text-5xl`  | 营销首屏 hero（B 端基本不用）             |


⛔ **禁止**：

- ❌ 任意 px：`text-[15px]`、`text-[17px]`、`text-[22px]`
- ❌ 直接使用 `<h1>/<h2>/<h3>` 浏览器默认样式（会注入 `font-weight: bold` + 不可控 size）
- ✅ 优先：`<FormTitle />`；只有非组件化排版示意时，才显式写 `<h2 className="text-base leading-6 [font-weight:var(--font-semibold)]">`（⛔ 不要写裸 `font-semibold` / `font-bold` 类承载规范字重，见 §2.2）

### 2.2 字重（共 4 档）与显式 `font-weight`（Tailwind v4）

B 端主路径的**标题与密集区强调文案**默认以 **600（`--font-semibold`）** 为同一视觉层级；`700（--font-bold）` **仅**在个别组件 props / rules 已约定时使用（如部分 `Tag` `fontWeight="bold"`）。**与 `FormTitle` / `Form` / `ConversationList` / `Button` 等实现不一致的硬编码字重一律视为错误。**

在 **Tailwind v4** 下，`font-semibold` / `font-bold` 等语义类可能把 `--font-`* 命名空间与 **font-family** 语义搅在一起，导致字重表现不稳定。TFDS 约定：凡是必须符合设计规格的字重，在 className 中优先写 **任意属性显式绑定**：

- **600**：`[font-weight:var(--font-semibold)]`（必要时强制：`![font-weight:var(--font-semibold)]`）
- **700**：`[font-weight:var(--font-bold)]`

**分档速查（与当前 `@tfdesign/b-end` 实现一致）**


| 语义档           | Token             | 数值  | 典型落点                                                                                           |
| ------------- | ----------------- | --- | ---------------------------------------------------------------------------------------------- |
| 正文 / 描述       | `--font-normal`   | 400 | `font-normal` 或组件内定                                                                            |
| 次级强调          | `--font-medium`   | 500 | 表格数据主列、部分 Tag、说明性标签                                                                            |
| **默认「标题级」强调** | `--font-semibold` | 600 | `**FormTitle` 全变体**、`Form` 字段标题 / 必填星号 /「可选」、`ConversationList` 会话项标题、`Button` 全变体、`Tabs` 选中态等 |
| 更强一档（组件约定）    | `--font-bold`     | 700 | **仅**命中组件 rules / props 时（勿用来手搓替代 `FormTitle`）                                                 |



| Token（设计语义）       | 值   | 运行时推荐 class 片段                       |
| ----------------- | --- | ------------------------------------ |
| `--font-normal`   | 400 | `font-normal`（正文）                    |
| `--font-medium`   | 500 | `font-medium`                        |
| `--font-semibold` | 600 | `[font-weight:var(--font-semibold)]` |
| `--font-bold`     | 700 | `[font-weight:var(--font-bold)]`     |


⛔ **禁止**

- 在已有 `FormTitle` / `Form` / `ConversationList` 的场景，用 `<h1|h2|span className="font-bold|font-semibold">` 冒充组件字重
- 给 `FormTitle` 传 `className` / `style` 覆写 `font-`*（见 `components.index.json` 铁律）


| ❌ Bad                                                         | ✅ Good                                                                                     |
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `<h2 className="font-bold text-lg">总览</h2>`                   | `<FormTitle variant="form" title="总览" />`                                                  |
| `<div className="font-bold">用户名</div>`                        | `<div className="font-medium">用户名</div>`                                                   |
| `<span className="font-semibold">小标题</span>`（在须走 TFDS 字重的业务区） | `<FormTitle variant="level-2" title="小标题" />` 或组件已规定的 `[font-weight:var(--font-semibold)]` |


### 2.3 行高 Token（强约束）


| Token                       | 值     | 适用                             |
| --------------------------- | ----- | ------------------------------ |
| `--leading-4`               | 16px  | 紧凑：12px 字号配 16px 行高（Tag、Badge） |
| `--leading-5`               | 20px  | 紧凑正文：14px 字号配 20px 行高          |
| `leading-tight`（Tailwind）   | 1.25  | 标题（18-24px 字号）                 |
| `leading-normal`（Tailwind）  | 1.5   | 正文段落（默认）                       |
| `leading-relaxed`（Tailwind） | 1.625 | 描述性长文本                         |


⛔ **禁止**：

- ❌ `leading-[1.4]`、`leading-[28px]` 等任意值
- ❌ 段落不写行高（浏览器默认 1.2 太挤）

**行高黄金组合**：

- 12px 字号 → `leading-4`（16px）
- 14px 字号 → `leading-5`（20px）或 `leading-normal`
- 16-18px 字号 → `leading-6`（24px）或 `leading-normal`
- 20-24px 字号 → `leading-tight` 或 `leading-7`/`leading-8`

---

## 3. 间距 Token（共 13 档）

> **⚡ 高频误用 P0 决策线（写任何 `gap-*` / `space-y-*` 之前先回答）**
>
> 1. **父容器 `flex` 方向是什么**？
>    - `flex-row`（**横向**并列分栏 / 多面板 Playground / 同行多白卡） → **8px** (`gap-2`)
>    - `flex-col`（**纵向**叠放板块：白卡 ↔ 白卡 / 白卡 ↔ Header / 白卡 ↔ KPI 区 / 白卡 ↔ Tabs / 白卡 ↔ 筛选行） → **16px** (`gap-4`)
> 2. 同一容器同时存在横向 + 纵向板块时，**两个值都要写**——内层 `flex-row gap-2`、外层 `flex-col gap-4`，不要互相借用。
> 3. ⛔ 最常见错配：纵向板块栈错用 `gap-2`（板块挤在一起，看起来像同一个组件）。
> 4. ⛔ 次常见错配：把白卡内部 `p-6`（24px）误用到 `main` 外框 padding（详见本节表末和 §3 最后的“最高频误用”警告）。
>
> AI 入口页（`ChatHomePagePattern`）例外：右侧 Hero / `ChatInput` / Tabs / 模板卡网格直接坐灰底，不套主内容白卡。

| Token           | 值    | Tailwind        | 推荐用途                           |
| --------------- | ---- | --------------- | ------------------------------ |
| `--spacing-0`   | 0px  | `gap-0`         | 无间距                            |
| `--spacing-0_5` | 2px  | `gap-0.5`       | 极紧凑（图标内部、徽标）                   |
| `--spacing-1`   | 4px  | `gap-1`         | 单行 Tag 之间、icon-text 间距         |
| `--spacing-2`   | 8px  | `gap-2`         | **同组元素默认值**、横向并列白卡之间       |
| `--spacing-3`   | 12px | `gap-3`         | 跨小组件间（按钮组合）                    |
| `--spacing-4`   | 16px | `gap-4`         | **表单字段之间**、卡内行块、**纵向叠放板块之间（白卡 ↔ 白卡 / 白卡 ↔ Header / 白卡 ↔ KPI / 白卡 ↔ Tabs / 白卡 ↔ 筛选行）**                |
| `--spacing-5`   | 20px | `gap-5`         | 中等分区                           |
| `--spacing-6`   | 24px | `gap-6` / `p-6` | **白卡内 padding 默认值**、Section 之间 |
| `--spacing-7`   | 28px | `gap-7`         | 较大分区（少用）                       |
| `--spacing-8`   | 32px | `gap-8`         | 大区块间隔                          |
| `--spacing-10`  | 40px | `gap-10`        | 页头底部留白                         |
| `--spacing-12`  | 48px | `gap-12`        | hero 区高度                       |
| `--spacing-16`  | 64px | `gap-16`        | 大型空状态留白                        |


⛔ **禁止任意值**：`p-[14px]`、`gap-[7px]`、`space-y-[18px]`、`mt-[10px]`。
✅ **取整数档**：设计稿 14px → 向上取 16px (`gap-4`)，11px → 取 12px (`gap-3`)。

**白卡分区间距黄金组合**（按"横向并列 / 纵向叠放"区分，绝不可混用）：


| 位置                | 值           | Tailwind          | 用法                                |
| ----------------- | ----------- | ----------------- | --------------------------------- |
| 通用灰底页面（main）→ 白卡外边缘 | **16px**    | `p-4`             | 非基础页面框架的 `main` 容器 padding，白卡距浏览器边缘 16px |
| 基础页面框架普通入口 → 右侧白卡 | **16 / 16 / 16 / 0** | `PAGE_FRAME_CONTENT_MARGIN` | 左侧 NavBar 与右侧白卡之间不额外加 16px 灰底空隙 |
| 基础页面框架页面示例3 → 内容白卡 | **0 / 16 / 16 / 0** | `PAGE_FRAME_CONTENT_WITH_TOPBAR_MARGIN` | 顶部 Tab 已占位，不再额外加顶部和左侧间距 |
| **横向并列**：白卡 ↔ 白卡（同行多白卡分栏 / 多面板 Playground） | **8px** | `gap-2` | 父容器 `flex-row` 时使用，仅同组协作场景 |
| **纵向叠放**：白卡 ↔ 白卡 / 白卡 ↔ 其他板块组件（Header / Pill Tabs / 筛选行 / KPI 区 / 段落区）| **16px** | `gap-4` | 父容器 `flex-col` 时使用；上下两个独立 UI 板块默认 16px |
| 白卡边缘 → 卡内内容       | **24px**    | `p-6`             | 写在白卡 div 内部的 padding              |
| 白卡内分区之间           | 16px 或 24px | `gap-4` / `gap-6` | 白卡内 section 之间                    |
| 表单字段之间            | 16px        | `gap-4`           |                                   |
| 同组按钮之间            | 8px         | `gap-2`           |                                   |
| 跨组件之间             | 12px        | `gap-3`           |                                   |


⚠️ **最高频误用**：把白卡内部的 `p-6`（24px）误用到通用 `main` 外框 padding → 白卡距浏览器边缘变成 24px，左右出现大空白，看着像居中布局。通用灰底页面 `main` 默认 `p-4`；基础页面框架 / 菜单栏入口必须使用 `pageListShared.jsx` 的 `PAGE_FRAME_CONTENT_MARGIN / PAGE_FRAME_CONTENT_WITH_TOPBAR_MARGIN`，不要给 NavBar 右侧额外加左 padding。详见 LAYOUT_RULES §1.2。

⚠️ **AI 入口页例外**：`ChatHomePagePattern` 不套“主内容白卡”，右侧 Hero / `ChatInput` / Tabs / 搜索 / 模板卡网格直接坐 `--color-blueGrey-200` 灰底；只有模板卡片自身使用 `<Card color="white" />`。

### 3.0 组件对齐与微间距默认值

这些值用于组件库没有提供完整区块时的自定义组合；先判断页面 recipe，再套下面的微间距：

| 场景 | 默认间距 / 对齐 | 说明 |
| --- | --- | --- |
| PageMetaBar 左标题 + 右操作 | `items-start justify-between gap-3` | 标题 `min-w-0 flex-1`，右侧 `shrink-0 flex-wrap gap-2` |
| 白卡 Header | `px-6 py-4`，标题到正文 16px | 标题用 `FormTitle`，状态 Tag 放 `titleSuffix` |
| 筛选栏 | 同组 `gap-2`，跨组 `gap-3`，整行可 wrap | 搜索 240px，状态 / 枚举 160-200px，控件高度 36px |
| 表单字段 | `gap-4` | 单列主字段默认 `w-full`，不要套筛选栏固定宽 |
| 卡片网格 | `gap-4` | 模板 / 入口卡片直接坐灰底时用 `Card color="white"` |
| 白卡内轻分组 | `gap-3` 或 `gap-4` | 父级白底时内部卡片用 `Card color="grey"` 或轻量 InlinePanel |

⛔ 禁止为了“刚好对齐”新增 10px / 14px / 18px 这类任意值；设计稿落在中间值时取最近的 token 档。

### 3.1 表单控件宽度与对齐铁律

> **本节是 Token 层铁律**（决定“控件该不该撑满”）。**完整决策树**（按场景：主表单 / 抽屉 / 弹窗 / 侧栏 / 筛选栏 / 工具条 / 表格行内等）以 `LAYOUT_RULES.md §6.3 表单控件宽度决策` 为准。两份文档冲突时**以 `LAYOUT_RULES.md §6.3` 为准**。

页面主表单、白卡表单、抽屉 / 弹窗正文表单、侧栏详情表单中的所有可输入控件默认必须撑满字段内容列。

- ✅ `Form` 外层：`w-full min-w-0 self-stretch`
- ✅ 字段列：`w-full min-w-0 max-w-full`
- ✅ 控件本体：`Input` / `Select` / `TextArea` / `InputNumber` / `DatePicker` / `TimePicker` / `TagInput` 默认使用 `w-full min-w-0 max-w-full`
- ✅ `labelPosition="left"`：标签列固定 96px，右侧控件列必须 `flex-1 min-w-0 w-full`
- ✅ 同一表单区域内，所有输入控件的左右边界必须对齐；不要让 `Input` 满宽、`Select` 只有 240px、`DatePicker` 又是 300px
- ✅ 自定义 `children` 或 `item.control` 时，外层必须补 `className="w-full min-w-0 max-w-full"`，不能绕过 `Form` 的宽度约束
- ✅ 只有筛选栏 / 工具条 / 紧凑查询区允许固定宽：搜索 240px，枚举 / 状态选择 160-200px；禁止把这些固定宽套到业务表单正文
- ⛔ 禁止在表单正文控件上写 `w-[160px]` / `w-[200px]` / `w-[240px]` / `w-[300px]` / `max-w-sm` / `inline-flex` 固定宽来破坏对齐

```jsx
<section className="flex w-full min-w-0 flex-col gap-4">
  <Form
    className="w-full min-w-0 self-stretch"
    items={[
      { id: 'name', label: '名称', type: 'input' },
      { id: 'type', label: '类型', type: 'select' },
      { id: 'date', label: '生效日期', type: 'date-picker' },
      { id: 'desc', label: '描述', type: 'textarea' },
    ]}
  />
</section>
```

### 3.2 卡片内长内容控件的高度节奏

白卡里如果有“标题 + 长内容控件”（TextArea / JSON / 代码块 / 日志 / 列表），视觉节奏必须是：

- 标题区：自然高度，`shrink-0`，永远稳定，不被滚走
- 内容区：占满剩余高度，`flex-1 min-h-0`
- 主控件：如果承担整张卡片的主要工作区，必须撑满内容区并在控件内部滚动
- 普通字段：仅按 `minRows` / 自然内容高度展示，不强行撑满

**TextArea 主编辑区**分两类：

- System Prompt / Agent 指令 / 模型高级参数 JSON / 请求参数 / 代码参数 / JSON / 代码 / SQL / Markdown / 规则配置：使用 `<TextArea variant="code" fillHeight resize="none" className="flex-1 min-h-0 w-full" />`，左侧行号用于逐行定位。
- 普通长文本 / 工单详情 / 描述编辑：使用 `<TextArea fillHeight resize="none" className="flex-1 min-h-0 w-full" />`。
- 纯数值模型参数（Temperature / Top-P / Max Tokens / 阈值 / 权重）：使用 `<InputNumber />`，不要误用 TextArea。

普通备注字段仍使用 `minRows={3}`。不要把这两类高度策略混用，也不要手搓原生 textarea 或行号列。

---

## 4. 圆角 Token（共 9 档）


| Token             | 值      | 用途                 |
| ----------------- | ------ | ------------------ |
| `--radius-none`   | 0px    | 无圆角（直角/分割线）        |
| `--radius-avatar` | 3px    | 方形头像专用             |
| `--radius-sm`     | 4px    | 极小元素（mini Tag、徽标）  |
| `--radius-md`     | 8px    | **输入框、按钮、小卡片**     |
| `--radius-lg`     | 12px   | ⭐ **白卡默认圆角**、抽屉/弹窗 |
| `--radius-xl`     | 16px   | 大尺寸卡片              |
| `--radius-card`   | 20px   | 大型展示卡（少用）          |
| `--radius-2xl`    | 24px   | 营销卡片               |
| `--radius-full`   | 9999px | 头像、胶囊按钮、Tag pill   |


⛔ **禁止**：`rounded-[10px]`、`rounded-[14px]`、`rounded-[18px]`。圆角只能取上表 9 档。
✅ **白卡默认**：`rounded-[12px]`，与 `var(--radius-lg)` 等价。

---

## 5. 阴影 Token（基础刻度 + 业务语义）


| Token              | 用途                   |
| ------------------ | -------------------- |
| `--shadow-none`    | 无阴影（**白卡默认**）        |
| `--shadow-sm`      | 浮起卡片（hover 微抬起）      |
| `--shadow-DEFAULT` | 微浮（很少用）              |
| `--shadow-md`      | 中等浮层（dropdown）       |
| `--shadow-lg`      | 弹窗、悬浮面板              |
| `--shadow-xl`      | 大型悬浮（picker、cascade） |
| `--shadow-2xl`     | 极少使用                 |
| `--shadow-card`    | 业务语义投影：大型展示卡         |
| `--shadow-list`    | 业务语义投影：链接参数 / 列表块轻投影 |


⛔ **白卡默认无阴影**：TFDS 风格是「灰底分区 + 白卡圆角」承担层级，不靠阴影。
⛔ **禁止**：`shadow-[0_4px_8px_rgba(0,0,0,0.1)]`、`drop-shadow-2xl`。
✅ **只有浮层**（dropdown / dialog / popover / picker）才能加阴影，且由组件自带。
✅ **业务语义投影**（如 `shadow-card`、`shadow-list`）只在设计系统已命名的明确场景中使用，不能把任意业务块都升级成新的 shadow primitive。
✅ **hover 投影仅限可点击对象**：入口卡、列表项、选项卡片、推荐卡片可使用组件约定的 hover 反馈；主工作白卡、表单面板、结果面板、对话区、详情侧栏等结构容器不做整卡 hover、投影或浮起。


| ❌ Bad                                                   | ✅ Good           |
| ------------------------------------------------------- | ---------------- |
| `<Card className="shadow-md">`                          | `<Card>`（去掉阴影）   |
| `<div className="shadow-[0_2px_8px_rgba(0,0,0,0.06)]">` | `<div>`（白卡靠灰底分层） |


---

## 5.5 白卡描边规则（与"无阴影"同等地位的硬约束）

> **⚡ 一句话判别口诀（先看背景色再做事）**
>
> - 背景是 `--color-blueGrey-200`（**灰底**：页面外框 / 应用壳 / 灰底分区）→ **允许**有 `border`（这是页面壳边界，不是白卡）
> - 背景是 `--color-surface`（**白卡**：承载工作区内容的大白卡）→ **绝不**加 `border` / `outline` / `ring` / `shadow`
>
> 这条口诀同时回答 §5.5.4 “与灰底外框的边界（防止 AI 误删）”——先看背景，再判断描边是否合法。

> 白卡的层级感**唯一靠"灰底 vs 白卡的明度差 + 12px 圆角"**承担——加 border 会让明度差被划线覆盖，视觉权重变硬，卡片显得拥挤像旧式 admin 后台。

### 5.5.1 铁律

```
最外层大白卡容器  →  纯白底 + 12px 圆角 + 无 border + 无 shadow
                  ↓
                  内部一切控件 / 子卡的描边保持原样（Input / Select / Tag / Table 行线 / 内嵌灰卡 …）
```

**严格区分两类边界**：

- ⛔ **禁止**给"承载工作区内容的最外层大白卡"加任何 `border` / `outline`
- ✅ **保留**白卡**内部**所有 TFDS 组件自带的描边（Input、Select、TagInput、Tag、Tabs line variant、Table 行分隔线、内嵌 Card color="grey" 等）——这些描边是**控件功能边界**，不是卡片层级边界

### 5.5.2 Bad / Good 对照（图 1 同款误用）


| ❌ Bad                                                                                                                                     | 后果                             | ✅ Good                                                                                               |
| ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------- |
| `<div style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--color-border-default)' }}>` | 白卡被灰线圈死，明度差被覆盖，气息感丢失           | 去掉 `border`：`<div style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}>` |
| `<Card className="border border-border-default">`                                                                                         | 白卡视觉权重变硬，与下面内嵌组件描边互相干扰         | `<Card>`（去掉 className 上的 border）                                                                     |
| `style={{ ..., outline: '1px solid var(--color-border-default)' }}`                                                                       | outline 在选中状态下会和 focus ring 打架 | 去掉 outline                                                                                           |
| `borderColor: 'var(--color-border-default)'` 写在白卡 div 上                                                                                   | 同上                             | 删除该属性                                                                                                |
| `className="ring-1 ring-border-default"`                                                                                                  | ring 是描边的另一种伪装                 | 去掉 ring                                                                                              |


### 5.5.3 例外（极少数）

下面四种场景**允许**有描边，但不属于"最外层大白卡"：

1. **白卡内嵌的灰卡**（`Card color="grey"`）→ 由组件自带浅描边表达"次级容器"，保留
2. **TFDS 组件自带描边**：Input / Select / TextArea / TagInput / Tabs line / Table 行线 / Tooltip → 保留
3. **白卡内部的"分割线"**（`borderTop` / `borderBottom`）→ 用 `var(--color-border-default)` 划行，不是给整张卡加 border
4. **浮层 Dialog / Drawer 自带的轻描边** → 由组件自带，保留

⛔ 以上 4 种之外，**任何**直接给白卡 div 加 `border` / `outline` / `ring` 都视为违规。

### 5.5.4 与"灰底外框"的边界（防止 AI 误删）

⚠️ TFDS 部分 page-pattern 在**灰底页面外框**（不是白卡！）会有一圈 `border: 1px solid var(--color-border-default)`——这是页面预览容器在 TFDS 平台内的展示边界，**和白卡描边不是一回事**：

- 灰底页面外框 → **保留** border（这一层不是白卡，是整个页面壳）
- 白卡（surface 白底 + 12px 圆角，承载工作区内容）→ **绝不**加 border

判别口诀：背景色是 `--color-blueGrey-200`（灰）就允许有 border；背景色是 `--color-surface`（白）就**禁止**有 border。

### 5.5.5 分隔线使用纪律

分隔线只用于表达真实结构边界，不能作为“让页面看起来更细”的装饰。

✅ **允许**：

- 表格表头 / 表格行线
- Modal / Sheet / Drawer 的 header / footer 边界
- 工具条与可滚动正文之间的边界
- Split pane / 可拖拽分栏边界（可见竖线统一 **2px**，引用 `shared/resize-handle.js`；热区通常 8px）
- 白卡内部不同功能区之间的少量 `borderTop` / `borderBottom`

⛔ **禁止**：

- 每个 Card header 都习惯性加一条线
- 用分隔线替代间距和 FormTitle 层级
- 在白卡外边界再套一圈 border / ring
- 为了“精致感”给普通说明区、KPI 块、入口卡片反复加横线

---

## 6. 过渡 / 动效规则

⛔ **禁止**：

- ❌ `transition-all`（性能差，会触发布局重计算）
- ❌ 任意 duration：`duration-[450ms]`、`duration-[280ms]`、`duration-[700ms]`
- ❌ 任意 easing：`ease-[cubic-bezier(...)]`、手写 `ease-in-out` / `ease-out` 替代设计 token
- ❌ 业务里手写 `@keyframes`、三点跳动、`animate-spin` 自绘 loading（见 §6.1）

✅ **必须**：

- 只过渡需要的属性：`transition-colors`、`transition-opacity`、`transition-transform`
- 交互过渡只选四档预设（时长 + 标准缓动一体，事实源 `TOKEN_MOTION_TRANSITION` + `MOTION_EASING_TRANSITION`）：

  | Token | 时长 + 缓动 | 用途 |
  | --- | --- | --- |
  | `--motion-ui-instant` | 100ms · standard | 日历格、选项轻反馈 |
  | `--motion-ui-fast` | 150ms · standard | hover/focus 微变化（主力档） |
  | `--motion-ui-normal` | 200ms · standard | 按钮、Input、Tabs、Card 默认 |
  | `--motion-ui-slow` | 300ms · standard | 侧栏展开、抽屉/大面积布局 |

- Tailwind 写法：`transition-colors tfds-motion-ui-fast`（禁止拆写 duration / easing）
- inline 写法：`transition: opacity var(--motion-ui-fast)`
- 循环动画缓动：`--motion-loop-easing`（`ease-in-out`）；交互过渡缓动已编入 `--motion-ui-*`，勿单独引用
- 循环 loading 速率：`--motion-speed-{600|900|1200|1500|1800|2100}`，经组件 `speed` prop 注入；禁止自绘


| ❌ Bad | ✅ Good |
| --- | --- |
| `className="transition-all duration-300"` | `className="transition-colors tfds-motion-ui-fast"` |
| `className="transition duration-[280ms] ease-in-out"` | `transition: width var(--motion-ui-slow)` |
| `className="transition-opacity duration-300 ease-out"` | `className="transition-opacity tfds-motion-ui-slow"` |

### 6.1 循环动效组件分工（`category=motion`）

> 通过 **图标 / 图形元素** 表达 loading 时，统一走 `IconLoading`（`import { IconLoading } from '@tfdesign/b-end'`）；`Loading` 仅为兼容别名。

| 场景 | 组件 | 说明 |
| --- | --- | --- |
| 空白页、区块居中、按钮旁独立图形、提交等待、行内 pending / Agent 执行中 | `<IconLoading />` | 渐变环图形 loading 统一出口；行内 / 节点 pending 用 `size="sm"`，区块居中默认 `size="md"`；速率用 `speed` 六档 |
| 列表 / 卡片 / 表格等内容结构未就绪 | `<Skeleton preset="…" />` | 扫光骨条占位，保留布局；默认骨条底 `fill-default` |
| Input / Select / TextArea 下方 **AI 推荐**内容面板加载中 | `<AiSuggestionPanelSkeleton />` 或 `<Skeleton preset="ai-suggestion-panel" />` | 骨条填充 **`--gradient-ai-fill-1`**（与已加载 `AiSuggestionPanel` 列表底一致）+ 扫光；禁止灰色骨条 / IconLoading 替代 |
| 文案即 loading 表面（思考中、生成中） | `<TextShimmer>…</TextShimmer>` | 纯文字扫光，不用骨条或图形 spinner |

⛔ 禁止在业务里手写 `@keyframes`、三点跳动、`animate-spin` 自绘环替代上述组件。

---

## 7. 焦点可达性规则（键盘可达性必须）

⛔ **禁止**：

- ❌ 裸 `outline-none` 不补 `:focus-visible` → 键盘用户失明
- ❌ `focus:outline-none` 不加替代焦点样式

✅ **必须**：

- 使用 TFDS 组件（Button/Input/Select 等）—— 焦点环已封装
- 自定义可交互元素必须保留焦点：
  ```jsx
  <button
    className="
      focus:outline-none
      focus-visible:ring-2 focus-visible:ring-offset-2
    "
    style={{
      '--tw-ring-color': 'var(--color-ring)',
    }}
  >
  ```
- 焦点环颜色统一用 `var(--color-ring)`（= `--color-brand-500`）

---

## 8. 文本自适应：换行优先，省略必须配 Tooltip（B 端高频问题）

> **目标**：页面文字不能被卡片、父容器或 `overflow-hidden` 静默裁切。要么完整可读地换行展示，要么有意识地省略，并能通过 TFDS `<Tooltip />` 查看全文。

### 8.1 UX 决策树（先判内容价值，再选展示方式）


| 内容类型                                                 | 推荐策略                                                              | 原因                       |
| ---------------------------------------------------- | ----------------------------------------------------------------- | ------------------------ |
| **用户必须立即读懂的说明**：字段说明、运行状态说明、错误原因、空状态说明，以及明确开启的白卡标题描述 | **完整展示，允许换行**：`whitespace-normal break-words`                     | 这类内容影响理解和操作，不能藏在 hover 里 |
| **短标题 + 辅助描述**：`FormTitle level-2/level-3` 右侧描述      | **默认隐藏**；有板块介绍/场景说明诉求时开启并允许换行                                     | 保持标题稳定，避免普通卡片标题堆叠低价值副标题  |
| **表格单元格 / 文件名 / 对象名 / 资源名**                          | **单行省略 + Tooltip 全文**：`truncate` + `<Tooltip content={fullText}>` | 行高必须稳定，全文是补充阅读           |
| **卡片摘要 / 消息预览 / 列表描述**                               | **2-3 行省略 + Tooltip 或详情入口**：`line-clamp-2/3`                      | 保持列表扫描效率                 |
| **ID / URL / 模型名 / code / JSON**                     | **优先换行或内部滚动**：`break-words whitespace-pre-wrap`；极紧凑处才省略 + Tooltip | 这些内容常包含长英文串，不能撑爆布局       |
| **Tag / Badge / Button 文案**                          | **不换行，保持短文案**；过长必须改文案，不靠 Tooltip 承担主信息                            | 控件尺寸应稳定，长句不应塞进控件         |


### 8.2 禁止静默裁切

⛔ **禁止**让文字被父容器“硬切掉”：


| ❌ Bad                                                             | 问题                         | ✅ Good                                                                                 |
| ----------------------------------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------- |
| `<span className="overflow-hidden">{desc}</span>`                 | 没有换行也没有省略提示，用户读不到全文        | 核心说明：`whitespace-normal break-words`                                                   |
| `<div className="flex overflow-hidden"><span>{desc}</span></div>` | flex 子项无 `min-w-0`，文字被卡片裁掉 | `<div className="flex min-w-0"><span className="min-w-0 break-words">...</span></div>` |
| `<span className="truncate">{name}</span>` 但无 Tooltip             | 省略后没有完整信息路径                | `<Tooltip content={name}><span className="truncate">...</span></Tooltip>`              |
| `FormTitle` 外层被 `overflow-hidden whitespace-nowrap` 包住            | 标题描述被强行截断                  | 外层允许 `min-w-0`，不要给标题区域加 `whitespace-nowrap`                                            |


### 8.3 推荐写法

**核心说明完整展示（明确开启的白卡标题描述 / 字段说明）**：

```jsx
<div className="min-w-0 whitespace-normal break-words text-sm text-foreground-muted">
  所有参数控件改为 TFDS 组件，并保留原始参数结构。
</div>
```

**非核心长文本省略 + Tooltip（表格单元格 / 文件名 / 对象名）**：

```jsx
<Tooltip content={modelName} placement="top">
  <span className="block max-w-full truncate">{modelName}</span>
</Tooltip>
```

**多行摘要（卡片描述 / 消息预览）**：

```jsx
<Tooltip content={summary} placement="top">
  <p className="line-clamp-2 min-w-0 break-words text-sm text-foreground-secondary">
    {summary}
  </p>
</Tooltip>
```

⚠️ **关键陷阱**：`truncate` / `line-clamp-N` 必须父容器有**确定宽度**或 `min-w-0` 才能生效；换行文本也必须有 `min-w-0`，否则 flex 子项会把卡片撑爆或被裁切。


| ❌ Bad（无效）                                                                  | ✅ Good                                                                                                                         |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `<div className="flex"><span className="truncate">{longText}</span></div>` | `<div className="flex min-w-0"><Tooltip content={longText}><span className="block truncate">{longText}</span></Tooltip></div>` |


### 8.4 标题区文字适配（禁止竖排 / 逐字换行）

标题不是普通长文本。白卡标题、面板标题、section 标题是用户识别板块语义的锚点，必须横向可读。

**硬规则**：

- 标题主文本永远不使用 `break-all` / `writing-mode` / `text-orientation` / 过窄固定宽度。
- `FormTitle` 描述文案默认隐藏；只有有板块介绍、场景说明、风险/空状态解释，或明确要求展示副标题时，才写 `description` + `showDescription`。
- 标题区最小可读宽度不低于 `120px`；低于这个宽度时，保持默认无描述或缩短标题文案。
- 标题右侧的 **状态 / 数量 / 身份 Tag** 属于标题语义补充，必须通过 `FormTitle titleSuffix` 紧跟主标题展示，不能放到 header 最右侧操作区。
- 标题右侧的 Tabs、Button、筛选入口、更多菜单才属于“操作区”，可以换行、下移或收起，不能挤压标题。
- `FormTitle` 主标题保持横排；开启后的 description 可以换行，但不能把 title 挤成一字一行。


| ❌ Bad                                                             | 问题               | ✅ Good                                                                                      |
| ----------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------- |
| `<div className="w-4 break-all">原始信息只读</div>`                     | 一字一行，读起来像乱码      | `<FormTitle variant="level-2" title="原始信息" />`                                              |
| `<header className="flex nowrap">标题 + 很多 Tabs</header>`           | Tabs 把标题挤窄       | 标题区 `min-w-[120px] flex-1`，操作区 `shrink-0 flex-wrap`                                         |
| `<FormTitle title="Conversation" /><Tag>0 条消息</Tag>` 且 Tag 被推到最右侧 | Tag 是标题状态，不是操作按钮 | `<FormTitle title="Conversation" titleSuffix={<Tag variant="grey" size="m">0 条消息</Tag>} />` |
| `writing-mode: vertical-rl`                                       | B 端卡片标题不使用竖排     | 保持横向标题，必要时缩短标题或隐藏描述                                                                         |


---

## 9. 全局图标规则

> **本节只回答 “如何 import + 该用什么尺寸”**。**业务语义色映射**（AI / 数据 / 风控 / 客服 / 知识库 → pink / blue / orange / green）以及黑底白标 vs 彩色浅底的风格选择请去 `DESIGN_PRINCIPLES.md §4.5 图标统一语义规则`。

- ✅ **必须**用 `<Icon name="kebab-case" />`（来自 `@tfdesign/b-end`）
- ⛔ **禁止**直接 `import { ChevronDown } from 'lucide-react'` 替代 TFDS Icon（线宽不一致）
- **图标尺寸**：

  | 控件高度        | Icon size | 实际像素 |
  | ----------- | --------- | ---- |
  | 28-32px     | `xs`      | 12px |
  | 36px（默认 md） | `sm`      | 16px |
  | 48px        | `md`      | 20px |
  | 大型展示        | `lg`      | 24px |

- **业务语义色** → 见 `DESIGN_PRINCIPLES.md §4.5.2 图标色系选择`。
- **Card 内图标容器尺寸 / 圆角**（信息卡片1 / 信息卡片2 / 数据卡片）→ 见 `DESIGN_PRINCIPLES.md §4.5.5 图标尺寸与圆角统一`。


---

## 9.5 Tabs 尺寸默认规则（与 Button 相反，必须单独记）

Tabs 是“切换视图”的控件，不是动作按钮；它在内容区需要更轻、更紧凑的节奏。

- ✅ **内容区默认 SM**：所有 4 种 Tabs 变体（`line` / `button` / `pill` / `segment`）只要出现在白卡、面板、卡片、表单分段、筛选维度、Playground 内部，默认都用 `size="sm"`，也可以直接省略 `size`（组件默认即 SM）。
- ✅ **Header 例外**：只有整个平台顶部 header、页面级顶导、产品级一级导航里的 Tabs，才允许根据场景使用 `size="md"` 或 `size="lg"`。
- ⛔ **禁止**内容区默认 `size="md"` / `size="lg"`：这会让卡内切换器视觉权重过高，挤占内容空间。
- ⚠️ 注意：Button 的默认尺寸仍是 MD（36px）；Tabs 的默认尺寸是 SM。二者规则相反，不要混用。

---

## 10. 状态徽标 / Tag 全局规则

- ✅ **任何**状态、分类、计数都必须用 `<Tag />`
- ⛔ **禁止**手写 `<span className="px-2 py-0.5 rounded-full bg-green-100 text-green-700">在线</span>` 假徽标
- 默认 `size="m"`；表格行内极紧凑场景才用 `size="s"`

---

## 10.5 密集内容背景规则

表格、表单、日志、配置、代码、运行结果、长文本编辑器属于“工作内容”，首要目标是扫描和编辑效率。

- ✅ 使用稳定白底、`Card color="grey"`、TextArea code variant 或组件自身指定的填充背景。
- ✅ 需要层级时优先使用 `FormTitle`、间距、表格行线、输入框描边、轻填充，不用装饰背景制造层级。
- ⛔ 禁止把密集内容放在 AI 渐变、透明玻璃、浅彩色装饰、低对比图片或弱对比背景上。
- ⛔ 禁止让密集内容区整卡 hover 浮起；只允许行、按钮、选项、tab 等真实交互对象响应 hover。

---

## 11. 链接（文本品牌色）规则

- ⛔ **禁止**裸 `<a className="text-blue-600 hover:underline" />`
- ✅ **必须**用 `<Button variant="text-brand" size="md">查看模型列表 →</Button>`

---

## 12. 空状态 / 错误页 / 加载中规则

- ✅ 页面、容器、表单、列表、板块本应有内容但为空时，必须优先使用基础组件 `Empty`
- ✅ 无权限 / 建设中整页场景可使用 `NoAccessPage` / `ConstructionPage`；内容结构加载用 `Skeleton`；空白页 / 无结构预览的图形等待用 `IconLoading`
- ⛔ 禁止手写 `暂无数据` 文案块、灰色占位 div、裸 Icon + 文案组合来冒充空状态

```jsx
<Empty
  illustrationType="no-content"
  title="暂无数据"
  description="当前没有可显示的内容"
  primaryLabel="立即创建"
/>
```

**关键约束**：

- 数据为空：`illustrationType="no-content"`
- 搜索 / 筛选无结果：`illustrationType="no-result"`
- 页面不存在：`illustrationType="not-found"`
- 无权限：`illustrationType="no-access"` 或使用页面模板 `NoAccessPage`
- 加载 / 网络失败：`illustrationType="failure"`，可配 `linkText="刷新重试"`
- 功能建设中：`illustrationType="construction"` 或使用页面模板 `ConstructionPage`
- 主行动通过 `primaryLabel` / `secondaryLabel` / `tertiaryLabel` 配置，按钮尺寸由 `Empty` 内部复用 `Button size="md"`

---

## 13. 自定义信息卡 / KPI 块（组件库未覆盖时）

> **⛔ 启用前置门槛**：本节只在 `components.index.json` **确实没有覆盖**当前内容时才能使用。
>
> - 如果是「**摘要 / 趋势 / 工具入口 / 模板入口**」→ 必须用 `<Card type="data" />`，禁用本节兜底写法。
> - 如果是「**身份对象 / 能力本体**」（智能体 / 知识库 / 应用） → 必须用 `<Card type="info" />` 或 `info3`。
> - 如果是「**强行动 CTA 入口**」 → 必须用 `<Card type="info2" />`。
> - 如果是「**具体业务对象**」（订单 / 商品 / 工单附件） → 必须用 `<Card type="product" />`。
> - 详细 Card 五类语义选型见 `DESIGN_PRINCIPLES.md §4.4 Card 五类语义选型（AI 必读）`。
>
> 只有当上面 5 类都不匹配（例如非标 KPI 数字组合、步骤进度卡、装饰性区块），才允许使用以下示例。

如果你必须为「KPI 数字 + 描述」「带图标的功能卡」「步骤进度卡」这类非标元素手写一个区块，**严格遵守**：

```jsx
<div
  style={{
    background: 'var(--color-surface)',
    borderRadius: 'var(--radius-lg)',
    padding: 'var(--spacing-6)',
    display: 'flex',
    flexDirection: 'column',
    gap: 'var(--spacing-2)',
  }}
>
  <div
    style={{
      fontSize: 'var(--text-sm)',
      lineHeight: '20px',
      color: 'var(--color-foreground-secondary)',
      fontWeight: 'var(--font-medium)',
    }}
  >
    总调用量
  </div>
  <div
    style={{
      fontSize: 'var(--text-2xl)',
      lineHeight: '32px',
      color: 'var(--color-foreground)',
      fontWeight: 'var(--font-semibold)',
    }}
  >
    1,284,392
  </div>
  <div
    style={{
      fontSize: 'var(--text-xs)',
      lineHeight: '16px',
      color: 'var(--color-foreground-muted)',
    }}
  >
    较昨日 +12%
  </div>
</div>
```

**关键约束**：

- 任何内联样式都必须走 `var(--token-...)`，禁止裸值
- 卡片之间永远 `gap: 8px`，卡片内 padding 永远 `24px`
- 卡片**不加阴影**，靠灰底+白卡的明度差自然分层
- 字号 / 字重 / 颜色都要走 token

---

## 14. AI 自检 Checklist（生成完整页/区块后必查）


| 检查项         | 失败信号（grep 关键字）                                                                                                                           | 修复                                                                          |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| 颜色硬编码       | `#000`、`#333`、`text-black`、`bg-gray-`、`border-gray-`                                                                                     | 改用 `var(--color-...)`                                                       |
| 任意值字号       | `text-[\dpx]`、`text-[1.`                                                                                                                 | 取上表 9 档                                                                     |
| 错误字重        | 标题用 `font-bold`                                                                                                                          | 改 `font-semibold`                                                           |
| 任意值间距       | `p-[\dpx]`、`gap-[\dpx]`、`m[xytlrb]?-\[`                                                                                                  | 取整数档                                                                        |
| 任意值圆角       | `rounded-[\dpx]`                                                                                                                         | 取 9 档之一                                                                     |
| 白卡阴影        | 白卡 / Card 含 `shadow-md`、`shadow-lg`                                                                                                      | 去掉，靠灰底分层                                                                    |
| 任意值阴影       | `shadow-[`                                                                                                                               | 去掉或用 `shadow-{sm,md,lg}`                                                    |
| **白卡描边**    | 背景为 `--color-surface` 的 div / Card 上出现 `border` / `outline` / `ring` / `borderColor` / `border-border-default` / `border border-border-` | **删除该 border**——白卡靠"灰底 vs 白卡的明度差 + 12px 圆角"承担层级，⛔ 不准加描边。仅卡内组件描边保留（详见 § 5.5） |
| 错误边框        | `border-black`、`border-gray-300`                                                                                                         | 用 TFDS 组件，或 `var(--color-border-default)`                                   |
| 假 Tag       | `<span ... rounded-full ... bg-`                                                                                                         | 改成 `<Tag />`                                                                |
| 裸链接         | `<a className="text-`                                                                                                                    | 改成 `<Button variant="text-brand" />`                                        |
| 单独引入 lucide | `from 'lucide-react'` 单独图标                                                                                                               | 改成 `<Icon name="..." />`                                                    |
| 错误过渡        | `transition-all`                                                                                                                         | 改 `transition-colors` / `transition-opacity` 等                              |
| 失明焦点        | `outline-none` 但未配 `focus-visible:ring`                                                                                                  | 补焦点环                                                                        |
| 截断不生效       | `truncate` 父容器无 `min-w-0`                                                                                                                | 父容器加 `min-w-0`                                                              |
| 任意 leading  | `leading-[\d`                                                                                                                            | 取 token 之一                                                                  |
