docs: add base UI component contract

Define the P1 component APIs, token mappings, and settings-page pilot scope for parallel implementation.
This commit is contained in:
xiaohan.yuan committed 2026-10-18 18:36:44 +08:00
1 parent 3ceae1e983
commit febb28aa6f
1 file changed
+251
+251
View File
@@ -0,0 +1,251 @@
# YCode 界面基础组件规范(2026-10-18)
本文件是第一轮组件统一的接口契约。后续子代理按此实现,不得各写一套。
## 总则
- YCode 自有界面只消费 `--yc-*`;`--wb-*` 只允许出现在恢复模块的兼容层。
- 组件样式只能写 `var(--yc-*)`,组件内部不得写裸 hex、裸 `px`、`!important`。
- 组件不得带外部 margin;间距由调用方决定。
- 每个组件顶层元素必须接受并合并 `className`。
- 第一轮不引入第三方 UI 库,不引入 Tailwind,不引入 `clsx`。
- 组件用显式 variant/size props,禁止调用方拼 `wb-button--*` 类名。
## 命名
```text
--yc-<component>-<property>[-<state>][-<size>]
```
示例:
```text
--yc-button-primary-bg
--yc-button-primary-bg-hover
--yc-button-height-md
--yc-input-height-md
--yc-switch-track-width-lg
```
组件 token 定义在 `src/renderer/src/styles/tokens.css` 或独立的 `src/renderer/src/ui/tokens.css`,二者只能有一处。页面作用域不得重定义。
## 需要补的 L0 基础色
现有 `styles/tokens.css` 缺少原版控件的部分透明度阶。第一轮组件需要补齐:
| token | 浅色 | 深色来源 |
| --- | --- | --- |
| `--yc-black-90` | `rgba(0, 0, 0, 0.9)` | `rgba(255, 255, 255, 0.9)` 由组件层重映射 |
| `--yc-black-75` | `rgba(0, 0, 0, 0.75)` | `rgba(255, 255, 255, 0.75)` 由组件层重映射 |
| `--yc-white-90` | `rgba(255, 255, 255, 0.9)` | 同 |
| `--yc-white-75` | `rgba(255, 255, 255, 0.75)` | 同 |
| `--yc-white-30` | `rgba(255, 255, 255, 0.3)` | 同 |
| `--yc-white-20` | `rgba(255, 255, 255, 0.2)` | 同 |
| `--yc-white-10` | `rgba(255, 255, 255, 0.1)` | 同 |
这些值来自当前 `--wb-palette-*` 实际使用值,不是新设计。
## P1 组件
### Button
接口:
```ts
type ButtonVariant = 'primary' | 'secondary' | 'grey' | 'ghost' | 'link' | 'danger'
type ButtonSize = 'sm' | 'md' | 'lg'
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant
size?: ButtonSize
iconOnly?: boolean
loading?: boolean
iconLeft?: React.ReactNode
iconRight?: React.ReactNode
}
```
当前对应的 `wb-button` 类名到 props 的映射:
| 现类名 | 新写法 |
| --- | --- |
| `wb-button wb-button--primary wb-button--medium` | `<Button variant="primary" size="md">` |
| `wb-button wb-button--secondary wb-button--medium` | `<Button variant="secondary" size="md">` |
| `wb-button wb-button--ghost wb-button--medium wb-button--icon-only` | `<Button variant="ghost" size="md" iconOnly>` |
| `wb-button wb-button--small` | `<Button size="sm">` |
几何值来源(当前 `--wb-button-*`):
| 属性 | sm | md | lg |
| --- | ---: | ---: | ---: |
| height | 24px | 32px | 48px |
| padding-x | 8px | 12px | 32px |
| gap | 2px | 4px | 8px |
| font-size | 12px | 14px | 14px |
| line-height | 20px | 22px | 22px |
| radius | 6px | 8px | 12px |
| icon | 12px | 16px | 16px |
状态值来源:
| 状态 | 浅色来源 | 深色来源 |
| --- | --- | --- |
| primary bg | black-90 | white-90 |
| primary bg hover | black-75 | white-75 |
| primary fg | white-100 | black-90 |
| secondary bg | white-100 | `#262626` |
| secondary bg hover | gray-2 `#f7f7f7` | `#2e2e2e` |
| secondary border | `#dedede` | `#3d3d3d` |
| grey bg | gray-4 `#ebebeb` | `#383838` |
| grey bg hover | gray-5 `#e6e6e6` | `#404040` |
| ghost hover | `#0000000f` | `#ffffff14` |
| link fg | `#1470b4` | `#46a8f0` |
| danger bg | red-7 `#e85c5c` | `#af191a` |
| danger hover | red-6 `#ec7474` | `#a31717` |
深色值来自当前 `capability-center-source.css` 与 `subagents/theme.css` 的已验证闭包;实现时先取现有计算值,不得自创。
### IconButton
`IconButton` 是 `Button` 的 `iconOnly` 语法糖,不单独维护尺寸表。
- 必须有 `aria-label` 或 `title`。
- 默认 `size="md"`、`variant="ghost"`。
- 图标容器固定为对应 size 的 height/width。
### Input
接口:
```ts
type InputSize = 'sm' | 'md' | 'lg'
interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
size?: InputSize
invalid?: boolean
filled?: boolean
prefix?: React.ReactNode
suffix?: React.ReactNode
wrapperClassName?: string
}
```
几何值来源:
| 属性 | sm | md | lg |
| --- | ---: | ---: | ---: |
| height | 28px | 32px | 48px |
| padding-x | 8px | 8px | 12px |
| radius | 6px | 8px | 12px |
| font-size | 12px | 14px | 14px |
状态值:
| 状态 | 浅色 | 深色 |
| --- | --- | --- |
| bg | white-100 | `#242424` |
| border | gray-5 `#e6e6e6` | `#454545` |
| border hover/focus | black-30 `#0000004d` | white-30 `#ffffff4d` |
| filled bg | gray-3 `#f2f2f2` | `#242424` |
| disabled bg | gray-2 `#f7f7f7` | `#171717` |
| placeholder | black-30 | white-30 |
### Switch
接口:
```ts
interface SwitchProps {
checked: boolean
onCheckedChange?(checked: boolean): void
disabled?: boolean
readOnly?: boolean
size?: 'sm' | 'md' | 'lg'
'aria-label': string
className?: string
}
```
几何值来源:
| 属性 | sm | md | lg |
| --- | ---: | ---: | ---: |
| track width | 24px | 26px | 34px |
| track height | 14px | 16px | 20px |
| thumb | 10px | 12px | 16px |
| thumb translate | 8px | 8px | 12px |
状态值:
| 状态 | 来源 |
| --- | --- |
| track bg | gray-5 `#e6e6e6` |
| track bg checked | brand `#00c29a` |
| track bg checked hover | brand-7 `#40d1b3` |
| thumb bg | white-100 |
| focus outline | 2px / offset 2px |
| disabled | 保持几何,降低不透明度 |
### Select
第一轮不重写行为,只统一外观壳。
- 复用 `src/renderer/src/components/source-select/SourceSelect.tsx` 与 `RecoveredSelect`。
- 闭合态必须支持 `value`、`placeholder`、`invalid`、`disabled`、`size`、`loading`。
- 几何沿用 `--wb-select-*`:height 32 / radius 8 / font-size 14 / caret 16。
- 设置页不可用行里的“假 Select”也必须走同一视觉壳,禁止再手写 `wb-select`。
### Dialog / ConfirmDialog
第一轮先统一壳,不改业务流程。
- `Dialog`:标题、内容、footer actions、关闭按钮、遮罩。
- `ConfirmDialog`:`title`、`description`、`confirmLabel`、`cancelLabel`、`danger`、`busy`。
- 几何来源:radius 24、max-width 480/640/960、遮罩 `#00000026` 浅色 / `#0006` 深色。
- 已存在 `task-confirm-dialog.tsx` 与 `wb-modal` 结构,优先抽壳而不是重写。
### Popover / Menu
第一轮统一:
- `Popover`:定位、边界收敛、关闭策略、`role`。
- `Menu` / `MenuItem` / `MenuSeparator`:键盘导航、选中态、危险态。
- 几何来源:radius 16、padding 8、shadow 沿用当前 `--wb-popover-shadow`。
### Segmented / Tabs
- `Segmented`:track padding 2、item gap 4、item radius 6、height 32、active weight 600。
- `Tabs` 用于设置导航、能力中心一级导航;选中态只改颜色与字重,不跳尺寸。
## 设置页试点范围
试点只迁移设置页里由 YCode 自己渲染的控件,不改恢复面板的布局规则。
第一批替换点:
| 文件 | 当前 | 目标 |
| --- | --- | --- |
| `GeneralSettingsPanel.tsx` | 本地 `Switch` / `UnavailableSwitch` | `ui/Switch` |
| `GeneralSettingsPanel.tsx` | `UnavailableSelect` 手写 `wb-select` | `ui/Select` 的只读/禁用态 |
| `GeneralSettingsPanel.tsx` | `wb-button` 打开目录/更改/前往系统设置 | `ui/Button` |
| `GeneralSettingsPanel.tsx` | `wb-input` 路径输入 | `ui/Input` |
| `SettingsDialog.tsx` | 本地 `.btn` 工作空间按钮 | `ui/Button` |
| `ModelSettingsPanel.tsx` | 本地 `Button` / `Field` 包装 | `ui/Button` + `ui/Input` |
不允许改动的部分:
- `source-*-recovered.css` 与任何 REA 生成文件。
- `.general-settings-panel__*` 布局类名,迁移后仍保留外层结构。
- 已确认的 1:1 几何、文案、禁用态与 `aria-disabled` 语义。
## 验收
每个组件落地时必须提供:
1. `npm run typecheck` 通过。
2. 组件自身的最小回归或渲染 fixture。
3. 设置页浅色/深色截图对比,差异必须可解释。
4. `npm run check:tokens` 不因新增文件失败。
5. 报告修改文件、映射了哪些旧类名、未迁移的调用点、剩余风险。
在 `scripts/verify-ui.mjs` 假桥修复前,渲染断言可以由主上下文代跑;组件实现者不得改用其它采集渠道查看 WorkBuddy 原版。