create-design-system
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCreate Design System from Website
从网站创建设计系统
Extract design tokens and brand assets from any website and save them as structured files for use by agents building UIs consistent with that brand.
从任意网站提取设计令牌和品牌资产,保存为结构化文件,供构建符合该品牌 UI 的 Agent 使用。
Prerequisites
前置条件
This skill needs Node.js, Playwright CLI, and a configured browser. Check that they are available. If any are missing, briefly inform the user, then follow the one-time setup guide and install them through the permitted package manager. Ask for approval only if the environment requires it.
The examples use with one named session. If the project uses a local CLI, use its runner in each command. Do not rely on a shell alias persisting between calls. Close the session with when finished.
playwright-cliplaywright-cli -s=design close本 Skill 需要 Node.js、Playwright CLI 和已配置的浏览器。请检查它们是否可用。如有缺失,请简要告知用户,然后按照一次性设置指南通过允许的包管理器安装。仅在环境要求时才请求批准。
示例使用带有一个命名会话的 。如果项目使用本地 CLI,请在每条命令中使用其运行器。不要依赖 shell 别名在调用之间持久存在。完成后使用 关闭会话。
playwright-cliplaywright-cli -s=design closeProcess
流程
Step 1 — Prepare output directories
步骤 1 — 准备输出目录
Create the required directories before starting:
On Bash, use . On Windows PowerShell, use .
mkdir -p assets docsNew-Item -ItemType Directory -Force assets, docs | Out-Null开始前创建所需目录:
在 Bash 中,使用 。在 Windows PowerShell 中,使用 。
mkdir -p assets docsNew-Item -ItemType Directory -Force assets, docs | Out-NullStep 2 — Navigate and dismiss overlays
步骤 2 — 导航并关闭覆盖层
⚠️ MUST DO — dismiss every cookie banner / modal / overlay covering the page BEFORE you screenshot or extract styles. This is the single most common failure of this skill. An overlay both skews computed styles and ruins the screenshot.
Open the URL with the browser configured during the one-time install:
text
playwright-cli -s=design open <URL>If the page returns HTTP 403 or displays a blocked/challenge page, do not extract styles or screenshot that page. Wait briefly and retry navigation once or twice in the same session; a temporary block can clear. A configured alternative browser may be tried, but do not assume Chrome is installed or bypass a site's access controls. If the real page remains unavailable, stop and report the block instead of inventing a design system. This is a site response, not necessarily a Linux or Playwright setup problem.
Cookie/consent banners frequently load on a DELAY and are NOT present in the first snapshot. (Real example: a banner appeared only several seconds after load and silently ended up in the screenshot.) So:
- Wait for a delayed banner to mount. There is no command; use
wait:run-codebashplaywright-cli -s=design run-code "async page => await page.waitForTimeout(3000)" - Search for the banner instead of reading a whole snapshot. returns only matching nodes with context, which costs a fraction of the tokens:
findbashplaywright-cli -s=design find --regex "/cookie|consent|accept|akceptuj|zgadzam/i" - Snapshot just the banner region by its ref to see the exact buttons:
bash
playwright-cli -s=design snapshot e945 - Click the accept button by ref. Common labels: "Accept", "Accept all", "Accept All Cookies", "OK", "I agree", "Allow all", and Polish: "Akceptuj", "Akceptuj wszystkie", "Zgadzam się", "Rozumiem".
bash
playwright-cli -s=design click e955 - Confirm it is gone before continuing — re-run for the button label and expect no matches:
findbashplaywright-cli -s=design find --regex "/Accept All Cookies/i" - Also dismiss any tooltip / onboarding / newsletter popups that appear after consent.
Every command prints a snapshot link () rather than the snapshot itself. Read or grep that file only if was not enough.
.playwright-cli/page-<timestamp>.ymlfind⚠️ 必须执行 — 在截图或提取样式之前,关闭所有覆盖页面的 cookie 横幅 / 模态框 / 覆盖层。 这是本 Skill 最常见的失败原因。覆盖层既会扭曲计算出的样式,也会破坏截图效果。
使用一次性安装时配置的浏览器打开 URL:
text
playwright-cli -s=design open <URL>如果页面返回 HTTP 403 或显示被拦截/验证页面,不要提取该页面的样式或截图。稍等片刻,在同一会话中重试导航一到两次;临时拦截可能会解除。可以尝试已配置的备用浏览器,但不要假设 Chrome 已安装,也不要绕过网站的访问控制。如果真实页面仍然无法访问,请停止并报告拦截情况,而不是编造一个设计系统。这是网站的响应,不一定是 Linux 或 Playwright 的设置问题。
Cookie/同意横幅通常会延迟加载,在第一次快照中并不存在。(真实案例:一个横幅在加载数秒后才出现,悄无声息地出现在截图中。)因此:
- 等待延迟加载的横幅挂载。没有 命令;请使用
wait:run-codebashplaywright-cli -s=design run-code "async page => await page.waitForTimeout(3000)" - 搜索横幅而不是读取整个快照。仅返回带上下文的匹配节点,仅消耗极少的 token:
findbashplaywright-cli -s=design find --regex "/cookie|consent|accept|akceptuj|zgadzam/i" - 通过引用仅对横幅区域拍快照,查看确切的按钮:
bash
playwright-cli -s=design snapshot e945 - 通过引用点击接受按钮。常见标签:"Accept"、"Accept all"、"Accept All Cookies"、"OK"、"I agree"、"Allow all",以及波兰语:"Akceptuj"、"Akceptuj wszystkie"、"Zgadzam się"、"Rozumiem"。
bash
playwright-cli -s=design click e955 - 确认横幅已消失 后再继续 — 重新运行 搜索按钮标签,预期无匹配项:
findbashplaywright-cli -s=design find --regex "/Accept All Cookies/i" - 同时关闭同意后出现的任何工具提示 / 新手引导 / 邮件订阅弹窗。
每条命令都会打印一个快照链接()而不是快照本身。仅当 不够用时才读取或 grep 该文件。
.playwright-cli/page-<timestamp>.ymlfindStep 3 — Take the homepage screenshot
步骤 3 — 截取首页截图
🛑 STOP — re-check for overlays first (yes, again). Before this screenshot, take one more snapshot and confirm NO cookie banner, modal, or popup is covering content. Banners load on a delay, so even if the page looked clean in Step 2 one may have appeared since. If anything is covering the page, dismiss it (Step 2) and only then screenshot.
bash
playwright-cli -s=design screenshot --filename=assets/homepage.png --full-pageAfter saving, open with an available image viewer and visually verify there is no banner/overlay strip across it. If there is, dismiss it and retake — do not move on with a polluted screenshot.
assets/homepage.png🛑 停 — 首先再次检查覆盖层(是的,再来一次)。 在截图前,再拍一次快照,确认没有 cookie 横幅、模态框或弹窗覆盖内容。横幅是延迟加载的,所以即使步骤 2 中页面看起来很干净,之后也可能出现。如果有任何东西覆盖页面,请先关闭它(步骤 2),然后再截图。
bash
playwright-cli -s=design screenshot --filename=assets/homepage.png --full-page保存后,用可用的图像查看器打开 并目视验证 图片上没有横幅/覆盖层条带。如果有,关闭后重新截图 — 不要带着有污染的截图继续。
assets/homepage.pngStep 4 — Extract design tokens via eval (run in multiple small calls)
步骤 4 — 通过 eval 提取设计令牌(分多次小调用运行)
Each call below is a JavaScript arrow function passed to . Keep the three calls separate: a single mega-call is hard to debug and its output is hard to read.
playwright-cli -s=design evalSmall results can print inline; anything sizeable should go to a file with so it never enters the context window:
--filenamebash
playwright-cli -s=design eval "<the arrow function below>" --filename=tokens-a.json--filename下面的每个调用都是一个 JavaScript 箭头函数,传递给 。请将三个调用分开:单个巨型调用难以调试,输出也难以阅读。
playwright-cli -s=design eval小结果可以内联打印;任何较大的结果都应该通过 写入文件,这样就不会进入上下文窗口:
--filenamebash
playwright-cli -s=design eval "<the arrow function below>" --filename=tokens-a.json--filenameCall A — Core element styles
调用 A — 核心元素样式
js
() => {
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')),
};
}js
() => {
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')),
};
}Call B — Colors, radius, buttons
调用 B — 颜色、圆角、按钮
js
() => {
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
};
}js
() => {
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
};
}Call C — Logo, favicon, fonts
调用 C — Logo、favicon、字体
js
() => {
// 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
};
}js
() => {
// 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
};
}Step 5 — Save brand assets
步骤 5 — 保存品牌资产
Logo: Save to only when it contains SVG markup. If it is absent, inspect and the visible header to identify the actual wordmark, not a flag or product image. When its image URL serves SVG and the page permits fetching it, fetch the SVG in-page, validate the returned text, then save it as . A result containing SVG text may be a JSON-encoded string: inspect it and parse once, not twice.
logoSVGassets/logo.svglogoImageCandidatesassets/logo.svg--filenameIf the selected wordmark is a PNG, JPEG, or WebP image, fetch its actual bytes in the page context. Return a single-entry object mapping , , or to base64 (choose the extension from the response type or file signature), save that object with , and decode it with the existing Node helper in Step 5b, using as the input. Open the saved image to verify it. If the fetch is blocked, report that the logo could not be saved. Never put raster bytes or HTML in .
logo.pnglogo.jpglogo.webp--filename=logo-binary.jsonlogo-binary.jsonlogo.svgFavicon + custom fonts (binary assets): Use the in-page fetch helper in Step 5b on every platform. Do not use , , or PowerShell to download these assets; shell downloaders can bypass the site's same-origin context and may be denied by the agent environment. Respect the site's asset rights.
curlwgetInvoke-WebRequestTo grab just the favicon you can run the helper (it also handles fonts — see Step 5b) and keep from its output.
favicon.*Logo: 仅当 包含 SVG 标记时,才将其保存到 。如果不存在,请检查 和可见的页头,识别出实际的字标,而不是国旗或产品图片。当图片 URL 提供 SVG 且页面允许获取时,在页面内获取 SVG,验证返回的文本,然后保存为 。包含 SVG 文本的 结果可能是 JSON 编码的字符串:请检查它并仅解析一次,不要解析两次。
logoSVGassets/logo.svglogoImageCandidatesassets/logo.svg--filename如果选中的字标是 PNG、JPEG 或 WebP 图片,请在页面上下文中获取其实际字节。返回一个单条目对象,将 、 或 映射到 base64(根据响应类型或文件签名选择扩展名),使用 保存该对象,然后在步骤 5b 中使用现有的 Node 辅助工具解码,以 作为输入。打开保存的图片进行验证。如果获取被阻止,请报告无法保存 logo。切勿将栅格字节或 HTML 放入 中。
logo.pnglogo.jpglogo.webp--filename=logo-binary.jsonlogo-binary.jsonlogo.svgFavicon + 自定义字体(二进制资产): 所有平台都使用步骤 5b中的页面内获取辅助工具。不要使用 、 或 PowerShell 的 下载这些资产;shell 下载工具可能会绕过网站的同源策略,并且可能被 Agent 环境拒绝。请尊重网站的资产权利。
curlwgetInvoke-WebRequest如果只想获取 favicon,可以运行辅助工具(它也处理字体 — 见步骤 5b),并从输出中保留 。
favicon.*Step 5b — Download custom/self-hosted fonts (only if the site provides them)
步骤 5b — 下载自定义/自托管字体(仅当网站提供时)
When to do this: ONLY when the site serves its own custom fonts via self-hosted rules (font files on the site's origin, e.g. ). Skip it when the site only uses:
@font-face…/themes/site/fonts/Brand-Regular.woff2- generic system fonts (,
Arial,-apple-system, …), orSegoe UI - a third-party CDN you can't fetch same-origin (e.g. , Adobe Typekit) — these are CORS-blocked and not "fonts provided by the website" to redistribute.
fonts.gstatic.com
If the you collected in Step 4 / Call C point at real font files on the site's domain, download them:
fontFaces-
Fetch all binary assets in-page (fonts + favicon), writing base64 to a file so it never floods context. Use the command for the folder containing this skill. Run project-folder commands from the project root. If the skill lives elsewhere (for examplein the project or
.claude/skills/create-design-systemin the user folder), locate this$HOME/.claude/skills/create-design-systemand use its adjacentSKILL.mdfiles.scripts/Bash, script in the project skills folder:bashplaywright-cli -s=design eval "$(cat '.agents/skills/create-design-system/scripts/fetch-binary-assets.browser.js')" --filename=binary-assets.jsonBash, script in the user skills folder:bashplaywright-cli -s=design eval "$(cat "$HOME/.agents/skills/create-design-system/scripts/fetch-binary-assets.browser.js")" --filename=binary-assets.jsonWindows PowerShell, script in the project skills folder:powershellplaywright-cli -s=design eval (Get-Content -Raw '.agents\skills\create-design-system\scripts\fetch-binary-assets.browser.js') --filename=binary-assets.jsonWindows PowerShell, script in the user skills folder:powershellplaywright-cli -s=design eval (Get-Content -Raw (Join-Path $HOME '.agents\skills\create-design-system\scripts\fetch-binary-assets.browser.js')) --filename=binary-assets.jsonThe script returns an object mapping asset paths to base64 data. -
Decode to real files with the Node helper in the foreground. Use the matching script location:Bash, script in the project skills folder:bash
node '.agents/skills/create-design-system/scripts/decode-binary-assets.cjs' binary-assets.json assetsBash, script in the user skills folder:bashnode "$HOME/.agents/skills/create-design-system/scripts/decode-binary-assets.cjs" binary-assets.json assetsWindows PowerShell, script in the project skills folder:powershellnode '.agents\skills\create-design-system\scripts\decode-binary-assets.cjs' binary-assets.json assetsWindows PowerShell, script in the user skills folder:powershellnode (Join-Path $HOME '.agents\skills\create-design-system\scripts\decode-binary-assets.cjs') binary-assets.json assetsIt writesandassets/fonts/<family>/*in the current project, regardless of where the helper script is stored, then prints how many files it wrote.assets/favicon.* -
Verify and clean up: Inspectand its subdirectories. Font files should have nonzero, plausible sizes, and no unexpected files should appear at the top level. Delete the temporary
assets/fonts/when done.binary-assets.json
🧨 Learn from this incident — read before writing your own decode loop. If the in-page code returns a string (e.g.), the saved file can end up double-encoded (a JSON string of JSON). A naiveJSON.stringify(obj)then yields a string, andJSON.parse(file)iterates one entry per character — a previous run wrote ~600,000 zero-byte files namedObject.entries(string),0,1,… into2. The provided helpers prevent this two ways: (a)assets/fonts/returns an object, not a stringified string; (b)fetch-binary-assets.browser.jsre-parses if it still sees a string AND refuses to write if there are >200 entries, numeric keys, or values too short to be a font. Always prefer the helpers. If you must hand-roll, replicate those guards, and never run the decoder in the background.decode-binary-assets.cjs
📜 Tell the user about asset rights (REQUIRED). Custom fonts and logos may be licensed. In your final response to the user, identify the downloaded fonts and logos and ask them to verify usage rights before using or redistributing those assets. Do not assume fonts, logos, or other brand assets are free to reuse or redistribute.
何时执行: 仅当网站通过自托管的 规则提供其自有自定义字体时(字体文件位于网站的源站,例如 )。如果网站仅使用以下内容则跳过:
@font-face…/themes/site/fonts/Brand-Regular.woff2- 通用系统字体(、
Arial、-apple-system等),或Segoe UI - 无法同源获取的第三方 CDN(例如 、Adobe Typekit)— 这些会被 CORS 阻止,不属于“网站提供的可再分发字体”。
fonts.gstatic.com
如果你在步骤 4 / 调用 C 中收集的 指向网站域名上的真实字体文件,请下载它们:
fontFaces-
在页面内获取所有二进制资产(字体 + favicon),将 base64 写入文件,避免充斥上下文。使用对应于包含本 Skill 的文件夹的命令。从项目根目录运行项目文件夹命令。如果 Skill 位于其他位置(例如项目中的或用户文件夹中的
.claude/skills/create-design-system),请找到该$HOME/.claude/skills/create-design-system并使用其相邻的SKILL.md文件。scripts/Bash,项目技能文件夹中的脚本:bashplaywright-cli -s=design eval "$(cat '.agents/skills/create-design-system/scripts/fetch-binary-assets.browser.js')" --filename=binary-assets.jsonBash,用户技能文件夹中的脚本:bashplaywright-cli -s=design eval "$(cat "$HOME/.agents/skills/create-design-system/scripts/fetch-binary-assets.browser.js")" --filename=binary-assets.jsonWindows PowerShell,项目技能文件夹中的脚本:powershellplaywright-cli -s=design eval (Get-Content -Raw '.agents\skills\create-design-system\scripts\fetch-binary-assets.browser.js') --filename=binary-assets.jsonWindows PowerShell,用户技能文件夹中的脚本:powershellplaywright-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,项目技能文件夹中的脚本:bash
node '.agents/skills/create-design-system/scripts/decode-binary-assets.cjs' binary-assets.json assetsBash,用户技能文件夹中的脚本:bashnode "$HOME/.agents/skills/create-design-system/scripts/decode-binary-assets.cjs" binary-assets.json assetsWindows PowerShell,项目技能文件夹中的脚本:powershellnode '.agents\skills\create-design-system\scripts\decode-binary-assets.cjs' binary-assets.json assetsWindows PowerShell,用户技能文件夹中的脚本:powershellnode (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 的 JSON 字符串)。简单的JSON.stringify(obj)会返回一个字符串,而JSON.parse(file)会每个字符迭代一个条目 — 之前的一次运行向Object.entries(string)中写入了约 60 万个零字节文件,文件名分别为assets/fonts/、0、1……。提供的辅助工具通过两种方式防止这种情况:(a)2返回一个对象,而不是字符串化的字符串;(b)fetch-binary-assets.browser.js如果仍然看到字符串会重新解析,并且如果条目数 >200、存在数字键或值太短而不是字体,则拒绝写入。始终优先使用辅助工具。如果必须手动编写,请复制这些防护措施,并且永远不要在后台运行解码器。decode-binary-assets.cjs
📜 告知用户资产权利(必需)。 自定义字体和 logo 可能受许可保护。在你给用户的最终回复中,说明下载的字体和 logo,并要求他们在使用或再分发这些资产之前验证使用权利。不要假设字体、logo 或其他品牌资产可以自由重用或再分发。
Step 6 — Build design-tokens.json
步骤 6 — 构建 design-tokens.json
Map the extracted values into a structured JSON. Follow this schema:
After dismissing consent, its elements may remain hidden in the DOM. Ignore hidden or zero-size elements when choosing colors and components; verify candidate buttons, links, and headings against the visible page instead of trusting the first selector match.
json
{
"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": "..." }
}
}Save as .
assets/design-tokens.json将提取的值映射为结构化的 JSON。遵循此模式:
关闭同意横幅后,其元素可能仍隐藏在 DOM 中。选择颜色和组件时请忽略隐藏或零尺寸的元素;请根据可见页面验证候选按钮、链接和标题,而不是信任第一个选择器匹配项。
json
{
"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.jsonStep 7 — Write docs/design-guidelines.md
步骤 7 — 编写 docs/design-guidelines.md
The guidelines document must include:
- Assets table — links to all saved files relative to
docs/ - Colors section — token name, hex, usage
- Typography section — font family (with @font-face details), weight scale, size scale. If custom fonts were downloaded (Step 5b), list each downloaded file and its repo path under so agents know the fonts exist locally and can be used in the app — and add a one-line note that their license must be verified before use.
assets/fonts/… - Spacing section — base unit and common values
- Border radius section — all radii with usage context
- Components section — header, nav, button, inputs, promo bar etc.
- Logo usage — how/where to use the saved logo, with an inverted variant on dark backgrounds if one exists
- Visual style summary — 3–5 sentences describing the brand personality
Save as .
docs/design-guidelines.md指南文档必须包含:
- 资产表格 — 所有保存文件相对于 的链接
docs/ - 颜色部分 — 令牌名称、十六进制值、用途
- 排版部分 — 字体系列(含 @font-face 详情)、字重刻度、字号刻度。如果下载了自定义字体(步骤 5b),请列出每个下载的文件及其在 下的仓库路径,以便 Agent 知道字体存在于本地并可在应用中使用 — 并添加一行说明,指出使用前必须验证其许可证。
assets/fonts/… - 间距部分 — 基本单位和常用值
- 圆角部分 — 所有圆角及使用场景
- 组件部分 — 页头、导航、按钮、输入框、推广栏等
- Logo 使用规范 — 如何/在何处使用保存的 logo,如果存在深色背景下的反色变体也应说明
- 视觉风格总结 — 3-5 句话描述品牌个性
保存为 。
docs/design-guidelines.mdStep 8 — Verify and hand off
步骤 8 — 验证并交付
Delete temporary files (e.g. , snapshot dumps), confirm no junk slipped into , and review the generated files with the user. In a Git repository, check so unrelated changes are not included. Commit only when the user requested a commit and the repository's contribution process permits it.
binary-assets.jsonassets/git status --short删除临时文件(例如 、快照转储),确认 中没有混入垃圾文件,并与用户一起审查生成的文件。在 Git 仓库中,请运行 检查,确保不包含无关更改。仅当用户要求提交且仓库的贡献流程允许时才提交。
binary-assets.jsonassets/git status --shortCommon Pitfalls and Fixes
常见陷阱与修复方法
| Issue | Fix |
|---|---|
| Add |
| Logo selector misses the wordmark | Inspect visible header images; the wordmark may be an |
| Site opens to a 403 or blocked page | Retry briefly in the same browser session; if access remains blocked, report it and do not extract the block page |
No | Use |
| The argument must be an expression. A script file ending in |
Decoder dies with | In projects using ES modules, run the provided |
| Snapshot too large to read | Use |
| Direct asset downloads are denied | Fetch same-origin assets in-page with |
| Cookie banner missed — ends up in the screenshot | It loads on a delay and is absent from the first snapshot. Wait 3s → |
| Cookie consent dialog never dismissed | Always check for and click "Accept all" / "OK" / "Akceptuj wszystkie" before extracting styles — overlays produce incorrect computed styles |
| Decode created hundreds of thousands of 0-byte files | The |
| Verifying only the subfolders you expected | After downloading fonts, inspect the parent |
Computed style returns | This means transparent — skip these when building color palettes |
Fonts show as | Check |
| 问题 | 修复方法 |
|---|---|
| 添加 |
| Logo 选择器未命中字标 | 检查可见的页头图片;字标可能是指向 SVG 或栅格文件的 |
| 网站打开显示 403 或被拦截页面 | 在同一浏览器会话中稍等重试;如果访问仍然被阻止,请报告,不要提取拦截页面 |
CLI 中没有 | 使用 |
| 参数必须是一个表达式。以 |
解码器报错 | 在使用 ES 模块的项目中,请运行提供的 |
| 快照太大无法读取 | 使用 |
| 直接下载资产被拒绝 | 使用 |
| 遗漏 cookie 横幅 — 出现在截图中 | 它是延迟加载的,在第一次快照中不存在。等待 3 秒 → |
| Cookie 同意对话框从未关闭 | 提取样式前始终检查并点击 "Accept all" / "OK" / "Akceptuj wszystkie" — 覆盖层会导致计算出的样式不正确 |
| 解码生成了数十万个零字节文件 | |
| 仅验证你预期的子文件夹 | 下载字体后,还要检查父级 |
计算出的背景样式返回 | 这表示透明 — 构建调色板时请跳过这些 |
字体显示为 | 检查样式表中的 |
Output Files
输出文件
All files are saved to the project root:
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所有文件都保存到项目根目录:
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