| name | brand-design |
| description | Extract brand assets (fonts, colors, logos) from any website using browser automation. Use when the user mentions brand, brand assets, fonts from a website, colors, logo extraction, brand identity, brand config, or brand guidelines. |
Skill: Brand Asset Extraction
Metadata
name: brand-asset-extraction
version: 1.0.0
updated: 2026-02-22
author: InstaClaw
triggers:
keywords: [brand, extract brand, brand assets, fonts from website, colors from website, logo, brand identity, brand consistency, match brand, brand config, brand guidelines]
phrases: ["extract brand assets", "get the fonts from", "match the brand", "what fonts does this site use", "brand colors", "find the logo", "create brand config"]
NOT: [design logo, create brand, rebrand]
Overview
Automated extraction and documentation of brand assets (fonts, colors, logos) from any website using browser automation. This is the foundational skill that feeds into Remotion video production, Kling AI prompting, and any branded content creation. Without accurate brand assets, nothing looks professional.
This skill is documentation + code patterns, no separate scripts. The agent uses its browser tool directly with the patterns documented here.
Dependencies
- Browser automation (Playwright/Chromium, pre-installed on VM snapshot)
- No external API keys required
What This Skill Extracts
- Typography — Font families, weights, hierarchy (heading vs body vs button)
- Colors — Primary, secondary, background, text colors (as hex codes)
- Logo variants — White, dark, color, transparent (URLs + download)
- Spacing/sizing patterns — Common sizes, margins (for layout consistency)
Complete Extraction Workflow
Step 1: Open Target Website
const tab = await browser.open({
profile: "openclaw",
targetUrl: "https://target-brand.com"
});
Step 2: Extract Fonts
browser.act({
request: {
kind: "evaluate",
fn: `() => {
const selectors = ['body', 'h1', 'h2', 'h3', 'h4', 'p', 'button', 'a'];
const fonts = {};
selectors.forEach(sel => {
const el = document.querySelector(sel);
if (el) {
const style = window.getComputedStyle(el);
fonts[sel] = {
family: style.fontFamily,
weight: style.fontWeight,
size: style.fontSize,
lineHeight: style.lineHeight
};
}
});
return fonts;
}`
}
});
Font troubleshooting:
- If font shows as generic ("sans-serif"): font may be loaded via CSS
@font-face. Check network tab for font file requests.
- Quote multi-word font names:
'"Instrument Serif", serif' — quotes needed in CSS.
Step 3: Extract Colors (with RGB → Hex Conversion)
browser.act({
request: {
kind: "evaluate",
fn: `() => {
const rgbToHex = (rgb) => {
const match = rgb.match(/\\d+/g);
if (!match) return null;
const [r, g, b] = match.map(Number);
return '#' + [r, g, b].map(x => x.toString(16).padStart(2, '0')).join('');
};
const colorFrequency = {};
document.querySelectorAll('*').forEach(el => {
const styles = getComputedStyle(el);
// Background colors
const bg = styles.backgroundColor;
if (bg && bg !== 'rgba(0, 0, 0, 0)') {
const hex = rgbToHex(bg);
if (hex) colorFrequency[hex] = (colorFrequency[hex] || 0) + 1;
}
// Text colors
const color = styles.color;
if (color) {
const hex = rgbToHex(color);
if (hex) colorFrequency['text:' + hex] = (colorFrequency['text:' + hex] || 0) + 1;
}
});
return Object.entries(colorFrequency)
.sort((a, b) => b[1] - a[1])
.slice(0, 15)
.map(([color, count]) => ({ color, count }));
}`
}
});
Color troubleshooting:
- Element might have gradient: check
background-image and background in addition to backgroundColor.
- Transparency: check
opacity property. Some "colors" are transparent overlays.
Step 4: Find Logo URLs
browser.act({
request: {
kind: "evaluate",
fn: `() => {
const logos = [];
// Image-based logos
const selectors = [
'img[alt*="logo" i]', '[class*="logo"] img',
'header img', 'nav img', '.navbar-brand img',
'[id*="logo"] img', 'a[href="/"] img'
];
selectors.forEach(sel => {
document.querySelectorAll(sel).forEach(img => {
logos.push({
type: 'img',
src: img.src,
alt: img.alt,
width: img.naturalWidth || img.width,
height: img.naturalHeight || img.height
});
});
});
// SVG logos
document.querySelectorAll('svg[class*="logo"], header svg, nav svg, [id*="logo"] svg').forEach(svg => {
logos.push({
type: 'svg',
viewBox: svg.getAttribute('viewBox'),
html: svg.outerHTML.substring(0, 500)
});
});
// CSS background logos
document.querySelectorAll('[class*="logo"]').forEach(el => {
const bg = getComputedStyle(el).backgroundImage;
if (bg && bg !== 'none') {
logos.push({ type: 'css-background', value: bg });
}
});
return logos;
}`
}
});
Logo troubleshooting:
- Logo might not be an
<img> tag — check for inline SVGs, CSS background images, or icon fonts.
- Always find both light and dark variants. Check footer (often has different variant than header).
Step 5: Take Reference Screenshot
browser.screenshot({
profile: "openclaw",
targetId: tab.targetId,
fullPage: true
});
Step 6: Generate Brand Config File
Save extracted data as brand-config.json:
{
"brand": "Company Name",
"extracted_from": "https://example.com",
"extracted_at": "2026-02-22T00:00:00Z",
"typography": {
"heading": {
"family": "\"Instrument Serif\", serif",
"weights": [400, 700],
"use": "Headlines, hero text"
},
"body": {
"family": "Inter, sans-serif",
"weights": [400, 500, 600],
"use": "Body copy, UI elements"
}
Logo Variant Rules (Most Common Mistake)
| Background | Logo Variant | Result |
|---|
| Dark (#0f1419) | White logo | ✅ Visible |
| Dark (#0f1419) | Dark/black logo | ❌ INVISIBLE |
| Light (#f5f3ee) | Dark logo | ✅ Visible |
| Light (#f5f3ee) | White logo | ❌ INVISIBLE |
| Gradient | Test both | Depends on dominant color |
This is the #1 mistake agents make. The InstaClaw video production proved it — v1-v2 had invisible logos due to wrong contrast variant.
Real Example: InstaClaw Brand Extraction
Extracted from: https://instaclaw.io
Typography:
heading: "Instrument Serif", serif (display font, warm/editorial)
body: Inter, sans-serif (clean, modern UI font)
Colors:
primary: #e67e4d (orange — the claw color)
dark: #0f1419 (almost black — backgrounds)
light: #f5f3ee (off-white — content areas)
Logos:
white variant: used on dark backgrounds (CRITICAL)
color variant: used on light backgrounds
Key learning: The white logo variant was the make-or-break discovery.
Integration with Other Skills
| Skill | How Brand Assets Are Used |
|---|
| Remotion Video (Skill 1) | Fonts → fontFamily styles, colors → backgrounds/text, logos → staticFile() |
| Social Media (Skill 9) | Colors inform visual content style, brand voice informs copy |
| Email (Skill 8) | Brand colors/logo for email templates |
| Any branded content | brand-config.json is the single source of truth |
Quality Checklist
Files
~/.openclaw/skills/brand-design/SKILL.md — This file (the complete skill)
~/.openclaw/skills/brand-design/references/brand-extraction-guide.md — Quick reference card
Error Handling & Recovery
Browser Failure Protocol
If the browser tool fails during brand extraction:
- NEVER go silent. The user is waiting for your response.
- First try:
web_fetch(url) — may get partial CSS/HTML for color/font extraction.
- Second try:
python3 ~/scripts/crawlee-scrape.py --url "URL" --mode light — stealth fetch.
- Third try:
python3 ~/scripts/crawlee-scrape.py --url "URL" --mode browser — full browser with anti-detection.
- If all fail: Tell the user the site blocked automated access and suggest alternatives (e.g., "Can you share a screenshot of the website?" or "I can work with the colors/fonts you describe manually.").
Timeout Handling
- If a browser page load takes more than 30 seconds, abort and try the next tier.
- If JavaScript
evaluate calls hang, kill the browser session and try web_fetch instead.
- NEVER wait indefinitely for a page to load or a script to execute.
Retry Limits
- Maximum 2 retries per extraction method.
- After 2 failures, escalate to the next tier (browser → web_fetch → crawlee → manual).
- NEVER enter a retry loop. NEVER go silent after an error.