| name | create-design-system |
| description | Extract a design system from any website URL using Playwright MCP. Produces design tokens (JSON), brand assets (logo SVG, favicon), a homepage screenshot, and a design-guidelines.md document. Use when the user wants to clone, match, or stay consistent with an external brand. |
| allowed-tools | mcp__playwright__browser_navigate, mcp__playwright__browser_click, mcp__playwright__browser_evaluate, mcp__playwright__browser_take_screenshot, Write, Bash |
Create 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.
Process
Step 1 — Prepare output directories
Create the required directories before starting:
mkdir -p assets docs
Step 2 — Navigate and dismiss overlays
Navigate to the URL, then always check for and dismiss overlays before doing anything else. Cookie consent dialogs and welcome modals block layout and skew computed styles.
await mcp__playwright__browser_navigate({ url: "<URL>" });
Look at the snapshot for buttons like "Accept", "Accept all", "OK", "W porządku", "Zgadzam się", "Allow all". Click the first one you find:
await mcp__playwright__browser_click({ ref: "<ref>" });
Also dismiss any tooltip or onboarding popups that may appear after consent.
Step 3 — Take the homepage screenshot
await mcp__playwright__browser_take_screenshot({
filename: "assets/homepage.png"
});
Step 4 — Extract design tokens via evaluate (run in multiple small calls)
CRITICAL: Do NOT try to extract everything in a single evaluate call. Results over ~30KB will be truncated or rejected. Split extraction into 2-3 focused calls.
Call A — Core element styles
() => {
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
() => {
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);
}
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
() => {
const logoEl = document.querySelector('header .logo') ||
document.querySelector('[class*="header"] [class*="logo"]');
const logoSVG = logoEl?.innerHTML?.trim() ?? null;
const favicon = document.querySelector('link[rel*="icon"]');
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) {}
}
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: logoSVG?.substring(0, 3000),
faviconHref: favicon?.href ?? null,
fontFaces: fontFaces.slice(0, 8),
navLinks
};
}
Step 5 — Save brand assets
Logo SVG: If logoSVG was found, save it directly:
Write({ file_path: "assets/logo.svg", content: `<svg ...>...</svg>` })
Favicon: The favicon is a binary .ico file. On Windows use PowerShell, on Linux/macOS use curl:
powershell -Command "Invoke-WebRequest -Uri '<faviconHref>' -OutFile 'assets/favicon.ico'"
curl -L -o assets/favicon.ico "<faviconHref>"
Note: PowerShell may print profile warnings — these are non-fatal. Verify the file exists after:
ls assets/favicon.ico
Step 6 — Build design-tokens.json
Map the extracted values into a structured JSON. Follow this schema:
{
"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.
Step 7 — Write 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
- 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 SVG, inverted variant on dark bg
- Visual style summary — 3–5 sentences describing the brand personality
Save as docs/design-guidelines.md.
Step 8 — Commit
git add assets/ docs/design-guidelines.md
git commit -m "Docs: add <BrandName> design tokens and assets"
Common Pitfalls and Fixes
| Issue | Fix |
|---|
evaluate result exceeds token limit (>30KB) | Split into 3 separate focused calls — never extract everything at once |
| Logo selector returns country flag image | Use header .logo or [class*="header"] [class*="logo"] — the wordmark is usually an inline SVG, not <img> |
browser_wait_for error on time | The time param must be a number (integer ms), not a string |
Write tool error on path param | Use file_path, not path |
curl command denied by permissions | Use powershell -Command "Invoke-WebRequest ..." on Windows instead |
| PowerShell prints profile warnings | These are non-fatal — verify the file exists with ls |
| Cookie consent dialog never dismissed | Always check for and click "Accept all" / "OK" buttons before extracting styles — overlays produce incorrect computed styles |
Computed style returns rgba(0, 0, 0, 0) for bg | This means transparent — skip these when building color palettes |
Fonts show as Arial instead of brand font | Check @font-face rules in stylesheets — the brand font may load as a custom alias (e.g., Euclid = Euclid Circular B) |
Output Files
All files are saved to the project root:
assets/
homepage.png # Full-page screenshot
logo.svg # Brand wordmark (extracted from DOM)
favicon.ico # Favicon binary
design-tokens.json # Structured design tokens
docs/
design-guidelines.md # Human-readable design system reference