extract-design

Extract a complete design system — colors, typography, spacing, components, shadows, and W3C design tokens — from any live website using Dembrandt. Runs a headless browser against the URL and returns real computed values from the DOM. Use when you need a site's actual design tokens, want to reverse-engineer a visual design, or need to seed a design system from an existing product.

dembrandt/dembrandt-skills523 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: extract-design
description: Extract a complete design system — colors, typography, spacing, components, shadows, and W3C design tokens — from any live website using Dembrandt. Runs a headless browser against the URL and returns real computed values from the DOM. Use when you need a site's actual design tokens, want to reverse-engineer a visual design, or need to seed a design system from an existing product.
license: MIT
---

# Extract Design — Dembrandt

Dembrandt runs a headless Chromium browser against any URL, walks up to thousands of DOM elements, reads computed CSS, and returns a structured design system: colors with confidence scoring, typography styles, spacing scale, border radius, borders, shadows, and interactive component styles.

## How to Run

```bash
# Zero-install — npx fetches the package on first run (lowest friction)
npx -y dembrandt https://dembrandt.com

# Or install once (global), then call `dembrandt` directly
npm i -g dembrandt

# Basic extraction — outputs to terminal
dembrandt https://dembrandt.com

# JSON output — pipe into files or other tools
dembrandt https://dembrandt.com --json-only > dembrandt-tokens.json

# W3C DTCG format (design-tokens.org standard)
dembrandt https://dembrandt.com --dtcg --save-output

# Generate DESIGN.md (human + AI readable brand doc)
dembrandt https://dembrandt.com --design-md

# Multi-page crawl (follows internal links)
dembrandt https://dembrandt.com --crawl 5

# Dark mode colors
dembrandt https://dembrandt.com --dark-mode

# Mobile viewport
dembrandt https://dembrandt.com --mobile

# Everything saved to output/
dembrandt https://dembrandt.com --save-output

# Tailwind v4 @theme CSS — observed values only  [dembrandt 0.28+]
dembrandt https://dembrandt.com --tailwind src/app.css

# Self-contained HTML report — open offline or attach as a CI artifact  [dembrandt 0.19+]
dembrandt https://dembrandt.com --html report.html

# Drift gate — compare against a saved baseline; exits 1 on drift  [dembrandt 0.19+]
dembrandt https://app.example.com --compare baseline.json --html report.html
```

## MCP Usage (async by default)

To expose Dembrandt as MCP tools, add this server to the agent's MCP config (no install — `npx` fetches it on first run):

```json
{ "mcpServers": { "dembrandt": { "command": "npx", "args": ["-y", "--package", "dembrandt", "dembrandt-mcp"] } } }
```

When using the Dembrandt MCP server, all extraction tools return a `job_id` immediately rather than blocking. Poll `get_job_status` until `status` is `"completed"`:

```
1. get_design_tokens({ url: "dembrandt.com", pages: 5 })
   → { job_id: "job_123_abc", status: "queued" }

2. get_job_status({ job_id: "job_123_abc" })
   → { status: "running" }   // poll again

3. get_job_status({ job_id: "job_123_abc" })
   → { status: "completed", result: { ... } }

4. get_findings({ job_id: "job_123_abc" })      // no need to resend the extraction
   → { findings: [ ... ], contrast: { ... } }
```

**Hand the `job_id` to the analysis tools instead of passing the extraction back.** Every pure tool accepts it, and the queue keeps the whole extraction for an hour, so a job started by a narrow tool such as `get_color_palette` still feeds `export_dtcg`. Passing the extraction inline works and wins when you give both, but a real extraction is far too large to travel back through the model as a tool argument.  [dembrandt 0.29+]

Pass `sync: true` to any extraction tool to block and return the result directly (useful on fast networks, risks timeout on slow sites, and proportionally slower when `pages` is above 1).

Extraction tools: `get_design_tokens` (everything), `get_color_palette`, `get_typography`, `get_component_styles`, `get_surfaces`, `get_spacing`, `get_brand_identity`. All accept `slow`, `mobile` (mobile viewport), and `cookie` (cookie string for authenticated pages); `get_design_tokens` and `get_color_palette` also accept `darkMode` and `wcag` (contrast analysis).  [dembrandt 0.23.1+ for mobile/cookie/wcag]

Every extraction tool also crawls, which is the single biggest lever on token quality: one page gives you one page's tokens.  [dembrandt 0.29+]

