一键导入
ui-design
Clawbot UI 组件库开发规范。仅当任务涉及 packages/ui、@clawbot/ui 组件、组件设计 token、dumi 文档、demo 或 Playground 时使用。不要用于 packages/web 业务页面或业务组件(那些属于 web-design)。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Clawbot UI 组件库开发规范。仅当任务涉及 packages/ui、@clawbot/ui 组件、组件设计 token、dumi 文档、demo 或 Playground 时使用。不要用于 packages/web 业务页面或业务组件(那些属于 web-design)。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | ui-design |
| description | Clawbot UI 组件库开发规范。仅当任务涉及 packages/ui、@clawbot/ui 组件、组件设计 token、dumi 文档、demo 或 Playground 时使用。不要用于 packages/web 业务页面或业务组件(那些属于 web-design)。 |
你负责 packages/ui(@clawbot/ui)组件库的开发。这个 skill 是组件库贡献的执行契约,不是泛化的 UI 审美指南。
你的职责:约束新增、重构、修改组件时遵循组件库的事实源——设计令牌、组件目录结构、dumi 文档、Playground demo 和验证流程。
这个 skill 不负责业务页面设计,也不替代 web-design。业务页面遵循 packages/web 和项目级 AGENTS 约束。
packages/ui 组件。index.md 文档。demos/playground.tsx。style.css 基础 / 语义层、tokens.css 共享层、组件 style.css 专属层),不自造裸值。bg-[#xxx]、text-[14px]、rounded-[10px]、shadow-[...]。red-* / rose-* / amber-* / emerald-* / sky-* / slate-* / gray-* / zinc-* 等)。状态色、强调色一律走语义 token(accent / danger / muted / notice-* / toggle-* 等);缺哪个就先补到对应的 token 定义文件(见「Token 归置」),再用,不要用裸调色板绕过 token 系统。固定顺序,不要跳步:
style.css 基础 / 语义层、tokens.css 共享层),确认要用的 token;要改某组件专属 token 时读该组件的 style.css。demos/playground.tsx 覆盖主要可变 props。index.md 的 API 表与 TypeScript props 一致。单个组件推荐结构:
Component/
demos/
playground.tsx
Component.tsx
index.md
index.ts
复杂 / 组合组件结构:
Component/
demos/
playground.tsx
type.ts # 共享类型与对外类型
context.tsx # Context + useXxxContext
style.ts # 多个子组件共享的 class 映射 / 派生函数
Component.tsx # 根组件 + 编排,只负责组装
ComponentHeader.tsx
ComponentBody.tsx
... # 子组件按功能分组拆文件
Component.test.tsx
index.md
index.ts
拆分硬规则(满足任一即必须拆,不是"建议"):
.tsx 超过 ~200 行,必须拆分。 命中其一即触发,不要把所有实现堆在一个大文件里。Context 与 useXxxContext 放 context.tsx;对外/共享类型放 type.ts;根组件留在 Component.tsx 只做编排;子组件按功能分组拆到各自文件(如 Header / Body / Split 一组一文件,而非一个组件一文件也无妨,关键是单文件聚焦)。size/tone → class 的条件映射时,抽到 style.ts 的共享函数或映射表,不在每个子组件里复制条件分支。index.ts 只做导出(export * 各子文件 + export type 对外类型),不写实现。type.ts,即使数量不多,对外类型也优先独立成文件,方便调用方引用。其它规则:
@base-ui/react 封装。cn(packages/ui/src/utils/cn.ts)。packages/ui/src/icons),不手写 SVG。index.md API 表一致。any 承接公开 API。新增或调整 variant、size、tone 等枚举前,必须先搜索现有同类组件,沿用既有语义,不创造平行命名。
避免把视觉实现细节暴露为公开 API,例如 green、white、border。纯图标按钮、全宽按钮等能力沿用现有组件模式(如 Button 的 iconOnly、fullWidth):用明确行为建模,不污染尺寸语义。
加任何 prop 前必须先证明它真的有人需要,默认不加。宁可少而准,不要为"可能用得上"预留旋钮。每个候选 prop 逐条自检:
packages/web 等消费方确认场景。注意区分两种"暂无调用方":组件接入期、有明确近期规划的 prop 是合理的,可以保留;而纯臆测、只有 demo / playground 会用到、又说不出落地场景的 prop,等于没人用,不要加。判断不清时,问一句"这个 prop 是规划要用,还是只是先占个位"。className 解决? 宽度、间距、对齐等一次性视觉差异,交给调用方传 className(组件用 cn 合并),不要做成 size/spacing 这类枚举 prop。事实上调用方常用 className 覆盖内置枚举——这就是该 prop 不该存在的信号。size 和 layout 互相打架这类组合爆炸)。prop === "x" ? ... : ... 分支,要权衡这复杂度是否真有等价收益;没有就砍。判断不准时,先不加;等出现第二个真实场景再抽象,好过先造一个没人用的 prop 再删。删除无用 prop 时按"不可妥协规则"直接改调用方,不留兼容。
组件库的设计 token 是 CSS 自定义属性(:root 上的 --*),按归属分布在几个文件里,不是全堆在一个文件:
src/style.css:品牌色阶(brand-* / claw-* / live-*)、语义别名(accent / ink / muted / surface / notice-* 等)、字号、圆角、阴影、动效等基础 / 语义 token,是基础调色与排版的事实源。src/tokens.css:原子刻度与跨组件共享 token——--spacing-* 刻度、--spacing-icon-*、被多个组件或 shared.css 共用的几何 / 颜色(如 button/toggle)。src/<Component>/style.css:只属于该组件的 token(几何内联到 spacing 刻度;颜色作为 :root 块放在该组件文件、@layer 之外)。src/global.css:只在 .ui-demo-*(demo / playground)里用的 demo token,直接内联进 demo 规则。具体归置规则见下面「Token 归置」。
使用规则:
paper、surface、ink、ink-soft、ink-deep、muted、muted-strong、accent、accent-strong、danger、danger-strong、panel、line、overlay 等语义 token。notice-*、toggle-*、pane-* 等 token。red-* / emerald-* / sky-* / slate-* 等)。需要某种状态色时,先在 token 定义文件(style.css 语义层)找语义 token,没有就补一个语义 token(如 danger),不要用裸调色板顶替。brand-500)只在组件明确需要表达品牌强度或色阶层级时直接使用。token 按归属放文件,不要全塞进 tokens.css:
style.css:
var(--spacing-9),不要再起 --spacing-badge-sm 这种 1:1 别名层);确实脱离刻度的破格值(如 switch 轨道尺寸、440px 弹窗宽度)就地写字面量并加一行注释说明为何破格。:root {} 块放在组件文件顶部、@layer clawbot 之外——保证调用方在 :root 覆盖仍然生效,与旧的中心定义语义一致。shared.css 用)→ 留在 tokens.css:spacing 刻度、--spacing-icon-*、button/toggle 共享几何、--color-button-*(Button+Toggle)、--color-toggle-*(Toggle/ToggleGroup/Switch)。.ui-demo-* 用)→ 内联进 global.css。为什么这么分:co-location + 砍掉无意义的 1:1 别名层能改善 DX(IDE hover / 跳转直接落到刻度 token、少一跳),又不破坏可主题化的对外 token 契约——公共入口 dist/style.css(由 scripts/bundle-css.mjs 聚合各组件 style.css)仍然包含 co-located 的 :root 颜色块。
落地判断:
tokens.css;否则几何内联到刻度、颜色 co-locate 在组件文件。这与「Props 必要性」同源——不为「可能共享」预留中心 token。var(--x) 引用悬空(引用了已不存在的 token),并跑 build 确认公共 bundle 仍含对外颜色 token。vscode-css-variables 插件兜底(.vscode/settings.json 的 cssVariables.lookupFiles 已限定扫描 packages/ui/src,跳转落到库自己的定义)。组件库的间距不是审美临场判断,必须先归入密度场景,再落到 packages/ui/src/style.css 的 spacing token。新增或修改 px-*、py-*、p-*、gap-*、space-y-*、space-x-*、h-*、w-*、size-*、max-w-* 等尺寸类之前,先回答它属于哪种场景:
硬规则:
space-8 / space-9 及以上的 padding/gap,必须先证明它不是页面 section 的误用。layout 只决定结构,不等于“大号模式”。例如 dialog / panel / split 可以改变列结构,但不应该顺手把标题、padding、footer、sidebar 全部放大。className 放大,不为此新增 density / spacing 公开 prop。style.ts,并使用语义命名(例如 dialog section、footer、sidebar、content gap),不要在每个子组件里重复裸 class。space-y-*、gap-*、p-* 也必须服从组件密度,不能用随手的大留白塑造错误基准。图标尺寸是密度规则的例外,单独走图标专用 token,不要套用文字或裸 spacing 刻度。两个坑必须避开:
text-* 字号刻度是 11–15px(text-base 仅 13px),把图标缩到正文字号会让图标糊成一团。图标的视觉重量应大于正文,不跟随 text-*。size-4。 packages/ui/src/style.css 的 --spacing-* 刻度被重映射过:size-4 ≈ 8px、size-6 ≈ 16px、size-7 ≈ 20px,与社区版 Tailwind 完全不同。按肌肉记忆写 size-4 会得到 8px 的极小图标,这正是图标偏小的根因。硬规则:
size-icon-* 语义 token(size-icon-sm / size-icon-md / size-icon-lg),具体像素值以 style.css 的 --spacing-icon-* 为准,本文不复制数值。size-icon-md;行内/密集控件用 size-icon-sm;标题图标、空状态等强调场景用 size-icon-lg。size-icon-*。容器内可用 [&_svg]:size-icon-md 统一约束子图标,避免依赖调用方自己传尺寸。tokens.css 补 --spacing-icon-*(icon 是跨组件共享 token),再用;不要在组件里写 size-[18px] 或裸 size-5 绕过。如果设计确实需要新 token:
style.css;原子刻度与跨组件共享放 tokens.css;单组件 token 放该组件 style.css(几何内联到刻度、颜色 co-locate 在 :root);demo token 放 global.css。每个组件至少提供一个 demos/playground.tsx。
Playground 必须覆盖组件的主要可变 props。按组件实际 props 选取,下面只是示例,没有的 prop 不要硬塞控件:
variantsizedisabledchildrenvalueopentonefullWidth推荐写法(变体选项与默认值从现有同类组件沿用,不要凭空造):
const controls = useControls({
variant: {
options: existingVariantOptions,
value: existingDefaultVariant,
},
disabled: false,
children: "提交",
});
规则:
StoryBook(packages/ui/src/Playground)包裹,由 StoryBook 负责组件预览画布、网格背景、内边距和 Tweaks 面板。index.md 中的 dumi demo 默认使用 <code src="./demos/playground.tsx" nopadding></code>,去掉 dumi 外层 padding,避免 dumi 和 StoryBook 双重包裹。StoryBook,不要先移除 nopadding。value + onChange / onValueChange 写回 useSetControl(name, value),让预览区组件本身可操作并与 Tweaks 面板实时同步。禁止 readOnly + 受控值、onChange={() => {}} 这类冻结预览组件的写法;纯展示型组件不强加 value 控件。每个组件的 index.md 应包含:
---
title: Button
group: 基础组件
description: 一句话说明组件用途。
---
# Button
简短说明组件用途和适用场景。
## Playground
<code src="./demos/playground.tsx" nopadding></code>
## APIs
| 属性 | 描述 | 类型 | 默认值 |
| ---- | ---- | ---- | ------ |
要求:
Dialog、AdminCard。修改 packages/ui 后至少执行:
pnpm -F @clawbot/ui typecheck
pnpm -F @clawbot/ui fmt:check
pnpm -F @clawbot/ui lint
pnpm -F @clawbot/ui build
pnpm -F @clawbot/ui docs:build
如果同步修改了 packages/web 调用方,额外执行:
pnpm -F @clawbot/web typecheck
必须执行两条 token 违规扫描,两条都应为空(命中即需人工确认并修复,不能绕过):
# 1. 任意值:绕过 token 的 magic value(bg-[#xxx]、text-[14px] 等)
rg -n '(^|\s)(bg|text|border|rounded|shadow|ring|p|px|py|m|mx|my|w|h|min-w|max-w|min-h|max-h|gap|space)-\[' packages/ui/src --glob '!*.md'
# 2. 默认调色板:绕过语义 token 的裸 Tailwind 颜色(red-700、emerald-500 等)
rg -n '\b(bg|text|border|ring|from|to|via|fill|stroke|outline|divide|decoration)-(red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|slate|gray|zinc|neutral|stone)-[0-9]{2,3}\b' packages/ui/src --glob '!*.md'
扫描不是最终裁决,但命中结果必须人工判断是否合理;状态色命中默认调色板时,应改为语义 token。
完成后,最终回复应说明:
index.md API 表。