Install
openclaw skills install extract-designUse this skill when the user wants to extract a webpage's design language into a reusable HTML style reference file, including typography, colors, spacing, surfaces, components, states, themes, motion, code-block styles, background atmosphere, decorative motifs, and art direction. The output should be a universal style specimen HTML for future AI-generated pages, not a 1:1 copy of the original page. Extracted style files are saved to the skill's own `assets/theme/` directory, never to the user's project.
openclaw skills install extract-designCRITICAL: All extracted style files MUST be saved to the skill's own assets/theme/ directory — never to the user's project directory, never to a relative path from the current working directory.
Before writing any output file, resolve the skill directory by running:
Glob pattern: **/skills/extract-design/SKILL.md
The directory containing that SKILL.md file is SKILL_DIR. All output goes under SKILL_DIR/assets/theme/.
Output files use the source domain or project name as a prefix:
SKILL_DIR/assets/theme/
├── {name}-style-manifest.json # Structured style manifest
└── {name}-style-specimen.html # Universal style specimen HTML
Examples (where SKILL_DIR is whatever path the Glob resolved):
{SKILL_DIR}/assets/theme/ampcode-style-manifest.json{SKILL_DIR}/assets/theme/ampcode-style-specimen.html{SKILL_DIR}/assets/theme/vercel-style-manifest.json{SKILL_DIR}/assets/theme/vercel-style-specimen.htmlCreate the assets/theme/ directory if it does not exist before writing.
The references/ directory inside this skill contains two files you MUST use during every extraction. Both paths are relative to SKILL_DIR (resolved via Glob above):
{SKILL_DIR}/references/extraction-checklist.md — comprehensive checklist for all extraction dimensions. Work through every section; note why if a dimension is not applicable.{SKILL_DIR}/references/style-specimen.html — structural template for Output C. Your generated specimen must follow the same structure.WARNING: The template contains /* REPLACE */ placeholders for every CSS token value. You MUST replace ALL of them with values measured from the target site. Do NOT keep any placeholder value in the final output. A /* REPLACE */ in the output file means the extraction is incomplete.
Use this skill when the user wants to:
Do not use this skill if the user wants:
This skill is for style system extraction, not page cloning.
Your task is not to reproduce the page structure.
Your task is to extract the page's visual system and represent it as:
The final result must be a general-purpose reference file that another AI can read and use to generate new pages in the same style.
Think in terms of:
Do not think in terms of:
The goal is:
Preserve not only the component language, but also the page atmosphere, decorative motifs, and art direction. A faithful extraction must capture both system structure and visual mood.
When using this skill, produce three outputs:
A concise explanation of the extracted style system:
A required machine-readable JSON file saved to {SKILL_DIR}/assets/theme/{name}-style-manifest.json, describing:
A single HTML file containing:
<script type="application/json">This HTML file should be general and reusable.
You must extract the following categories.
Capture:
Extract:
Do not only report raw values. Abstract them into roles such as:
--font-family-sans--font-family-mono--font-size-body--font-size-heading-lg--line-height-body--font-weight-semiboldExtract:
Abstract colors into semantic roles, not just raw hex values.
Examples:
--color-bg-page--color-surface-card--color-text-primary--color-text-secondary--color-border-default--color-brand-primary--color-code-bgExtract:
Abstract into spacing and layout tokens.
Extract:
Examples:
--radius-sm--radius-card--shadow-sm--shadow-elevated--focus-ringExtract motion and animation tokens:
Transitions (via computed styles):
@keyframes animations (require direct stylesheet parsing):
@keyframes rule must be captured separately — extract-styles.py does NOT capture theseHow to extract @keyframes — run {SKILL_DIR}/scripts/extract-keyframes.py or this browser console snippet:
const keyframes = [];
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule.type === CSSRule.KEYFRAMES_RULE) {
keyframes.push({ name: rule.name, cssText: rule.cssText });
}
}
} catch(e) { /* CORS restriction, skip */ }
}
console.log(JSON.stringify(keyframes, null, 2));
Abstract into motion tokens:
--motion-duration-fast, --motion-duration-normal--motion-ease-standard, --motion-ease-emphasizedAnimation archetypes to look for:
Extract reusable component families rather than page-specific instances.
At minimum inspect:
For each component archetype record:
This is mandatory if code blocks exist or may matter later.
Extract:
Capture:
Extract the page-level and section-level visual atmosphere, not only flat background colors.
You must inspect and abstract:
::before, ::after)Do not flatten atmospheric styling into a single background-color.
Always model it as a distinct system.
Examples of useful semantic outputs:
--bg-page-base--bg-page-overlay--bg-page-pattern--bg-hero-gradient--bg-section-muted--pattern-stripe-color--pattern-grid-color--texture-noise-opacity--ambient-glow-primary--ambient-glow-blur--decorative-line-opacityExtract repeated visual motifs that may appear in only a few places but clearly shape the site’s identity.
You must inspect and abstract:
A motif may originate in one component but still represent page-level visual DNA. Elevate it when appropriate.
Useful outputs include:
motifs.diagonal-stripemotifs.gridlinemotifs.editorial-rulemotifs.noise-overlaymotifs.ambient-glowmotifs.scanlineUse the final rendered/computed result as the ground truth.
Prioritize:
Do not rely solely on authored CSS.
Mandatory computed style checks — before recording any token value, use browser tools to read the actual computed value. Do not infer from class names, design system conventions, or framework patterns. Specifically:
document.documentElement.getAttribute('data-theme') to confirm the actual default theme on page load. Never assume dark-first or light-first from visual appearance alone.getComputedStyle(document.documentElement).getPropertyValue('--var-name') to get exact values.getComputedStyle(el).backgroundColor, .color, .borderColor etc. — not from class names.getComputedStyle(el).transition to get the actual easing curve, not a generic approximation.background-image on all elements to detect patterns, gradients, and motifs.If a value cannot be read via computed style (e.g. cross-origin stylesheet blocked, canvas-only visuals), you must label it as "inferred" in the manifest's limitations field. Do not silently use an approximated value as if it were measured.
Do not emit a token for every distinct observed value.
Instead:
Example:
Bad:
--blue-1--blue-2--blue-3--text-gray-7Better:
--color-brand-primary--color-link-default--color-link-hover--color-text-secondaryA useful reference file must explain what a value does, not only what the value is.
Always try to map raw observations into roles.
Do not assume the static DOM shows all relevant UI states.
For every component archetype in the manifest, you must record all of the following states:
If a state cannot be reliably observed, you must explicitly record it as "inferred" or "unavailable" — do not omit it silently. A missing state entry is not the same as an unavailable state.
If you see 11 cards, do not export 11 card classes unless they are meaningfully distinct.
Instead compress them into a small set of archetypes, for example:
The specimen file should use neutral placeholder content.
Do not preserve source page copy unless the user explicitly asks.
If some styles are not reliably extractable, say so.
Never emit an empty object or empty array for a field you could not extract. Instead:
null and add a note to limitations."inferred: <reason>" or add it to limitations.Bad:
"shadow": {}
"surfaces": {}
Good:
"shadow": null,
"limitations": ["Shadow styles not observed — site appears to use no box-shadow"]
Typical limitations include:
A page may feel distinctive because of background treatment, not because of buttons or cards.
Always inspect atmosphere at 4 levels: page-level (html/body/main wrappers), section-level (hero, feature, CTA), decorative-layer (pseudo-elements, absolutely positioned ornaments, SVG art-direction layers), and motif system (stripes, grids, scan lines, grain, glows, repeated patterns).
When inspecting, always check: background, background-image, background-size/repeat/position/attachment, mask, mask-image, filter, backdrop-filter, opacity, mix-blend-mode, isolation, decorative SVGs, data URI / base64 backgrounds, and pseudo-element paint layers.
If an atmospheric pattern appears only in one component but clearly represents the site's broader visual language, elevate it into a motif token rather than leaving it inside that component.
In addition to CSS facts, summarize the visual mood in plain language.
Examples:
This mood description should align with the extracted tokens and motifs.
Do not attempt to extract styles by reading static HTML source or stylesheet files alone — computed styles, CSS variable resolution, and theme state require a live browser context.
This skill ships a self-contained Python/Playwright script. Before running it, resolve SKILL_DIR using the Glob pattern **/skills/extract-design/SKILL.md (as described in Output Location above), then substitute it into the path below.
Setup (one-time):
pip install playwright
playwright install chromium
Usage:
# Light mode only
python3 {SKILL_DIR}/scripts/extract-styles.py https://example.com
# Light + dark mode
python3 {SKILL_DIR}/scripts/extract-styles.py https://example.com --dark
# Save to file
python3 {SKILL_DIR}/scripts/extract-styles.py https://example.com --dark --out /tmp/styles.json
For @keyframes extraction (required for animation system):
python3 {SKILL_DIR}/scripts/extract-keyframes.py https://example.com --out /tmp/keyframes.json
The script outputs structured JSON with light and dark keys, each containing:
cssVars — all CSS custom properties (--*) from stylesheetscolors — computed backgroundColor, color on html/body/card elementsfonts — computed fontFamily, fontSize, fontWeight on body, h1, h2button — computed styles on the first <button>backgroundImages — all elements with background-image !== none (patterns, gradients)accentColors — computed colors on links and accent/brand-classed elementstypography — h1 size, weight, letter-spacing, line-height, colorBefore starting: Open {SKILL_DIR}/references/extraction-checklist.md and keep it open throughout. Check off each item as you complete it. Use {SKILL_DIR}/references/style-specimen.html as your structural template when building Output C.
Summarize:
Collect all observed values by reading computed styles directly, not by inferring from class names or source code.
Required checks (run these before proceeding to Step 3):
document.documentElement.getAttribute('data-theme') — record the actual value on page loadgetComputedStyle(document.body).backgroundColorgetComputedStyle(document.body).colorgetComputedStyle(document.body).fontFamilydocument.styleSheets to extract all -- variables with their actual values.color and .backgroundColorbackgroundImage !== 'none' — record the full value including gradient stops and background-sizegetComputedStyle(el).transition on buttons and interactive elements — record the exact easing functionbackgroundColor, borderColor, borderRadius, boxShadow on card elements@keyframes rules from stylesheets (see section 6b for method)Record raw values first. Do not skip to naming or abstracting until all raw values are collected.
Reduce noise and create primitive tokens.
Assign role-based names.
Identify:
Identify reusable component families.
Summarize state transitions and motion behavior. If the site uses @keyframes (backgrounds, modals, loading states, hover effects):
{SKILL_DIR}/scripts/extract-keyframes.py or extract via browser consolemotion.keyframes arrayCreate a general-purpose design reference page following the structure in {SKILL_DIR}/references/style-specimen.html. Save to {SKILL_DIR}/assets/theme/{name}-style-specimen.html.
The template uses /* REPLACE */ as placeholders for every CSS token value. You must replace every single one with a value measured from the target site. Search the output file for /* REPLACE */ before saving — if any remain, the step is not complete. Add or remove motif cards and background sample cards to match what was actually found.
Ask:
If not, revise.
Before saving, verify that the manifest and specimen HTML are consistent:
manifest.components must have a corresponding rendered section in the specimen HTML.manifest.components.{} or empty array [] — use null with a limitations note instead.If they are out of sync, fix both files before finishing.
The structured manifest should generally follow this shape:
{
"meta": {},
"style_character": {},
"primitives": {},
"semantic": {},
"background": {
"page": {},
"sections": [],
"layers": [],
"gradients": [],
"patterns": [],
"images": [],
"overlays": []
},
"motifs": [],
"themes": {},
"motion": {
"duration": {},
"easing": {},
"keyframes": [
{
"name": "animationName",
"description": "What the animation does",
"from": "opacity: 0; transform: scale(0.95)",
"to": "opacity: 1; transform: scale(1)"
}
]
},
"components": [],
"responsive_rules": [],
"accessibility_notes": [],
"limitations": []
}
Note: If no @keyframes were found on the site, set motion.keyframes to null with a note in limitations.