关于 create-design-system
此技能可从网站提取设计令牌与品牌素材,并整理成结构化文件,供构建品牌一致界面的 Agent 使用。它涵盖浏览器检查、截图和素材处理流程。
主要功能
- 01提取样式与设计令牌
- 02截取首页
- 03保存 Logo 与网站图标
- 04收集符合条件的自托管字体
- 05生成结构化设计文件
适用场景
- 01记录网站的视觉体系
- 02为 UI Agent 准备品牌参考
- 03保存网站 Logo 和图标
- 04收集网站提供的字体
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ edukeyteam/ agent-toolbox --skill create-design-system安装后在 Claude Code 中输入/create-design-system确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/setup.mdscripts/decode-binary-assets.cjsfetch-binary-assets.browser.js详细介绍
从网站创建设计系统
从任意网站提取设计令牌和品牌素材,并将其保存为结构化文件,供构建符合该品牌风格的 UI 的 Agent 使用。
前提条件
此技能需要 Node.js、Playwright CLI 和已配置的浏览器。请检查这些组件是否可用。如果有缺失,请简要告知用户,然后按照一次性设置指南通过获准的包管理器安装。仅当环境要求时才请求批准。
示例使用 playwright-cli 和一个命名会话。如果项目使用本地 CLI,请在每条命令中使用其运行器。不要依赖 Shell 别名在多次调用之间持续有效。完成后,使用 playwright-cli -s=design close 关闭会话。
流程
步骤 1 — 准备输出目录
开始前创建所需目录:
在 Bash 中,使用 mkdir -p assets docs。在 Windows PowerShell 中,使用 New-Item -ItemType Directory -Force assets, docs | Out-Null。
步骤 2 — 导航并关闭浮层
⚠️ 必须执行 — 截图或提取样式前,先关闭遮挡页面的所有 Cookie 横幅、模态框和浮层。 这是此技能最常见的失败原因。浮层既会影响计算样式,也会破坏截图。
使用一次性安装期间配置的浏览器打开 URL:
playwright-cli -s=design open <URL>
如果页面返回 HTTP 403,或显示被拦截/验证页面,请勿从该页面提取样式或截图。稍等片刻后,在同一会话中重试导航一到两次;临时拦截可能会解除。可以尝试已配置的其他浏览器,但不要假设已安装 Chrome,也不要绕过网站的访问控制。如果真实页面仍无法访问,请停止并报告拦截情况,而不是臆造设计系统。这是网站的响应,不一定是 Linux 或 Playwright 的设置问题。
Cookie/同意横幅通常会延迟加载,首次快照中可能没有。(真实案例:横幅在页面加载几秒后才出现,结果悄然进入了截图。)因此:
- 等待延迟横幅加载。没有
wait命令;请使用run-code:playwright-cli -s=design run-code "async page => await page.waitForTimeout(3000)" - 搜索横幅,而不是读取整份快照。
find只返回含上下文的匹配节点,所需令牌数仅为完整快照的一小部分:playwright-cli -s=design find --regex "/cookie|consent|accept|akceptuj|zgadzam/i" - 按引用定位横幅区域的快照,查看准确的按钮:
playwright-cli -s=design snapshot e945 - 按引用点击接受按钮。常见标签包括:“Accept”、“Accept all”、“Accept All Cookies”、“OK”、“I agree”、“Allow all”,以及波兰语:“Akceptuj”、“Akceptuj wszystkie”、“Zgadzam się”、“Rozumiem”。
playwright-cli -s=design click e955 - 继续前确认横幅已消失——重新按按钮标签运行
find,确认没有匹配项:playwright-cli -s=design find --regex "/Accept All Cookies/i" - 还要关闭同意操作后出现的任何工具提示、入门引导或新闻简报弹窗。
每条命令都会打印快照链接(.playwright-cli/page-<timestamp>.yml),而不是快照本身。只有在 find 不够用时,才读取或 grep 该文件。
步骤 3 — 截取主页
🛑 暂停——再次检查浮层。 截图前,再截取一次快照并确认没有 Cookie 横幅、模态框或弹窗遮挡内容。横幅会延迟加载,因此即使步骤 2 中页面看起来正常,也可能在此后出现。如果有任何内容遮挡页面,请关闭它(步骤 2),然后再截图。
playwright-cli -s=design screenshot --filename=assets/homepage.png --full-page
保存后,使用可用的图像查看器打开 assets/homepage.png 并进行目视检查,确认图像上没有横幅/浮层条带。如果有,请关闭浮层并重新截图——不要带着被污染的截图继续操作。
步骤 4 — 通过 eval 提取设计令牌(分多次小调用运行)
下面的每次调用都是传递给 playwright-cli -s=design eval 的 JavaScript 箭头函数。请将三次调用分开:合并成一次超大调用难以调试,输出也难以阅读。
较小的结果可以直接打印;较大的结果应使用 --filename 保存到文件,避免进入上下文窗口:
playwright-cli -s=design eval "<the arrow function below>" --filename=tokens-a.json
--filename 会将对象写为 JSON;返回的字符串可能会以 JSON 字符串形式编码。解析前请先检查文件,字符串只解析一次。
调用 A — 核心元素样式
() => {
const getStyles = (el) => {
if (!el) return null;
const s = getComputedStyle(el);
return {
color: s.color,
backgroundColor: s.backgroundColor,
fontFamily: s.fontFamily,
fontSize: s.fontSize,
fontWeight: s.fontWeight,
padding: s.padding,
margin: s.margin,
borderRadius: s.borderRadius,
lineHeight: s.lineHeight,
};
};
return {
body: getStyles(document.body),
header: getStyles(document.querySelector('header') || document.querySelector('[class*="header"]')),
nav: getStyles(document.querySelector('nav')),
h1: getStyles(document.querySelector('h1')),
h2: getStyles(document.querySelector('h2')),
link: getStyles(document.querySelector('a')),
};
}
调用 B — 颜色、圆角、按钮
() => {
const allEls = document.querySelectorAll('*');
const bgColors = new Set(), textColors = new Set(), borderRadii = new Set();
for (let i = 0; i < Math.min(allEls.length, 500); i++) {
const s = getComputedStyle(allEls[i]);
if (s.backgroundColor !== 'rgba(0, 0, 0, 0)') bgColors.add(s.backgroundColor);
textColors.add(s.color);
if (s.borderRadius !== '0px') borderRadii.add(s.borderRadius);
}
// Buttons with non-transparent backgrounds
const btnStyles = [];
for (const btn of document.querySelectorAll('button, [class*="btn"], a[class*="button"]')) {
const s = getComputedStyle(btn);
if (s.backgroundColor !== 'rgba(0, 0, 0, 0)') {
btnStyles.push({
text: btn.innerText?.trim().substring(0, 30),
bg: s.backgroundColor, color: s.color,
borderRadius: s.borderRadius, padding: s.padding,
fontSize: s.fontSize, fontWeight: s.fontWeight,
border: s.border
});
if (btnStyles.length >= 5) break;
}
}
return {
uniqueBackgrounds: [...bgColors],
uniqueTextColors: [...textColors],
uniqueBorderRadii: [...borderRadii],
btnStyles
};
}
调用 C — Logo、favicon、字体
() => {
// Logo: look in header .logo or [class*="logo"] — NOT country flag selectors
const logoEl = document.querySelector('header .logo') ||
document.querySelector('[class*="header"] [class*="logo"]');
const logoSVGEl = logoEl?.matches('svg') ? logoEl : logoEl?.querySelector('svg');
const logoSVG = logoSVGEl?.outerHTML?.trim() ?? null;
const logoImageCandidates = [...document.querySelectorAll('header img, [class*="header"] img')]
.filter(img => img.getBoundingClientRect().width > 50)
.slice(0, 8)
.map(img => ({ alt: img.alt, src: img.currentSrc || img.src }));
// Favicon href
const favicon = document.querySelector('link[rel*="icon"]');
// Font faces from CSS rules
const fontFaces = [];
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule.type === CSSRule.FONT_FACE_RULE) {
fontFaces.push(rule.cssText.substring(0, 300));
}
}
} catch(e) {}
}
// Nav links
const navLinks = [...document.querySelectorAll('nav a')].slice(0, 5).map(a => ({
text: a.innerText?.trim().substring(0, 30),
color: getComputedStyle(a).color,
fontWeight: getComputedStyle(a).fontWeight,
textTransform: getComputedStyle(a).textTransform,
}));
return {
logoSVG,
logoImageCandidates,
faviconHref: favicon?.href ?? null,
fontFaces: fontFaces.slice(0, 8),
navLinks
};
}
步骤 5 — 保存品牌素材
Logo: 仅当 logoSVG 包含 SVG 标记时,才将其保存到 assets/logo.svg。如果不存在,请检查 logoImageCandidates 和可见页眉,以识别实际的字标,而不是旗帜或产品图片。如果其图片 URL 提供 SVG 且页面允许获取,请在页面内获取 SVG,验证返回的文本,然后将其保存为 assets/logo.svg。包含 SVG 文本的 --filename 结果可能是 JSON 编码的字符串:请先检查,再解析一次,不要解析两次。
如果选定的字标是 PNG、JPEG 或 WebP 图片,请在页面上下文中获取其实际字节。返回一个仅含单个条目的对象,将 logo.png、logo.jpg 或 logo.webp 映射到 base64(根据响应类型或文件签名选择扩展名),使用 --filename=logo-binary.json 保存该对象,并使用步骤 5b 中现有的 Node 辅助程序进行解码,输入文件为 logo-binary.json。打开保存的图片进行验证。如果获取被阻止,请说明无法保存 Logo。切勿将光栅图片字节或 HTML 放入 logo.svg。
Favicon + 自定义字体(二进制素材): 在所有平台上都使用步骤 5b 中的页面内获取辅助程序。请勿使用 curl、wget 或 PowerShell Invoke-WebRequest 下载这些素材;Shell 下载器可能绕过网站的同源上下文,也可能被 Agent 环境拒绝。请遵守网站的素材权利要求。
若只需获取 favicon,可运行该辅助程序(它也能处理字体,见步骤 5b),并从输出中保留 favicon.*。
步骤 5b — 下载自定义/自托管字体(仅当网站提供时)
何时执行:仅当网站通过自托管的 @font-face 规则提供自己的自定义字体时(字体文件位于网站源站,例如 …/themes/site/fonts/Brand-Regular.woff2)。以下情况请跳过:
- 仅使用通用系统字体(
Arial、-apple-system、Segoe UI等);或 - 使用无法通过同源获取的第三方 CDN(例如
fonts.gstatic.com、Adobe Typekit)——这些资源会受到 CORS 限制,且不属于可再分发的“网站提供的字体”。
如果你在步骤 4/调用 C 中收集的 fontFaces 指向网站域名上的真实字体文件,请下载这些文件:
-
在页面内获取所有二进制资源(字体和 favicon),将 base64 写入文件,避免大量内容涌入上下文。使用与此技能所在文件夹对应的命令。从项目根目录运行项目文件夹中的命令。如果技能位于其他位置(例如项目中的
.claude/skills/create-design-system,或用户文件夹中的$HOME/.claude/skills/create-design-system),请找到此SKILL.md,并使用其同级的scripts/文件。Bash,脚本位于项目技能文件夹中:
playwright-cli -s=design eval "$(cat '.agents/skills/create-design-system/scripts/fetch-binary-assets.browser.js')" --filename=binary-assets.jsonBash,脚本位于用户技能文件夹中:
playwright-cli -s=design eval "$(cat "$HOME/.agents/skills/create-design-system/scripts/fetch-binary-assets.browser.js")" --filename=binary-assets.jsonWindows PowerShell,脚本位于项目技能文件夹中:
playwright-cli -s=design eval (Get-Content -Raw '.agents\skills\create-design-system\scripts\fetch-binary-assets.browser.js') --filename=binary-assets.jsonWindows PowerShell,脚本位于用户技能文件夹中:
playwright-cli -s=design eval (Get-Content -Raw (Join-Path $HOME '.agents\skills\create-design-system\scripts\fetch-binary-assets.browser.js')) --filename=binary-assets.json脚本会返回一个对象,将资源路径映射到 base64 数据。
-
解码为真实文件,并在前台运行 Node 辅助程序。使用对应的脚本位置:
Bash,脚本位于项目技能文件夹中:
node '.agents/skills/create-design-system/scripts/decode-binary-assets.cjs' binary-assets.json assetsBash,脚本位于用户技能文件夹中:
node "$HOME/.agents/skills/create-design-system/scripts/decode-binary-assets.cjs" binary-assets.json assetsWindows PowerShell,脚本位于项目技能文件夹中:
node '.agents\skills\create-design-system\scripts\decode-binary-assets.cjs' binary-assets.json assetsWindows PowerShell,脚本位于用户技能文件夹中:
node (Join-Path $HOME '.agents\skills\create-design-system\scripts\decode-binary-assets.cjs') binary-assets.json assets无论辅助程序脚本存放在哪里,它都会在当前项目中写入
assets/fonts/<family>/*和assets/favicon.*,随后输出写入的文件数量。 -
**验证并清理:**检查
assets/fonts/及其子目录。字体文件的大小应大于零且合理,顶层目录中不应出现意外文件。完成后删除临时文件binary-assets.json。
🧨 从这次事件中吸取教训——编写自己的解码循环前请先阅读。 如果页面内的代码返回的是字符串(例如
JSON.stringify(obj)),保存的文件可能会变成双重编码(一个 JSON 字符串中又包含 JSON)。此时,简单调用JSON.parse(file)会得到一个字符串,而Object.entries(string)会按每个字符生成一个条目——此前有一次运行在assets/fonts/中写入了约 600,000 个大小为 0 字节的文件,文件名为0、1、2……提供的辅助程序通过两种方式避免了这一问题:(a)fetch-binary-assets.browser.js返回的是对象,而不是序列化后的字符串;(b)decode-binary-assets.cjs如果发现结果仍是字符串,就会重新解析,且在条目数超过 200、键为数字或值短到不可能是字体时,拒绝写入。请始终优先使用这些辅助程序。如果必须自行编写,请复现这些防护措施,且绝不要在后台运行解码器。
📜 告知用户资产权利问题(必须执行)。 自定义字体和徽标可能受许可证约束。在给用户的最终回复中,指出下载的字体和徽标,并请用户在使用或再分发这些资产前核实其使用权。不要假设字体、徽标或其他品牌资产可以免费使用或再分发。
步骤 6 — 构建 design-tokens.json
将提取的值映射到结构化 JSON 中。遵循以下架构:
关闭同意提示后,其元素可能仍隐藏在 DOM 中。选择颜色和组件时,请忽略隐藏或尺寸为零的元素;应对照可见页面验证候选按钮、链接和标题,而不要轻信第一个选择器匹配结果。
{
"colors": {
"brand": { "primary": "#...", "accent": "#...", "error": "#...", "success": "#..." },
"background": { "default": "#fff", "light": "#...", "overlay": "rgba(...)" },
"text": { "primary": "#...", "secondary": "#...", "muted": "#...", "onDark": "#fff" }
},
"typography": {
"fontFamily": { "primary": "\"Font Name\", Arial, sans-serif" },
"fontWeights": { "regular": 400, "medium": 500, "semibold": 600, "bold": 700 },
"fontSize": { "sm": "12px", "base": "14px", "md": "16px", "lg": "20px", "xl": "24px" },
"lineHeight": { "tight": "1.2", "base": "1.4", "relaxed": "1.6" }
},
"spacing": {
"1": "4px", "2": "8px", "3": "12px", "4": "16px",
"5": "20px", "6": "24px", "7": "28px", "8": "32px"
},
"borderRadius": {
"none": "0px", "sm": "4px", "md": "8px", "lg": "16px",
"full": "999px", "circle": "50%"
},
"components": {
"button": { "primary": { "backgroundColor": "#...", "color": "#...", "padding": "...", "borderRadius": "..." } },
"header": { "backgroundColor": "#...", "padding": "...", "fontSize": "..." },
"nav": { "color": "#...", "fontSize": "...", "fontWeight": "..." }
}
}
保存为 assets/design-tokens.json。
步骤 7 — 编写 docs/design-guidelines.md
指南文档必须包含:
- 资源表 — 链接到所有已保存文件,路径相对于
docs/ - 颜色部分 — 令牌名称、十六进制值、用途
- 排版部分 — 字体系列(含 @font-face 详情)、字重比例、字号比例。如果已下载自定义字体(步骤 5b),请列出每个下载文件及其在仓库中的
assets/fonts/…路径,让 Agent 知道这些字体已在本地,可以在应用中使用——并添加一行说明,使用前必须确认其许可证。 - 间距部分 — 基础单位和常用值
- 边框圆角部分 — 所有圆角及其使用场景
- 组件部分 — 页眉、导航、按钮、输入框、促销栏等
- Logo 使用方式 — 如何/何处使用已保存的 Logo;如果存在反白版本,请在深色背景上使用
- 视觉风格摘要 — 用 3–5 句话描述品牌个性
保存为 docs/design-guidelines.md。
步骤 8 — 验证并交接
删除临时文件(例如 binary-assets.json、快照转储),确认 assets/ 中没有混入无关文件,并与用户一起检查生成的文件。在 Git 仓库中,运行 git status --short 检查是否包含无关更改。仅当用户要求提交且仓库的贡献流程允许时,才进行提交。
常见问题和解决方法
| 问题 | 解决方法 |
|---|---|
eval 结果占满上下文 | 添加 --filename=<file>.json 并读取该文件,而不是直接打印结果 |
| Logo 选择器未找到字标 | 检查可见的页眉图片;字标可能是指向 SVG 或栅格文件的 <img>,而非内联 SVG |
| 网站打开后显示 403 或被拦截 | 在同一浏览器会话中短暂等待后重试;如果仍被拦截,请报告情况,不要提取拦截页面 |
CLI 中没有 wait 命令 | 使用 playwright-cli -s=design run-code "async page => await page.waitForTimeout(3000)" |
eval 报错 SyntaxError: Unexpected token ';' | 参数必须是一个表达式。以 }; 结尾的脚本文件会导致错误——辅助脚本特意以 } 结尾,不带分号 |
解码器报错 require is not defined in ES module scope | 在使用 ES 模块的项目中,运行提供的 .cjs 辅助脚本,不要更改其扩展名 |
| 快照过大,无法读取 | 使用 playwright-cli -s=design find "<text>" 或 playwright-cli -s=design find --regex "/pattern/i"——它只返回带上下文的匹配节点 |
| 直接下载资源被拒绝 | 使用 scripts/fetch-binary-assets.browser.js 在页面内获取同源资源,然后使用 scripts/decode-binary-assets.cjs 解码 |
| 未发现 Cookie 横幅——最终出现在截图中 | 它会延迟加载,因此首个快照中不会出现。等待 3 秒 → 使用 find 查找横幅 → 关闭 → 再次使用 find 并确认没有匹配结果,然后截图。还要目视检查已保存的 PNG,确认画面干净。 |
| Cookie 同意对话框始终未关闭 | 提取样式前,务必检查并点击“全部接受”/“确定”/“Akceptuj wszystkie”——遮罩层会导致计算后的样式不正确 |
| 解码后生成数十万个 0 字节文件 | filename 接收器会对字符串返回值进行二次编码。在页面内脚本中返回一个对象;解码器必须重新解析字符串,并拒绝超过 200 个条目或使用数字键的对象。请使用提供的辅助脚本;切勿对解码器使用 run_in_background。 |
| 只检查了预期中的子文件夹 | 下载字体后,也要检查上级 assets/fonts/ 目录。无关文件可能出现在顶层,只检查子文件夹会漏掉这些文件。 |
计算样式返回 rgba(0, 0, 0, 0) 作为背景色 | 这表示透明——构建颜色调色板时跳过这些值 |
字体显示为 Arial,而不是品牌字体 | 检查样式表中的 @font-face 规则——品牌字体可能以自定义别名加载(例如,Euclid = Euclid Circular B) |
输出文件
所有文件都保存到项目根目录:
assets/
homepage.png # Full-page screenshot (no overlays!)
logo.<source-format> # Brand wordmark, e.g. logo.svg or logo.png (only when obtainable)
favicon.ico # Favicon binary
design-tokens.json # Structured design tokens
fonts/ # Self-hosted custom fonts (only if the site provides them)
<family>/ # e.g. brygada-1918/, libre-franklin/
*.woff2 / *.ttf # downloaded font files
docs/
design-guidelines.md # Human-readable design system reference