| Option | What |
|---|---|
| `pages` | Extract up to N pages and merge them into one token set (1 to 20). Pages come from DOM links, or from sitemap.xml when `sitemap` is true |
| `paths` | Name the extra paths explicitly, e.g. `["/pricing", "/docs"]`. Overrides discovery |
| `sitemap` | Discover from sitemap.xml. Alone it takes up to 20 pages; `pages` caps it |
| `header` | One extra HTTP header, e.g. `"Authorization: Bearer ..."`, for pages a cookie cannot reach |
| `userAgent` | Custom user agent string |
| `noSandbox` | Disable the browser sandbox. Required inside Docker and most CI containers, where launch otherwise fails |

A page that fails to load is dropped and the merge carries the rest, so a crawl does not fail on one bad URL.

Pure tools (no browser, synchronous; take an extraction object, or the `job_id` of a completed one  [dembrandt 0.29+]): `compute_drift` (0-100 drift score between two extractions; takes `baselineJobId` and `candidateJobId` as the job-based form), `get_findings` (design-system lint: contrast, consistency, duplication), `export_dtcg` (W3C Design Tokens format), `generate_design_md` (DESIGN.md brand guide), `render_report` (self-contained HTML report). Job control: `get_job_status`, `list_jobs`, `cancel_job`.  [dembrandt 0.23.1+ for get_findings/export_dtcg/generate_design_md/list_jobs]

Note: `npx` runs a `dembrandt-mcp` already on PATH in preference to the version named in `--package`, so a globally installed dembrandt silently shadows the pinned one. Symptom: options the pinned version supports are rejected as unknown, or a crawl returns a single page. Check with `dembrandt --version` and upgrade the global install, or point the MCP config at an explicit path.

Note: dembrandt <=0.23.0 fails to start via the npx one-liner above (`McpDepsMissingError`) — the MCP SDK was an optional peer dependency. Fixed in 0.23.1; require it.

## Output Structure

Dembrandt returns a structured object. The key sections:

```
colors.palette        — Deduplicated colors with confidence (high/medium/low).
                        Each entry carries hex (`normalized`), plus `lch` and
                        `oklch` of the same colour, and derived `role`,
                        `onColor`, `hover`.
colors.semantic       — Primary, secondary, background, text, and accent detection
colors.cssVariables   — Named CSS custom properties. `value` is the author's
                        string verbatim (the only record of the authored
                        notation), plus computed hex + LCH + OKLCH.
typography.styles     — Font family, size, weight, line-height per context.
                        Each entry carries `count`, the number of elements
                        rendering that exact style.
typography.sources    — Google Fonts, Adobe Fonts, variable font detection.
                        `urls` lists the resolved font asset and webfont
                        stylesheet URLs, deduped, so you can re-fetch or verify
                        the real files. `filteredFamilies` lists families
                        dropped by the usage floor — check it before concluding
                        a face is missing.
spacing.commonValues  — Margin/padding scale with rem equivalents
spacing.scaleType     — 4px, 8px, or custom grid
borderRadius.values   — Border radius tokens with element context
borders.combinations  — Width + style + color combinations
shadows               — Box shadow elevation system
components.buttons    — Button variants with hover/active/focus states
components.inputs     — Input styles with focus states
components.links      — Link colors and hover states
components.badges     — Badge/tag/chip variants
breakpoints           — Responsive breakpoints from CSS media queries
frameworks            — Detected CSS framework (Tailwind, shadcn, MUI, etc.)
iconSystem            — Detected icon library (Heroicons, FA, Material, etc.)
pages                 — Present only on a merged multi-page result (`--crawl`,
                        `--sitemap`, extra paths, or MCP `pages`). One entry per
                        page extracted, so you can tell which URLs the merged
                        tokens came from. Palette entries then also carry
                        `pageCount`.
```

## Working with Extracted Tokens

### Seeding a Tailwind theme  *(dembrandt 0.28+)*

Don't hand-map the JSON. `--tailwind` writes a Tailwind v4 `@theme` block directly:

```bash
dembrandt https://dembrandt.com --tailwind            # → output/<domain>/theme.css
dembrandt https://dembrandt.com --tailwind src/app.css  # or straight into the project
```

```css
@import "tailwindcss";

@theme {
  --color-primary: #ea580c;
  --text-display: 96px;
  --text-display--line-height: 1;
  --spacing: 8px;
  --radius-lg: 8px;
  --breakpoint-md: 700px;
}
```

Observed values only: no 50–950 shade ramps, no interpolated scale steps, no derived hover or on-colour variants. An invented shade is indistinguishable from a measured one once it is in the file, so the export is a starting point you extend by hand. Colours keep their semantic role name (`--color-primary`) or the page's own custom property name where one is declared; the rest are numbered `--color-brand-N`. Spacing collapses to v4's `--spacing` multiplier when the page has a base-N rhythm, and falls back to named steps otherwise. Tailwind's defaults still apply to anything not listed, so the block extends the theme rather than replacing it.

