agentleFS
nathanonnnathanonn/agent-skills/skills/extract-design-md/SKILL.md

extract-design-md

Skill22 starsChanged 5 months agoMITView on GitHub

Extract a website's design system into a DESIGN.md file (the google-labs-code/design.md format). Use this whenever the user gives a site URL and wants to capture, reverse-engineer, clone, or document its visual identity — colors, typography, radius/spacing scales, components, and brand feel. Triggers on phrasings like "extract the design system from <url>", "make a DESIGN.md from <site>", "get the design tokens off <site>", "what's the color palette / fonts on <site>", or "I want my UI to look like <site>". It samples a few real pages with playwright-cli, reads CSS custom properties + computed styles (precise, cheap), takes light screenshots for prose, and validates with the official linter. Prefer this over eyeballing a screenshot whenever the goal is a reusable design spec.

  • Installs packages

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read it back and sanity-check varsTotal / vars count.

    line 107
  2. Very specific:

    Read the merged raw/tokens/*.json and map them onto the DESIGN.md schema.

    line 165
  3. Very specific:

    Use {path.to.token} refs inside components so values stay DRY and the linter validates them.

    line 173
  4. Very specific:

    When two themes were captured, emit DESIGN.light.md + DESIGN.dark.md — each a complete, lint-valid file.

    line 183
  5. Distinctive:

    Keep it tight and honest. State what was captured, which components were found vs.

    line 230

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Extract DESIGN.md from a site

Given a live URL, produce a DESIGN.md — YAML design tokens (colors, typography, rounded, spacing, components) plus prose in canonical section order — good enough to hand a coding agent so it builds new UI matching the source site.

Core idea (from research): tokens map ~1:1 to a site's CSS custom properties + computed styles, so the heavy lifting is a cheap in-browser extraction — not vision. Screenshots feed prose only. The official linter is a free quality gate.

URL ─▶ 1 DISCOVER ─▶ 2 EXTRACT TOKENS ─▶ 3 HARVEST COMPONENTS
        ─▶ 4 CAPTURE FEEL ─▶ 5 SYNTHESIZE ─▶ 6 VALIDATE ─▶ DESIGN.md

Prerequisites

This skill drives three external tools — make sure each is available before running:

  • playwright-cli on PATH — the in-browser extraction and screenshots run through it. Install once with npm install -g @playwright/cli@latest then playwright-cli install --skills (needs Node.js 18+). Check: which playwright-cli.
  • A reachable Firecrawl instance — used for page discovery (firecrawl map) and optional copy scraping. The hosted API or a self-hosted / local instance both work; if Firecrawl is unreachable the skill falls back to playwright link discovery (thinner page coverage). Confirm your instance responds before a run.
  • npx (Node.js) — pulls the official @google/design.md linter on demand for the quality gate. If it's unavailable the skill writes the file anyway and notes "lint skipped".

If your agent sandboxes network or filesystem access, allow the outbound calls this skill makes (headless browsing, Firecrawl, npx) before running.

Let SKILL = this skill's directory. Pick a stable playwright session name, e.g. S=designmd.

Flags

FlagDefaultEffect
<url>requiredEntry-point URL
--pages <n>5Max pages to sample
--pages-list <paths>autoPin exact pages (skips the confirm prompt)
--no-screenshotsshots ONSkip the feel pass; prose then leans on token names + copy
--theme <auto|light|dark|both>autoauto detects; both forces dual; light/dark pin one
--out <dir>.design_mds/<domain>-YYYYMMDD/Output folder
--no-lintlint ONSkip the linter (auto-skips if npx is unavailable)

Stage 1 — Discover pages

firecrawl map "<url>" > "<out>/raw/map.json"

Bucket the returned URLs into page-type slots and pick the best match per slot (always include the homepage), capped at --pages (default 5):

  • homepage (the input URL, always) — core palette + type + hero CTA
  • pricing / product — cards, toggles, tables
  • auth / login / signup / contact — inputs, form fields
  • blog / docs / changelog — long-form prose type
  • brand / about / design — often states colors & fonts explicitly; high value

Show the auto-picked list and accept add/remove/replace before crawling (one concise prompt; skip it if --pages-list was given). Skip a slot with no match — never force a sample. If Firecrawl is down, fall back to playwright link discovery (run-code returning [...document.links].map(a=>a.href)) — see references/edge-cases.md.

Stage 2 — Extract global tokens (the core)

Open the homepage, wait for it to settle, run the bundled extractor. The extractor (scripts/extract-tokens.js) reads noise-filtered :root vars, mode-samples computed styles of visible main-content elements, tags the prominent CTA + first input, and forces hover/focus — see references/extraction.md for what it returns and why.

Intermediate artifacts live under <out>/raw/ so the two deliverables stay uncluttered at the folder root — see Output layout. Create the folders once:

S=designmd
mkdir -p "<out>/raw/tokens" "<out>/raw/components" "<out>/raw/copy" "<out>/raw/logs" "<out>/screenshots"
playwright-cli -s=$S open "<homepage-url>"
sleep 2                                              # let CSS/fonts settle (FR-ROB.1)
playwright-cli -s=$S run-code "$(cat "$SKILL/scripts/extract-tokens.js")" \
  | bash "$SKILL/scripts/slice-result.sh" > "<out>/raw/tokens/tokens.home.json"

slice-result.sh strips playwright-cli's markdown wrapper so you get clean JSON. Read it back and sanity-check varsTotal / vars count. If vars is thin (the site isn't Tailwind-ish), relax the filter using varsExtra — see references/edge-cases.md (EC-FW.1 / no-design-system).

Theme detection (decision 8)

Inspect themeSignal in the JSON. A site is dual-theme when it exposes a light/dark switch — a .dark class or data-theme on <html>, or a prefers-color-scheme response. When two themes exist and --theme is auto/both, extract once per theme: toggle, re-settle, re-run the extractor into a second file (raw/tokens/tokens.home.dark.json). Toggle order of preference:

  1. click the site's own theme switch, else
  2. set the class/attribute directly, e.g. run-code doing document.documentElement.classList.add('dark') (or setAttribute('data-theme','dark')), else
  3. emulate prefers-color-scheme.

Re-sleep 2 after toggling. If the alternate theme can't be triggered reliably, capture the loaded theme only, emit one DESIGN.md, and say so in the report. --theme light|dark pins one theme even on a dual-theme site. Full detail: references/edge-cases.md (EC-TH).

Stage 3 — Harvest components + states (pages 2–N)

For each additional page, don't re-dump global vars (they're shared) — sample the components that page is good for (inputs on auth, cards/toggles on pricing, prose on blog). Reuse the same extractor; keep each page's JSON:

playwright-cli -s=$S goto "<page-2-url>"
sleep 2
playwright-cli -s=$S run-code "$(cat "$SKILL/scripts/extract-tokens.js")" \
  | bash "$SKILL/scripts/slice-result.sh" > "<out>/raw/tokens/tokens.<slug>.json"

Any bespoke per-page component captures (card/pill candidates, etc.) go to <out>/raw/components/; scraped copy to <out>/raw/copy/; per-step logs to <out>/raw/logs/.

The extractor already forces hover on the CTA and focus on the first input and returns the diffs in states. Keep only states that differ from the base (FR-ST.3) when you synthesize. Merge across pages: union the CSS vars (homepage wins the core palette, flag conflicts); accumulate components, collapsing identical styles.

Stage 4 — Capture feel (optional, default ON)

One viewport screenshot per sampled page (not full-page), into screenshots/:

playwright-cli -s=$S screenshot --filename="<out>/screenshots/<slug>.png"

Optionally firecrawl scrape --only-main-content "<url>" the homepage + brand page for copy/tagline, saved to <out>/raw/copy/. Screenshots stay at <out>/screenshots/ (they feed prose and the user may look at them). Screenshots and copy feed prose only (Overview, Do's & Don'ts) — never read token values off them. --no-screenshots skips this stage.

Stage 5 — Synthesize the DESIGN file(s)

Read the merged raw/tokens/*.json and map them onto the DESIGN.md schema. The full schema, the token→DESIGN mapping table, and the canonical prose section order live in references/design-md-schema.md — read it before writing the file. Key rules:

  • Resolve primary-palette colors to hex from the computed rgb(...) values; keep the raw CSS var (lab()/color-mix() etc.) as a fallback. Never invent a color.
  • Name colors by semantic role (--color-accent → primary/accent); fold long gray ramps into a representative neutral set — capture the system, not every shade.
  • Use {path.to.token} refs inside components so values stay DRY and the linter validates them. A primary color must exist or the linter errors.
  • Capture only components the pages actually expose (decision 10) — never invent a card/chip that isn't there. Track which of the common set (button, input, card, link, chip) were found vs. missing for the report.
  • Emit prose sections only where you have real evidence, always in canonical order, never a duplicate ## heading.

Dual-theme output (decision 8)

When two themes were captured, emit DESIGN.light.md + DESIGN.dark.md — each a complete, lint-valid file. typography, rounded, and spacing are shared; colors and any color-dependent components are per theme. A single-theme site emits one DESIGN.md. Prose may note the sibling theme exists.

Stage 6 — Validate (quality gate)

Lint every generated file:

npx -y @google/design.md lint "<out>/DESIGN.md"
  • Auto-fix structural errors only: wrong section order, broken {refs}, missing primary/required fields.
  • Flag value-level issues (out-of-gamut color, odd dimension) in the report — never silently delete a captured token to satisfy the linter (decision 12). The user decides on flagged values.
  • Surface non-fatal warnings (e.g. WCAG AA contrast) without blocking.
  • Re-lint after fixes; the final file must be error-clean. If npx/the linter is unavailable, write the file anyway and warn "lint skipped" in the report.

Output layout

The two deliverables sit at the folder root; everything intermediate is tucked under raw/ so the output reads cleanly at a glance.

.design_mds/<domain>-YYYYMMDD/        # <domain> = host with dots→hyphens
├── DESIGN.md                         # single-theme   (or DESIGN.light.md + DESIGN.dark.md)
├── screenshots/<slug>.png            # 1 desktop-viewport PNG per page (if enabled)
└── raw/                              # intermediate artifacts (audit trail + re-runs)
    ├── tokens/tokens.<slug>.json     # raw extracted token/computed-style data per page/theme
    ├── components/                   # any bespoke per-page component captures
    ├── copy/                         # scraped page copy (if screenshots stage ran)
    ├── logs/                         # per-step playwright/firecrawl logs
    └── map.json                      # firecrawl page-discovery result

The raw/tokens/*.json are always kept (decision 9) so prose can be regenerated without re-crawling and any value is traceable. Conflict guard: if the folder already exists for today's domain, ask (overwrite / suffix -2 / abort) — never silently overwrite.

Final report

Keep it tight and honest. State what was captured, which components were found vs. missing, which themes, and exactly what the linter said. If a stage degraded (no design system, Firecrawl down, lint skipped), say so — don't imply full fidelity.

DESIGN.light.md + DESIGN.dark.md → .design_mds/cursor-com-20260630/

Sampled 4 pages: home, pricing, sign-in, blog
Themes: light + dark (detected via .dark on <html>)
Captured: 9 colors/theme, 6 type roles, 7 radius, full spacing
Components found: button-primary (+hover), input-field (+focus), card · missing: chip
Lint: ✓ both files clean (1 contrast warning on dark: text-muted on bg — AA borderline)
Screenshots: screenshots/ (4, desktop viewport)

Ask-first rule (standing)

Whenever <95% confident about a mapping or an ambiguous choice — which of two colors is "primary", whether a site even has a real design system, how to resolve a same-day output conflict — ask with a recommendation rather than guess. At minimum, surface the ambiguity in the report. Any sub-agents you spawn run sequentially, never in parallel, and must carry the Firecrawl + sandbox rules above.

Bundled files

  • scripts/extract-tokens.js — the in-browser extractor for run-code (noise filter, mode sampling, CTA/input detection, hover/focus). The proven nucleus, productionized.
  • scripts/slice-result.sh — strips playwright-cli's markdown wrapper to clean JSON.
  • references/extraction.md — what the extractor returns, field by field, and how to use varsExtra to relax the filter.
  • references/design-md-schema.md — the DESIGN.md YAML schema, token→DESIGN mapping table, and canonical prose order. Read before Stage 5.
  • references/edge-cases.md — no design system, non-Tailwind, dual-theme toggling, merge conflicts, crawl/load failures, empty results.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.