关于 ui-kit-from-code
ui-kit-from-code 可将项目现有的设计令牌和组件整理为独立 HTML 参考套件,供设计师、开发者和 Agent 查阅复用。它通过组件覆盖检查和 Playwright 验证提供依据,不修改项目组件代码。
主要功能
- 01从项目代码提取令牌
- 02生成独立 HTML 参考页
- 03展示组件变体与交互状态
- 04支持主题切换与复制
- 05检查清单覆盖和页面布局
适用场景
- 01制作可复用的设计系统参考页
- 02检查组件变体和交互状态
- 03通过单个文件分享项目令牌与标记
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rheinmir/ setup --skill ui-kit-from-code安装后在 Claude Code 中输入/ui-kit-from-code确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/discover.mjssync-kit.pyui-kit-reference.html详细介绍
技能:ui-kit-from-code
内容
目的与背景
- 目的: 构建一个独立的 HTML 文件,让用户(设计师、开发者、Agent)查阅并复用项目的真实设计系统,作为 Figma UI kit:查看所有 token 和组件、即时切换主题、直接复制 token 或标记以供使用。
- 触发场景:“把它拆出来做成 UI kit”“用 HTML 做 Figma UI kit”“设计系统参考页面”“可复用的组件库”。通常出现在项目已有稳定的 token 和组件,但还没有页面将它们汇总起来时。
- **非目标:**创作新系统或调整美学风格(→
hallmark);完整克隆页面以便重新运行(→extract-site/web-clone);导出真正的 Figma 文件、Storybook 或 npm package;修改项目的组件代码。
心智模型
nguồn token (TS/CSS/Tailwind/Compose…) ──chạy thật──▶ JSON token ──▶ CSS vars --<ns>-<role> + --k
component trong code (kích thước, variant, tên kind) ──đọc──▶ class .<ns>-* chỉ dựa vào vars
URL gốc ──discover.mjs (link BFS · mở phần ẩn · chữ ký · bão hoà)──▶ inventory.json + crops ──▶ danh mục phải dựng
kit.html + inventory.json ──--coverage──▶ phủ 100% (data-sig / ui-kit-skip)
└──▶ ui-kit.html (7 phần bắt buộc) ──Playwright──▶ bằng chứng
Kit 是代码中系统的可验证副本:每种颜色和尺寸都能追溯到仓库中的某个常量或函数。如果代码没有明确说明、必须按规范绘制,就必须注明(RULE-04)。
输入与输出约定
| 字段 | 必填? | 含义 | |
|---|---|---|---|
| 输入 | token 来源 | 是 | 生成颜色、字体、形状和尺寸的文件/函数(例如 lib/tokens.ts → paletteOf) |
| 输入 | 组件目录 | 是 | 类型名称(kind/component name)+ 应用中的分组方式(palette、sidebar、docs) |
| 输入 | 目标平台 | 是 | mobile / desktop / 两者,附真实视口尺寸 |
| 输出 | docs/ui-kit.html(或用户选择的路径) | 是 | 1 个文件,可通过 file:// 打开,包含下方全部 7 个部分 |
| 输出 | 来源报告 | 是 | 说明哪些部分来自代码,哪些部分按规范绘制 |
| 输出 | Playwright 证据 | 是 | 0 个 JS 错误,390px 下 scrollWidth - innerWidth = 0,亮色 + 暗色 + 屏幕示例截图 |
| 输出 | kit 仓库上的版本 | 是(UI_KIT_SYNC=0 除外) | PRIVATE 仓库 Rheinmir/ui-kits(W09)中的 <id>/vN/index.html + manifest.json + registry.json 条目 |
规则与能力
- RULE-01(MUST):颜色和尺寸必须通过运行项目的代码获取(临时测试运行器/脚本,导出后删除),不得手动输入十六进制值。对于只能读取而无法运行的代码(CSS 变量、JSON),则解析文件。
- RULE-02(MUST):组件类只能引用 CSS 变量(
--<ns>-<role>、--k);组件 CSS 中不得出现零散的十六进制值或rgb值。这样,:root块和 CSS.<ns>-*就能直接移到其他项目中运行。 - RULE-03(MUST):每张组件卡片都必须准确记录代码中的名称(kind/component name + 主要字段)和代码使用的尺寸(dp/px)。
- RULE-04(MUST):在报告中(必要时也在卡片备注中)说明所有按规范绘制或经过简化的部分(例如,形变动画仅旋转);不得将其呈现为 1:1 复刻。
- RULE-05(MUST):必须包含以下 7 个结构部分;缺少任何部分时,必须在报告中说明原因(例如,应用没有桌面端)。
- RULE-06(MUST):文件必须自包含:字体仅来自 Google Fonts,JS/CSS 内联,不进行构建,也不从外部获取数据。
- RULE-08(MUST):**不能止步于框架。**组件清单必须来自 W01b 探索过程(沿链接浏览、点击展开隐藏内容,并在结果趋于饱和时停止),不能只根据几个已知页面/文件整理。
inventory.json中的每个 pattern 都必须在 kit 中有一张带有data-sig="<sig>"的卡片,或通过<meta name="ui-kit-skip" content="<sig>:<理由>;…">说明排除原因。门禁:node references/discover.mjs --coverage inventory.json kit.html返回码为 0。 - RULE-10(MUST):**组件不能只有默认状态。**W01b 测得的每种状态(hover、focus-visible、active、selected)都必须呈现在 kit 中:卡片应展示对应状态的变体(使用
is-hover/is-focus/is-active/is-on类,准确复现网站的:hover等规则),并带有data-sig-state="<sig>:<state>"。或者通过ui-kit-skip"<sig>:<state>:原因"说明排除原因。如果焦点状态仅为浏览器的outline:auto(ua: true),kit 必须明确注明“焦点使用浏览器默认样式,网站没有单独设置样式”。--coverage门禁也会统计状态。 - RULE-11(MUST):遵守 robots.txt 和网站条款。
discover.mjs会在扫描前读取/robots.txt。如果User-agent: *禁止起始路径,或有禁止自动采集的说明(例如 facebook.com),则返回码为 3 并停止;不要自行添加--i-have-permission。只有用户确认拥有许可时才能使用此标志。改为建议使用 B05:由用户自行保存页面。 - RULE-09(MUST):如果来源是他人网站,不得将其图片、logo、SVG 字标或受版权保护的内容嵌入 kit;应改用明确标注“图片 …”的占位块。Kit 可以自动同步到 PRIVATE 仓库(W09);如需将其分享至该仓库之外(公开链接、托管),请先征求同意(B03)。
- RULE-12(MUST):只同步已通过检查的 kit(W05b + W06 为绿色),并且只能同步到 PRIVATE 仓库——
sync-kit.py会自动检查visibility,并拒绝 PUBLIC 仓库(GH#187)。版本号只能递增:不得覆盖或删除旧的vN。 - RULE-07(SHOULD):制作时将示例文件
references/ui-kit-reference.html放在旁边;复用其框架、页面外壳 CSS 和控制脚本,只替换 token、组件和示例屏幕。 - Capabilities:读取代码库;运行项目的测试运行器/脚本;编写 1 个 HTML 文件;运行 Playwright(参见
/playwright-verify)。
每个部分的必备结构
严格按照 references/ui-kit-reference.html 中的顺序:
| # | 部分 | 必须包含 | 示例文件中的对应项 |
|---|---|---|---|
| 1 | 控制栏(sticky) | 选择调色板(如果应用有多个)· 浅色/深色 · 形状比例(如果应用有)· 为当前选择的 :root 块提供「Copy CSS token」按钮;使用 localStorage 记住选择(用 try/catch 包裹);首次使用时遵循 prefers-color-scheme | .bar · #swatches #mode #shape #copy-tokens |
| 2 | 目录(侧边栏,小于 900px 时隐藏) | 按基础样式 / 组件 / 示例分组;自动高亮当前查看的项目(IntersectionObserver) | .side |
| 3 | 简介 | eyebrow 用于指明源文件(<file token> → <file kit>)· h1 · 一段说明 kit 的来源及复用方式 · 列出重要布局常量的「facts」条 | .intro .facts |
| 4 | 基础样式 | (a) 颜色: 所有角色、文字颜色为正确配对的 on* 色块、点击即可复制 hex · (b) 文字: 字号比例,包含名称 + size/line-height/weight · (c) 形状: 圆角比例乘以 --k · (d) 布局与尺寸: 主要组件尺寸比例表 + 框架/边距/间距常量表 | #color #type #shape #layout |
| 5 | 组件 | 按应用现有分组方式组织(如操作 · 导航 · 内容容器 · 输入 · 反馈);每张 .spec 卡片 = 标题栏(可读名称 + 代码名称标签 + 尺寸)→ .stage 展示所有重要变体/尺寸/状态 → 页脚(备注 + 自动附加的 Copy HTML 按钮);卡片带有其覆盖的 pattern 的 data-sig(RULE-08)。inventory 中 pattern 较多的分组应按 pattern 拆分卡片,不要合并成一张通用卡片 | #actions #navigation #containment #inputs #feedback |
| 6 | 示例屏幕 | 至少包含 1 个 移动端屏幕和 1 个 桌面端屏幕(应用支持时),使用应用的正确框架尺寸,并通过 zoom 缩小;使用第 5 部分中的组件类,按应用的布局规则组合(如移动端底部导航 → 桌面端导航栏 + 列表-详情);在框架下方注明所用组件 | #screens .phone .desk |
| 7 | 脚本 | 以 ROLES + PALETTES[key].light/dark 形式嵌入 token 表,由 dump 生成 · apply() 将 CSS var 写入 :root · 根据数据渲染色块 / 字号比例 / 形状比例 · 绑定复制按钮 · 显示复制成功 toast(剪贴板受阻时提示手动复制) | 文件末尾的 <script> |
整个页面通用要求:页面外壳使用 kit 自身的 token(页面换主题时组件也随之变化);prefers-reduced-motion 关闭动画;:focus-visible 清晰可见;图标按钮带有 aria-label;在 390px 宽的移动端不出现横向溢出(表格/示例屏幕放在 overflow-x:auto 容器中)。
故障边界
- 找不到 token 来源(组件中散落着硬编码颜色)→ partial:通过 grep 汇总实际使用的颜色,明确写出“没有 token 来源,颜色根据组件推断”,建议拆分 token(单独处理)。
- 网站在独立容器中滚动(body 高度正好为 1 屏)→
fullPage只会截取 1 屏;discover.mjs会在截图前自动打开容器,自行编写的脚本也必须这样做。 - 状态仅在 JS 运行时出现(点击打开的菜单、延迟出现的 tooltip、进入动画)→ 纯
:hoverCSS 状态可测量,但依赖 JS 的状态可能会漏掉;W01c 查看*-hover.png裁剪图后自行补充。按住(active)状态通过mouse.down测量 → 将鼠标拖到外部 →mouse.up,因此不会触发链接。无法测量时,请在报告中明确说明。 - 页面禁用 JS(B05):JS 添加的状态(例如 FDS Facebook 的 hover/pressed 覆盖层)无法测量。根据相应 token(
--hover-overlay、--press-overlay)还原,并明确写出“根据 token 还原,未测量”。页面很重(feed 有 3000+ 个元素)时,需要提高--page-timeout。 - 探索达到页面上限(
stoppedBy: page-budget),但最后一个页面仍出现新模式 → 告知用户,建议提高--pages或缩小--include范围,不要将 kit 视为完整。 - Token 只能在繁重的运行时环境中运行(需要 DB/app server)→ 使用 Playwright 从运行中的应用导出(对
:root调用getComputedStyle),并说明获取方式。 - 项目只有一个平台 → 第 6 部分只展示该平台的屏幕,并说明原因。
- Playwright 报告 JS 错误或经过 3 轮修复后仍然出现横向溢出 → 停止并报告剩余错误。
- W09 同步时遇到网络错误/没有推送权限/仓库不是 PRIVATE → 脚本输出“尚未同步,原因 …”,rc 0;本地 kit 仍是有效结果,请在 W08 中说明原因。
操作方式
主要工作流
| 步骤 | 类型 | 输入 | 操作 | 输出/退出 | 失败/后续 |
|---|---|---|---|---|---|
| W01 | 判断 | 代码库 | 查找 token 来源、组件目录 + 分组、移动端/桌面端框架尺寸、schema 文档(如有) | file:line 锚点列表 | 没有 token → 部分完成(见 Failure) |
| W01b | 确定性 | 原始 URL(开发中的 app 或网站) | 探索循环:NODE_PATH=$(npm root -g) node references/discover.mjs <url> --out <scratch>/disc --pages 32 --saturate 4(需要全局安装 Playwright;每页约 40 秒,在后台运行)(BFS 遍历同源链接,滚动所有内部容器,点击展开 accordion/tab/details;签名 = 类型 + 样式 + 子项形状,去重;对于可点击模式,还通过 CDP CSS.forcePseudoState 强制触发 HOVER / ACTIVE / FOCUS-VISIBLE(类似 DevTools 的“Force state”:确定性,不拖放、不点击,关闭 JS 后也能运行),并根据 aria-pressed/selected/current、data-state、class is-active 合并 SELECTED 变体) | inventory.json + crops/*.png + 每页截图 | stoppedBy: page-budget 且仍有新内容 → 增加预算(Failure) |
| W01c | 判断 | crops + 页面截图 | 查看所有新 crop 和整页截图;将模式归组为 kit,并把分类器遗漏的模式(例如梯形 tab、流程图)记入待实现清单 | 模式 → 分组 → kit 卡片表 | — |
| W02 | 确定性 | token 锚点 | 编写临时测试/脚本调用真实 token 函数,导出 JSON(所有 palette × 浅色/深色、尺寸比例)→ 删除临时文件 | scratchpad 中的 tokens.json | 运行失败 → 静态解析 |
| W03 | 效果 | JSON | 精简为 ROLES + PALETTES,并嵌入样板文件副本 | <script> 中的数据块 | — |
| W04 | 判断 | 组件代码 | 重写第 4–5 部分:每个组件使用一个 .<ns>-* class,测量值取自代码;.spec 卡片包含 header/stage/footer | 第 4–5 部分 | 代码中缺少测量值 → 按 spec 绘制并说明(RULE-04) |
| W05 | 判断 | app 布局规则 | 拼接第 6 部分:使用 W04 中的 class 构建移动端 + 桌面端屏幕 | 第 6 部分 | app 仅支持一个平台 → 说明原因 |
| W05b | 确定性 | kit + inventory | node references/discover.mjs --coverage <scratch>/disc/inventory.json <kit.html> | 覆盖 N/N (100%) + 状态 M/M,rc 0 | 缺少模式 → 构建卡片;缺少 sig:state → 为卡片添加状态变体(RULE-10)或添加有理由的 ui-kit-skip;重新运行 |
| W06 | 确定性 | 文件 | Playwright:加载页面,统计 pageerror,截取页面顶部 + 中部 + 样例屏幕;开启深色模式 + 更换 palette 后重新截图;在 viewport 390 下测量横向溢出 | 截图 + 数值 | 出错 → 修复,最多 3 轮 |
| W07 | 判断 | 截图 | 查看一轮截图,修复可见的布局问题(文字挤在一起、项目错位),再截图 1 次 | kit 完成 | — |
| W08 | 判断 | 结果 | 向用户报告:文件路径、7 个部分的内容、哪些部分来自代码/按 spec 构建、W06 的证据、W09 的同步结果 | 报告 | — |
| W09 | 效果 | 已通过 W05b + W06 的 kit | python3 references/sync-kit.py <kit.html> --source <仓库目录 | 原始 URL>(无法从来源推断 id 时添加 --id <id>,例如登录后的 app)。Id:仓库 → 从 origin 获取 <owner>-<repo>(没有 remote → <目录>-local);URL → 去掉 www. 的 host,并将点替换为连字符。sha256 哈希忽略 meta ui-kit-id;与最新版本相同 → “与 vN 相同”,不提交。不同 → vN+1 + latest/,推送;若被拒绝则拉取后重新计算,最多 3 次。UI_KIT_SYNC=0 跳过;UI_KIT_SYNC_REPO 更改仓库 | ✓ <id>: vN → <link> 或 = 不变 | 出错 → “尚未同步,原因”,rc 0(Failure) |
**token 导出示例(vitest,TS):**创建 lib/zz-kit-dump.test.ts,调用 token 函数,然后执行 writeFileSync(<scratchpad>/tokens.json, …),运行 npx vitest run lib/zz-kit-dump.test.ts,再执行 rm 删除文件。不要让临时文件出现在项目 diff 中。
**Playwright 检查示例:**如 /playwright-verify:chromium.launch() → goto(file://…/ui-kit.html) → page.on("pageerror") → screenshot → click('#mode button[data-v="dark"]') → 在 390×844 页面上测量 document.documentElement.scrollWidth - innerWidth。
分支
| ID | 类型 | 条件 | 行为 | 跳过/失败 | 汇合 |
|---|---|---|---|---|---|
| B01 | conditional_required | token 是 CSS vars/Tailwind config/JSON(不是函数) | 静态解析,不运行(W02) | — | W03 |
| B02 | conditional_required | app 有面向 agent 的 schema 文档(例如 agent.md) | “代码中的名称”标签使用 schema 中准确的字段名,示例屏幕遵循该文档中的规则 | 没有 → 使用 component 名称 | W04 |
| B04 | conditional_required | 来源是正在运行的 site URL(没有代码) | 将 W02 替换为 Playwright:读取 :root/.dark 中的 CSS vars 和实际元素的 getComputedStyle;将 lab()/oklch() 颜色转换为 hex;应用 RULE-09 | 有代码 → 跳过 | W03 |
| B05 | conditional_required | site 禁止 crawler(RULE-11)或界面位于登录后 | user 将页面自行保存(“Webpage, Complete”)到一个文件夹 → python3 -m http.server 8765 --bind 127.0.0.1 → discover.mjs http://127.0.0.1:8765/<file>.html --offline --no-js --page-timeout 1500(阻止所有外部请求,关闭 JS,避免脚本自动回连 site)。保存的页面包含个人数据:kit 仅提取样式/结构,所有名称/图片/内容均替换为示例,交付前用 grep 检查真实名称 | site 允许 crawl → 使用常规 W01b | W01c |
| B03 | user_optional | user 希望将分享链接发布到 kit private 仓库之外 | 将文件上传到 user 选择的 Artifact/hosting;文件仍保留在 repo 中 | 默认:本地文件 + kit private 仓库(W09) | W08 |
验证与停止
机器检查:W09 的确定性测试 python3 -m pytest harness/tests/test_ui_kit_sync.py(id、hash、版本号、v1 不变);W05b 覆盖率门禁 rc 0(每个 pattern 都有 data-sig 或 ui-kit-skip);W06 必须确保 pageerror = 0 且在 390px 下水平溢出 = 0。人工检查:根据结构表确认 7 个部分齐全;对比明暗模式截图;测试 Copy CSS token 按钮和 1 个 Copy HTML 按钮。W06 最多修改 3 轮,W07 只重新截图一次。
示例
- **正向:**m3e-canvas(Next.js,Material 3 Expressive 编辑器)。将
paletteOf()的 vitest 输出为 7 个调色板 × 亮色/暗色;工具包包含 25 个颜色角色、15 种字体样式、3 种形状级别、约 30 个组件,分为 5 组,类似应用中的 Parts 表;移动端屏幕为 412×892(列表 + 详情),桌面端为 1280×800(导航栏 + 列表-详情);Playwright 0 个错误,390px 下无溢出。主要产物是references/ui-kit-reference.html。 - **正向(URL):**drafted.ai。首次只扫描了 3 个已知页面,因此工具包只有基本框架。从
/learn/ai-for-homebuyers运行 W01b,使用--pages 32 --saturate 4:discover.mjs遍历了 32 个页面(learn、use-cases、房屋示例列表页、FAQ、新闻、招聘、引导流程),收集 171 个模式,连续 4 页没有新内容后停止(约 40 秒/页)。W05b 首次报告覆盖 62/171 (36%)。随后补建房屋示例列表、文章与区块等分组,根据清单生成实际使用的字体表,并记录 22 个ui-kit-skip(图片、第三方嵌入内容、页面框架),检查关卡报告覆盖 171/171 (100%)。W01c 还发现一个分类器遗漏的模式:用 SVG 绘制的梯形标签页。 第 3 轮增加了状态测量:175 个模式、104 种状态(44 个 hover、56 个 focus、1 个 active、3 对 selected)。首次结果为状态 0/104。添加 23 条状态带和“Focus · 2 种”卡片后,检查关卡报告175/175+104/104。测量还修正了此前 6 条错误猜测的 hover 规则(例如,选择卡片 hover 时变为 stone-500,而不是 stone-900),并显示 44/56 个 focus 状态只是浏览器默认轮廓。 - **正向(B05,网站禁止爬虫):**facebook.com。robots.txt 禁止自动采集,因此
discover.mjs以 rc 3 停止,Agent 询问用户。用户保存 News Feed(6.4 MB + 252 个资源),离线运行并关闭 JS(阻止 49 个外部请求)。从已保存的 CSS 中获取了.__fb-light-mode和.__fb-dark-mode的 579 个 FDS 变量、78 个模式、5 种可测量状态(FAB hover 时阴影改变,story active 时为scale(.98),链接 hover 时显示下划线)。按钮的 hover/pressed 状态根据--hover-overlay/--press-overlay构建。工具包包含 27 张卡片、66 个颜色角色;检查关卡结果为 78/78 + 5/5;grep 未发现真实姓名;0 个外部请求。 - **边界情况/失败:**React 仓库只有分散在 40 个
.module.css文件中的十六进制颜色,没有 token → 部分完成:grep 找到 18 个实际使用的颜色,工具包标注“颜色根据组件推断”,并建议先拆分 token,再构建完整工具包。