v4 only. For a v3 `tailwind.config.js`, map the output by hand — `colors.semantic` → `theme.colors`, `typography.styles` → `fontFamily`, `spacing.commonValues` → `spacing`, `borderRadius.values` → `borderRadius`, `shadows` → `boxShadow`.

### Seeding a shadcn/ui theme

Map semantic colors to shadcn CSS variables in HSL:

```css
:root {
  --background: /* from colors.semantic.background (0.22.0+), else colors.palette — lightest neutral */;
  --foreground: /* from colors.semantic.text (0.22.0+), else colors.palette — darkest neutral */;
  --primary: /* from colors.semantic.primary */;
  --primary-foreground: /* contrasting color */;
  --muted: /* mid-tone neutral */;
  --border: /* from borders.combinations[0].color */;
  --radius: /* from borderRadius.values[0].value */;
}
```

### Reading confidence levels

Dembrandt scores every color by semantic context:

| Confidence | Meaning |
|---|---|
| **high** | Appears on semantically labeled elements (buttons, CTAs, headers with brand classes). Almost certainly a brand color. |
| **medium** | Moderate frequency or moderate context. Likely a brand color. |
| **low** | Rare, low semantic context. May be a one-off or component-specific color. |

Since 0.28.0 confidence also has a usage floor, as spacing and radii always had: a colour seen once caps at low, twice at medium, and high needs three occurrences whatever its semantic context scores. Hover and focus colours are the exception and keep medium — their single occurrence is provenance, not a usage claim.

Start with `high` confidence colors when building a palette. Include `medium` for full coverage. Treat `low` as reference only.

### Colour notation

Never convert a colour by hand and never re-derive one with your own maths. Every palette entry and every CSS variable already carries `lch` and `oklch` alongside the hex, so read the field you need straight from the JSON. `--color-format` only changes what the terminal prints, so it is the wrong tool when you are consuming JSON or MCP output.

Use hex (`normalized`) as the identity of a colour: it is what dedup, drift comparison and every downstream tool key on. Two entries with the same hex are the same token even when their emitted notations differ. When an author declared a token in a modern notation, `cssVariables[name].value` preserves it exactly, which is what you want when writing CSS back into that codebase, since it keeps the author's own notation and stays inside their gamut.

## Flags Reference

| Flag | What it does |
|---|---|
| `--json-only` | Clean JSON to stdout — pipe into files or tools |
| `--save-output` | Save JSON to `output/<domain>/<timestamp>.json` |
| `--dtcg` | W3C Design Tokens Community Group format |
| `--design-md` | Generate `DESIGN.md` — prose-first brand doc |
| `--html [path]` | Self-contained HTML report (inline CSS, embedded JSON). Open offline or attach as a CI artifact. *(0.19+)* |
| `--compare <baseline.json>` | Diff against a saved extraction; prints a drift verdict and exits `1` on drift. CI gate. *(0.19+)* |
| `--brand-guide` | Generate a PDF brand guide |
| `--dark-mode` | Extract dark color scheme and merge into palette |
| `--mobile` | Extract at 390px mobile viewport |
| `--crawl <n>` | Crawl up to N pages and merge tokens |
| `--sitemap` | Discover pages from sitemap.xml |
| `--slow` | 3× timeouts — use on slow-loading or JS-heavy sites |
| `--screenshot <path>` | Save a full-page screenshot |
| `--raw-colors` | Include pre-filter raw colors in JSON output |
| `--color-format <fmt>` | Notation for colors printed to the terminal: `hex` (default), `rgb`, `oklch`, `lch`, `source` (as authored). Presentational only, so JSON output is unchanged, and export paths ignore it. *(0.28+)* |
| `--tailwind [path]` | Write a Tailwind v4 `@theme` CSS file — observed values only. Defaults to `output/<domain>/theme.css`. *(0.28+)* |
| `--browser firefox` | Use Firefox instead of Chromium |
| `--stealth` | Opt-in anti-detection: navigator spoofing + human mouse simulation. Use only when authorized. |
| `--user-agent <string>` | Custom user agent string |
| `--locale <string>` | Browser locale, e.g. `fi-FI`, `en-GB` (default: `en-US`) |
| `--timezone <string>` | Browser timezone, e.g. `Europe/Helsinki` (default: `America/New_York`) |
| `--accept-language <string>` | Custom `Accept-Language` header value |
| `--screen-size <WxH>` | Physical screen resolution to report, e.g. `1920x1080` |

