# 组件目录与契约
> 配套:[DESIGN.md](../DESIGN.md)(规则)、[ADDING-A-COMPONENT.md](ADDING-A-COMPONENT.md)(新增流程)、
> [ALRIGHT-DECOMPOSITION.md](ALRIGHT-DECOMPOSITION.md)(参考站实测依据)
状态图例:✅ 已实现 · 🚧 开发中 · 🔜 规划 · ⛔ 不进库(用组合替代)
---
## 1. 组件地图
| 组件 | 层 | 状态 | 一句话 | 关键 props |
| --- | --- | --- | --- | --- |
| `Icon` | L1 | ✅ | 图标字体封装(446 个字形) | `name` |
| `Badge` | L1 | ✅ | 数字角标(18 高,12px) | `count` `tone` `max` |
| `ItemBadge` | L1 | ✅ | 状态角标:置顶 / 付费 / HOT | `tone` |
| `Tag` | L1 | ✅ | 分类与元信息标签(24 高,13px) | `tone` `variant` `shape` `icon` `dot` |
| `Divider` | L1 | ✅ | 分隔线 | `orientation` `variant` `spacing` |
| `Typography` | L1 | ✅ | 标题 / 文字 / 段落(Title / Text / Paragraph) | `level` `tone` `size` `lines` |
| `Button` | L2 | ✅ | 操作按钮(圆角 / 胶囊) | `variant` `size` `shape` `block` `loading` `icon` |
| `IconButton` | L2 | ✅ | 圆形图标按钮(28 / 36 / 44) | `label`(必填) `icon` `variant` `size` `spinning` |
| `Input` | L2 | ✅ | 组合式输入框 | `icon` `clearable` `onClear` `shortcut` `inputRef` |
| `Textarea` | L2 | ✅ | 多行文本(Markdown / 简介) | `rows` `resize` `invalid` |
| `PasswordInput` | L2 | ✅ | 密码输入(锁图标 + 显隐切换) | `icon` `size` `toggle` |
| `Switch` | L2 | ✅ | 即时生效的开关 | `checked` `onChange` `size` `label` |
| `Checkbox` | L2 | ✅ | 复选框(多选 / 确认) | `checked` `onChange` `label` |
| `RadioGroup` | L2 | ✅ | 互斥单选组 | `options` `value` `onChange` `direction` |
| `Select` | L2 | ✅ | 受控下拉选择 | `options` `value` `onChange` `placeholder` |
| `Segmented` | L2 | ✅ | 视图形态切换 | `options` `value` `onChange` `size` `block` |
| `Tabs` | L2 | ✅ | 页内分区切换 | `items` `activeKey` `onChange` `variant` |
| `Menu` | L2 | ✅ | 侧栏两级菜单(可折叠动画) | `items` `activeKey` `onSelect` `itemHeight` |
| `Breadcrumb` | L3 | ✅ | 路径导航 | `items` `separator` |
| `Brand` | L2 | ✅ | 品牌标识:logo / 首字标记 + 名称 | `name` `mark` `logo` `href` `size` `tone` |
| `SearchField` | L2 | ✅ | 搜索框(语义版 Input,对外只给字符串) | `value` `onChange` `variant` `size` `hotkey` |
| `Ticker` | L3 | ✅ | 无限滚动的信息条 | `children` `speed` `paused` `label` |
| `Layout` | L3 | ✅ | 页面骨架:页头 / 侧栏 / 内容 / 页脚 | `header` `sider` `aside` `maxWidth` `gap` `height` |
| `Card` | L3 | ✅ | 白色容器(1px 极浅描边) | `as` `padding` `hoverable` |
| `Cover` | L3 | ✅ | 比例封面卡(2:3 / 4:3 / 16:9 + 标题遮罩) | `ratio` `src` `caption` `lift` `onClick` |
| `Image` | L3 | ✅ | 带比例 / 圆角 / 失败降级的图片 | `ratio` `fit` `radius` `fallback` |
| `FormField` | L3 | ✅ | 表单字段排版(标签 + 控件 + 提示/错误) | `label` `hint` `error` `required` |
| `Grid` | L3 | ✅ | 卡片列表网格 | `columns` `tabletColumns` `mobileColumns` `gap` `as` |
| `Space` | L3 | ✅ | 一维间距容器 | `direction` `gap` `wrap` `align` `justify` |
| `Table` | L3 | ✅ | 只读数据表格 | `columns` `rows` `rowKey` `emptyText` |
| `Notice` | L4 | ✅ | 说明 / 错误提示条 | `tone` `title` `action` |
| `Empty` | L4 | ✅ | 空态 | `text` `action` |
| `Result` | L4 | ✅ | 整块结果反馈 | `status` `title` `description` `actions` |
| `Modal` | L4 | ✅ | 居中弹窗(原生 dialog) | `open` `onClose` `title` `footer` `size` |
| `Drawer` | L4 | ✅ | 侧滑抽屉(原生 dialog) | `open` `onClose` `title` `placement` `width` |
| `Tooltip` | L4 | ✅ | hover / 聚焦文字提示 | `content` `placement` |
| `Popover` | L4 | ✅ | 点击展开的小面板 | `content` `placement` |
| `Toast` | L4 | ✅ | 命令式轻提示 | `toast()` `toast.success()` `` |
| `Progress` | L4 | ✅ | 进度条 | `percent` `size` `tone` `showLabel` |
| `Steps` | L4 | ✅ | 纵向步骤条 | `items` `current` |
| `ScrollTop` | L4 | ✅ | 回到顶部 | `visible` `targetId` `label` |
| `Pagination` | L3 | ✅ | 分页 | `page` `total` `pageSize` `onChange` |
| `Skeleton` | L4 | ✅ | 骨架屏占位 | `variant` `lines` `width` `height` |
| `Spinner` | L4 | ✅ | 加载指示(行内 / 区块) | `size` `label` |
| `Avatar` | L1 | ✅ | 头像 / 封面占位(24 / 36 / 48 / 64) | `src` `fallback` `size` `shape` `tone` `verified` |
规划中的组件见 §5;明确不做的见 §6。
---
## 2. 用哪个?(决策表)
| 场景 | 用它 | 不要 |
| --- | --- | --- |
| 显示数量 | `Badge count={n}` | 用 `Tag` 包数字 |
| 显示「置顶 / 付费 / HOT」 | `ItemBadge tone="sticky \| paid \| hot"` | 用 `Tag` 拼渐变 |
| 分类、标签、元信息 | `Tag`(默认 neutral 浅底) | 自己写带底色的 span |
| 需要强调的分类标签 | `Tag variant="solid"`(实底白字) | 给浅底标签调色值 |
| 连接状态 | `Tag tone="success" dot` | 自绘圆点 |
| 列表为空、搜索无结果 | `Empty`(可带 `action`) | 复用 `Notice` |
| 接口失败、服务不可用 | `Notice tone="error"` | `Empty` |
| 一般说明、引导 | `Notice`(`tone="info"`) | 直接写 p |
| 一块内容 / 一个面板 | `Card`(`padding` 决定内边距) | 自己写白色 div |
| 列表行(应用行、作者行) | 直接 `Space` + 内容 | **套 `Card`**(行不需要底色/边框/阴影) |
| 多列排布 | `Grid` | 手写 `display:grid; repeat(...)` |
| 一维间距 | `Space` | 每个元素写 margin |
| 图标 | `` | 写 `iconfont icon-xxx` 类名 |
| 主操作 | `Button`(一页一个 primary) | 多个 primary 并列 |
| 登录 / 筛选这类主操作 | `Button shape="pill"` | 圆角按钮凑合 |
| 只有图标的操作 | `IconButton label="…"` | `` |
| 密码输入(登录 / 改密) | `PasswordInput`(锁图标 + 显隐切换) | 自己拼 `Input` + 眼睛按钮(容易漏掉切换) |
| 分隔内容块 | `Divider` | `border-top` 手写 |
| 长内容回到顶部 | `ScrollTop` | 自己写 fixed 按钮 |
---
## 3. 现有组件契约
每个组件按固定结构写:用途 / 不该用在哪 / 解剖 / Props / 规则 / 展示页。新增组件照抄这个结构(模板见 §8)。
### 3.1 Icon ✅
- **用途**:把图标字体的字形封装成组件,颜色与字号继承当前文字。
- **不该用在哪**:需要多色图标、需要 SVG 描边可控的场景。
- **解剖**:单个 ``,`aria-hidden="true"`。
- **Props**:`name: IconName`(13 个字形)+ 原生 `` 属性。
- **规则**:图标一律视为装饰;字号在父级用 `font-size` 控制;新增字形走 §7 流程。
- **展示页**:`#icons`
### 3.2 Badge ✅
- **用途**:数量角标(1、18、99+)。
- **不该用在哪**:状态文字(用 `ItemBadge` 或 `Tag`)。
- **解剖**:`inline-flex` 胶囊,18px 高、12px 字,等宽数字。
- **Props**:
| prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `count` | `number` | 必填 | 数字 |
| `tone` | `'danger' \| 'primary' \| 'neutral'` | `'danger'` | 配色 |
| `max` | `number` | `99` | 超过显示 `N+`;`0` 表示不截断 |
- **规则**:不负责定位;压在标题右上角由使用方用 `className` 绝对定位。
- **展示页**:`#marks`
### 3.3 ItemBadge ✅
- **用途**:标题前的状态角标——「置顶 / 付费 / HOT / 草稿」。
- **不该用在哪**:数量(`Badge`)、分类(`Tag`)。
- **解剖**:18px 高、radius 4、12px 加粗白字、红/橙渐变底(参考站 `.item-badge`)。
- **Props**:
| prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `tone` | `'hot' \| 'sticky' \| 'paid' \| 'neutral'` | `'hot'` | hot/sticky 红渐变,paid 橙渐变,neutral 灰底 |
| `children` | `ReactNode` | 必填 | 角标文字(建议 2–4 字) |
- **规则**:只做状态提示,不可点击;和标题并排时用 `Space gap="sm"`,不要加 margin。
- **展示页**:`#marks`
### 3.4 Tag ✅
- **用途**:分类、标签、元信息。
- **不该用在哪**:数量(`Badge`)、状态角标(`ItemBadge`)、可点击筛选(暂无交互态)。
- **解剖**:24px 高胶囊、13px 字,左侧可带图标或状态点;实底变体用于强调。
- **Props**:
| prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `tone` | `'neutral' \| 'primary' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | 语义配色 |
| `variant` | `'soft' \| 'solid'` | `'soft'` | 浅底 / 实底白字 |
| `shape` | `'pill' \| 'rounded'` | `'pill'` | 胶囊 / 小方块(元信息前缀用) |
| `icon` | `ReactNode` | — | 前置图标(如分类图标) |
| `dot` | `boolean` | `false` | 前置 6px 状态点 |
- **规则**:标签是内容宽度;放进纵向 flex 时用 `align-items: flex-start`,否则会被拉满。
- **展示页**:`#marks`
### 3.5 Divider ✅
- **用途**:横向分隔内容块、纵向分隔并排元素。
- **解剖**:`role="separator"` 的普通 div,靠 border 画线。
- **Props**:`orientation: 'horizontal' | 'vertical'`、`variant: 'solid' | 'dashed'`、`spacing: 'sm' | 'md'`。
- **规则**:横向自带上下外边距(唯一带 margin 的基础组件),纵向用于 `Space` 内部。
- **展示页**:`#layouts`
### 3.6 Button ✅
- **用途**:页面操作。
- **不该用在哪**:只有图标(`IconButton`)、纯跳转链接(``)。
- **解剖**:`[icon] [spinner] [label]`,高度 32 / 36,左右内边距 20。
- **Props**:
| prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `variant` | `'primary' \| 'secondary' \| 'outline' \| 'ghost' \| 'danger'` | `'primary'` | 语义 |
| `size` | `'sm' \| 'md'` | `'md'` | 32 / 36px |
| `shape` | `'rounded' \| 'pill'` | `'rounded'` | 6px 圆角 / 胶囊 |
| `block` | `boolean` | `false` | 占满一行 |
| `loading` | `boolean` | `false` | 自动禁用 + `aria-busy` |
| `spinning` | `boolean` | `false` | 图标原地旋转(不换转圈),用于刷新类动作 |
| `icon` | `ReactNode` | — | 文字左侧图标 |
| `…rest` | `ButtonHTMLAttributes` | — | `type` 默认 `"button"` |
- **规则**:一屏一个 `primary`;hover 是 `opacity .8`(参考站同款),不要改背景色;禁用是浅底弱字。
- **展示页**:`#buttons`
### 3.7 IconButton ✅
- **用途**:圆形图标按钮(顶栏入口、更多、刷新、关闭)。
- **解剖**:正圆 + 居中图标;`spinning` 时图标旋转。
- **Props**:`label`(**必填**,进 `aria-label` 与 `title`)、`icon`(必填)、
`variant: 'ghost' | 'soft'`、`size: 'xs' | 'sm' | 'md'`(28 / 36 / 44,图标 16 / 20 / 24)、`spinning`、`…rest`。
- **规则**:没有 `label` 不允许合并;`spinning` 只用于「操作进行中」,不做装饰动画。
- **展示页**:`#buttons`
### 3.8 Input ✅
- **用途**:搜索框、筛选输入、表单单行输入。
- **不该用在哪**:多行文本(未来 `Textarea`)、下拉选择(未来 `Select`)。
- **解剖**:`[icon] [input] [suffix] [shortcut] [clear]`,外层容器负责边框与高度(36px、`1px solid gray-3`)。
- **Props**:
| prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `icon` | `IconName` | — | 左侧图标 |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 32 / 36 / 44px |
| `suffix` | `ReactNode` | — | 右侧插槽:图标按钮、单位等;排在清除按钮之前 |
| `clearable` | `boolean` | `false` | 有值且传了 `onClear` 才显示清除 |
| `onClear` | `() => void` | — | 清除回调 |
| `shortcut` | `string` | — | 右侧提示(如 `⌘K`),仅展示 |
| `inputRef` | `Ref` | — | 拿真实 input 做聚焦 |
| `className` / `style` | — | — | **作用在外层容器** |
| `…rest` | `InputHTMLAttributes` | — | `onChange` 是原生事件 |
- **规则**:与 `Button size="md"` 同高 36;focus 只把边框换成主色(**不再叠 outline**:文本框点击时也会命中 `:focus-visible`,叠环会出现「双框」,2026-10-28 修);`type="password"` 时隐藏 Edge 原生显隐按钮(`::-ms-reveal`),否则会和 `PasswordInput` 的眼睛叠成两个。
- **展示页**:`#inputs`
### 3.9 Card ✅
- **用途**:白色容器(面板、卡片、详情块)。
- **不该用在哪**:列表行、纯排布容器。
- **解剖**:`1px solid gray-1`(几乎不可见)+ radius 12 + `shadow-card`(1%),内边距由 `padding` 决定。
- **Props**:`as: 'div' | 'section' | 'article' | 'aside'`、`padding: 'none' | 'sm' | 'md' | 'lg'`(0 / 12 / 20 / 24)、`hoverable`、`className`、`style`。
- **规则**:卡片不提供标题栏;「色条 + 标题 + 角标 + 更多」由页面组合;`hoverable` 只加深阴影,不做位移。
- **展示页**:`#cards`
### 3.10 Grid ✅
- **用途**:卡片列表的多列排布。
- **不该用在哪**:一维间距(`Space`)、页面整体骨架(CSS layout)。
- **解剖**:`display:grid`,列数走 CSS 变量,断点内建:`>1100`、`≤1100`、`≤720`。
- **Props**:
| prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `columns` | `number` | `4` | 宽屏列数 |
| `tabletColumns` | `number` | `columns / 2`(至少 2) | ≤1100px |
| `mobileColumns` | `number` | `1` | ≤720px |
| `gap` | `'sm' \| 'md' \| 'lg'` | `'md'` | 8 / 12 / 16 |
| `as` | `'div' \| 'ul'` | `'div'` | 列表语义 |
- **规则**:不要在父级用 `flex-direction: column` 包住含比例图片的 Grid(会被压扁,见 DESIGN §9)。
- **展示页**:`#layouts`
### 3.11 Space ✅
- **用途**:一组元素之间的一维间距。
- **解剖**:`display:flex` + `gap`,方向/对齐/间距都用语义档位。
- **Props**:`direction: 'row' | 'column'`、`gap: 'xs' | 'sm' | 'md' | 'lg'`(4 / 8 / 12 / 16)、`wrap`、`align: 'start' | 'center' | 'end' | 'stretch'`、`justify: 'start' | 'center' | 'end' | 'between'`。
- **规则**:间距只用档位,不传像素;纵向排列 + 左对齐要给 `align="start"`。
- **展示页**:`#layouts`
### 3.12 Notice ✅
- **用途**:页面级说明与错误提示。
- **不该用在哪**:表单字段校验、浮层提示(未来 `Toast`)。
- **解剖**:标题 + 正文 + 可选操作按钮;错误态 `role="alert"`。
- **Props**:`tone: 'info' | 'error'`、`title`(必填)、`children`、`action: { label, onClick }`。
- **规则**:错误态只用于「确实失败」;同一区域不要堆两条以上提示。
- **展示页**:`#feedback`
### 3.13 Empty ✅
- **用途**:列表为空、搜索无结果、内容未就绪。
- **解剖**:一句说明 + 可选操作区。
- **Props**:`text`(默认「暂无内容」)、`action`。
- **规则**:空态不要用红色/错误色;能给出下一步操作时一定给。
- **展示页**:`#feedback`
### 3.14 ScrollTop ✅
- **用途**:长内容回到顶部。
- **解剖**:固定在视口右侧中部(`top:50%`、`right:24`)的 42px 圆钮,`visible` 控制淡入淡出。
- **Props**:`visible`(必填)、`targetId`(滚动容器 id;不传滚窗口)、`label`(默认「回到顶部」)、`offset`(距右侧 px,默认 24)。
- **规则**:滚动容器在页面里滚动时必须传 `targetId`;隐藏时 `tabIndex=-1`。
- **展示页**:`#scrolltop`
### 3.15 Avatar ✅
- **用途**:头像、应用图标、封面占位。
- **解剖**:裁切层(图片 / 首字) + 外置认证角标,两者分层,角标不会被圆角切掉。
- **Props**:
| prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `src` | `string` | — | 图片;加载失败自动降级 |
| `alt` | `string` | `''` | 图片替代文本 |
| `fallback` | `string` | `'•'` | 无图时的字符(通常取首字) |
| `icon` | `ReactNode` | — | 无图时的图标(优先于 fallback),用于缺省头像 |
| `size` | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | 24 / 36 / 48 / 64px |
| `shape` | `'circle' \| 'square'` | `'circle'` | 人/应用用圆,图标封面用方 |
| `tone` | `'neutral' \| 'primary'` | `'neutral'` | 无图底色:浅灰 / 品牌浅渐变(资料卡) |
| `verified` | `boolean` | `false` | 右下角认证角标(12 / 16 / 20px) |
- **规则**:`fallback` 传一个字符;资料卡用 `xl` + `tone="primary"`,列表里用 `lg`,紧凑行用 `sm`;不要给头像加边框。
- **展示页**:`#avatars`
### 3.16 Modal ✅
- **用途**:需要打断流程的确认 / 表单 / 说明。
- **不该用在哪**:轻量反馈(等 `Toast`)、页面内的展开收起(用条件渲染 + 动画)。
- **解剖**:原生 `