## Drift Detection & CI  *(dembrandt 0.19+)*

`--compare` turns extraction into a gate. Save a known-good baseline, then compare later extractions against it:

```bash
# 1. capture a baseline (in the SAME environment you will check against)
dembrandt https://app.example.com --json-only > baseline.json

# 2. later — compare; exits 0 if stable, 1 if drifted
dembrandt https://app.example.com --compare baseline.json --html report.html
```

- Runs the canonical drift engine over **structured tokens** — deterministic, not a pixel/render diff.
- **Exit code:** `0` stable, `1` drift. Gates a pipeline directly.
- `--html` writes a self-contained report; with `--compare` it includes a drift banner (added/removed/changed tokens). Attach it as a CI artifact.

**Baselines churn once on 0.28.0.** Three fixes move colour and typography values: the palette usage floor, `body` ending at the 24px reading range (non-heading text above it takes `text`, so hero copy stops landing on the body token), and families under 2% of counted text being dropped. Measured on dembrandt.com against a 0.27.1 extraction, drift came out at 15 against a threshold of 10 — enough to fail a gate. On the first run after upgrading, re-approve with `--compare <baseline> --approve` or regenerate the baseline. Drift after that is real drift.

**Determinism:** capture the baseline in the *same environment* you check it in (both production, or both the same preview). A baseline from one environment compared against another shows false drift.

**In CI:** run `--compare <baseline> --html report.html` against a preview/deployed URL, fail the job on exit `1`, upload the HTML artifact. **Programmatic:** import `computeDrift` from `dembrandt/drift` and `generateHtmlReport` from `dembrandt/report` to diff and render server-side without the CLI.

## Anti-Bot and SPA Handling

Dembrandt handles common extraction challenges automatically:

- **SPA hydration** — waits 8s for React/Vue/Svelte to render before extracting
- **Lazy content** — scrolls the full page to trigger lazy-loaded components
- **Cloudflare / bot walls** — auto-retries with a visible browser if headless is blocked
- **Slow sites** — use `--slow` for 3× timeouts on heavy JS bundles
- **Cookie banners** — dismisses common CMP dialogs (OneTrust, cookielaw, GDPR patterns) automatically
- **Bot detection bypass** — use `--stealth` to opt in to navigator spoofing and human mouse simulation; off by default so the tool identifies itself honestly

## Checklist After Extraction

- [ ] Identify the 3–5 high-confidence colors — these are the core brand palette
- [ ] Check `colors.semantic.primary` — is it correct?
- [ ] Look at `typography.styles` — what are the heading and body fonts?
- [ ] Check `spacing.scaleType` — 4px or 8px grid?
- [ ] Review `components.buttons` — how many variants exist?
- [ ] Check `frameworks` — is Tailwind, shadcn, or MUI detected? This shapes how you apply the tokens.
- [ ] Use `--dark-mode` if the site has a dark theme
- [ ] Use `--crawl 3` if the site has a multi-section design system spread across routes

More Design Systems skills

stitch-design-taste

leonxlnx/taste-skill

Semantic Design System Skill for Google Stitch. Generates agent-friendly DESIGN.md files that enforce premium, anti-generic UI standards — strict typography, calibrated color, asymmetric layouts, perpetual micro-motion, and hardware-accelerated performance.

265.6k

figma

heygen-com/hyperframes

Import Figma content into a HyperFrames composition — rendered assets, brand tokens, components, storyboard sections → reconstructed motion (frames read as states, not slides) (REST/CLI), connector-assisted motion when available, and shaders from a connector or native export. Use when the user pastes a figma.com link or asks to bring a Figma design, frame, logo, brand, or animation into a video/composition.

101.8k

image

coreyhaines31/marketingskills

When the user wants to create, generate, edit, or optimize images for marketing — blog heroes, social graphics, product mockups, profile banners, listing visuals, or brand assets. Also use when the user mentions 'AI image generation,' 'generate an image,' 'create a graphic,' 'product mockup,' 'hero image,' 'social media graphic,' 'banner image,' 'cover photo,' 'profile banner,' 'listing screenshot,' 'Flux,' 'Flux Kontext,' 'Midjourney,' 'DALL-E,' 'GPT Image,' 'ChatGPT Images,' 'Ideogram,' 'Gemini image,' 'Nano Banana,' 'Recraft,' 'Stable Diffusion,' 'Canva,' 'Figma,' 'image optimization,' 'compress images,' 'WebP,' or 'OG image.' Use this for general-purpose marketing image creation and optimization. For paid ad image creative and platform-specific ad specs, see ad-creative. For video production, see video.

62.9k

← All Design Systems skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY