brandmd

io.github.yuvrajangadsinghPublished by GitHub account yuvrajangadsinghv1.0.1Updated Nov 14, 2026

Turn any website into a DESIGN.md: its colors, type and components, for your coding agent.

Overview

AI-generated overview

Renders a website in headless Chromium and returns its design system as a spec-valid DESIGN.md that coding agents can use as brand context.

What you can do
  • Extract a site's design system from one or more page URLs, merging several pages of the same site into a single spec.
  • Capture colors, typography, spacing, radii and component patterns measured from the rendered page, with an optional dark-mode capture.
  • Refuse block pages, login walls and near-empty pages instead of returning guessed tokens.
  • Return the DESIGN.md as the tool result, with warnings in a separate block and refusals or failures reported as tool errors.
When to use it
Useful when an assistant is writing or restyling UI and needs the real colors, fonts and components of an existing site, for example to make a page match a given brand. It fits coding-agent workflows that read a DESIGN.md from the project root.
Requirements
Runs locally over stdio via npx, so Node 20 or newer is needed. Playwright's headless Chromium must be installed; if it is missing the call fails with the install command. No API key is needed for the default extraction. Calls are capped at 180 seconds (adjustable with BRANDMD_MCP_TIMEOUT) and two run at once.
Before you install
The tool fetches and renders arbitrary public URLs from your machine, so the target site sees the request. It writes no files itself but returns content intended to be saved as a project file. Extraction is a measurement of a live page, so results change as the site changes. On Windows a cancelled or timed-out run may leave the browser open.

Installation

In SourceWeft

  1. Open brandmd in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

brandmd

[npm version] [DESIGN.md lint: clean] [License: MIT]

Stop Claude Code, Cursor, and Gemini CLI from guessing your UI. brandmd extracts any website's design system into a spec-valid DESIGN.md that passes the official @google/design.md linter.

AI coding agents generate generic screens when they don't know your colors, fonts, spacing, components, and layout rules. Run one command, drop DESIGN.md in your project root, and your agent has brand context before it writes code. Output is the official DESIGN.md format: YAML frontmatter with machine-readable tokens plus canonical prose sections. Default extraction runs locally with no API key.

bash
npx brandmd https://stripe.com -o DESIGN.md

Every generated DESIGN.md validates clean:

bash
npx @google/design.md lint DESIGN.md   # 0 errors, 0 warnings

Pass multiple URLs to merge brand tokens across pages: npx brandmd https://site.com https://site.com/pricing https://site.com/docs -o DESIGN.md

Outputs to whatever your stack reads:

  • DESIGN.md (default): for Claude Code, Cursor, Gemini CLI, Codex, Stitch
  • --json: raw extracted tokens, in brandmd's internal shape (it can change in a 1.x release, see Stability)
  • --css: CSS custom properties, drop into any project
  • --tailwind: Tailwind v4 @theme block
  • --html: visual, shareable brand guide

Combine with:

  • --agent: also writes .cursor/rules/brand.mdc and SKILL.md to both .agents/skills/brand-style/ (the universal Agent Skills path used by skills.sh across 50+ agents) and .claude/skills/brand-style/ (backward-compat for direct Claude Code users). No manual wiring. Picked up automatically by Claude Code, Cursor, Codex, Gemini CLI, Kiro CLI, and the rest of the skills.sh ecosystem. Schema-compatible with Anthropic Claude Code Skills, google/skills, and vercel-labs/agent-skills.
  • --dark: also extract dark mode tokens
  • --vision: adds illustration style, photography mood, copywriting voice, and microcopy patterns from a screenshot. Requires GEMINI_API_KEY or GOOGLE_API_KEY (free at aistudio.google.com/apikey). Uses gemini-3.8-flash; set BRANDMD_VISION_MODEL to pick another model. If the screenshot or the vision call fails the CSS-only DESIGN.md is still written and the run exits 1.

Or let your agent run it. brandmd mcp is an MCP server with one tool, extract_design_md, so Claude Code, Cursor, or any MCP client can pull a site's DESIGN.md in the middle of a task ("make this page look like linear.app"):

bash
claude mcp add brandmd -- npx -y brandmd mcp

For Cursor, Claude Desktop, and other clients that read an mcpServers config:

json
{ "mcpServers": { "brandmd": { "command": "npx", "args": ["-y", "brandmd", "mcp"] } } }

Or in one click, which adds that same config:

[Add to Cursor] [Install in VS Code]

It is also in the MCP Registry as io.github.yuvrajangadsingh/brandmd.

The tool takes one or more pages of a site (urls) and an optional dark flag, and refuses block pages and login walls the same way the CLI does. It renders pages in Playwright's headless Chromium; if that browser is missing, the call fails with the command that installs it.

A call runs for at most 180 seconds (set BRANDMD_MCP_TIMEOUT in seconds to change it), and at most two run at once (a third gets an error asking it to retry). Cancelling a call, or running out of time, stops it and closes its browser on macOS and Linux. On Windows the run is ended outright and the browser may be left open (untested).

Works as brand context for Claude Code, Cursor rules, Gemini CLI, Codex, Google Stitch, MCP servers, and any coding agent that reads markdown.

Real examples: Stripe · Linear · GitHub · Vercel · Notion · Cursor · Figma · Supabase · Raycast · +20 more in the gallery

[brandmd extracting Stripe into DESIGN.md for Claude Code, Cursor, and Google Stitch]

yaml
---version: alphaname: Stripecolors:  primary: "#533afd"  background: "#ffffff"  on-background: "#0a2540"typography:  headline-lg:    fontFamily: sohne-var    fontSize: 48px    fontWeight: 600components:  button-primary:    backgroundColor: "{colors.primary}"    textColor: "{colors.on-primary}"---
## Colors
- **Indigo** (`#533afd`): Accent background (accent)- **White** (`#ffffff`): Page background (dominant)
## Typography
**Primary font:** sohne-var

The YAML frontmatter is the machine-readable token layer; the prose sections carry the evidence-based rationale (tiered palette, confidence tags, observed component styles). brandmd gives Claude Code, Cursor, Gemini CLI, and Codex real design context: it extracts a live website's colors, typography, spacing, shadows, component patterns, and layout rules, so AI coding agents build on-brand UI instead of generic screens.

Installable example skills repo

If you want ready-made brand skills without running brandmd yourself, install yuvrajangadsingh/brand-skills directly:

bash
npx skills add yuvrajangadsingh/brand-skills

That installs 5 brandmd-generated skills (Tailwind CSS, shadcn/ui, Vercel, Mintlify, Anthropic) into your project's agent skills folder. Each skill is self-contained with a bundled references/DESIGN.md. Community examples generated from public marketing sites, not affiliated with the named brands.

Release notes
  • v1.0: Stable release. A stated 1.x contract (see Stability), bounded MCP calls, buttons read from the page's real call to action, a palette that no longer depends on colour order, and one refusal rule for every page and theme.
  • v0.14: Spec + truth release. Emits the official DESIGN.md format (YAML frontmatter + canonical sections) that passes @google/design.md lint clean. Fail-closed refusals with a distinct exit code (--allow-blocked to override), atomic writes, gradient-stop parsing, honest CTA/button selection, shadow values in DESIGN.md, and a fixed generate/parse/diff round trip.
  • v0.13: Detects block / access-denied pages (Akamai, PerimeterX, WAF 403s) and warns instead of emitting a garbage design system. Notes when a site uses motion (canvas, WebGL, Lottie, rAF).
  • v0.12: Trust-repair: evidence-based visual character, tiered palette, HSL color naming, no more scientific-notation radii, clustered type scale.
  • v0.11: diff subcommand and the hosted example gallery.
  • v0.10: Universal .agents/ skills path.
  • v0.9: --agent flag writes Cursor rule + Claude Code skill alongside DESIGN.md. CI switched to npm Trusted Publishing.
  • v0.8: More accurate primary font detection across display, heading, body, and global text roles.
  • v0.7: Cloudflare-protected sites handled by waiting up to 20s for the JS challenge.
  • v0.6: Optional --vision adds illustration style, photography mood, copywriting voice, and microcopy notes. Requires a free Gemini API key.

See CHANGELOG.md for the full history.

Examples

See all 29 DESIGN.md examples in the gallery →

Flagship deep links (DESIGN.md):

Other output formats:

  • Vercel (CSS custom properties)
  • Linear (Tailwind v4 @theme)

Why

DESIGN.md is an open spec (Apache 2.0) for encoding a design system in a format LLMs can read: YAML frontmatter with typed tokens plus canonical prose sections. There's an official @google/design.md CLI that lints, diffs, and exports it. Problem is, nobody wants to write one from scratch.

brandmd does it from the terminal. Point it at any URL, get a spec-valid DESIGN.md back that passes @google/design.md lint clean. Drop it in your project root and your AI tools start generating on-brand UI.

Install

Needs Node 20 or newer (Playwright and the Gemini SDK both require it).

bash
# Run directly (no install). On a machine without Playwright's headless# Chromium it stops and prints the command that installs it. A bare Linux# box (CI, Docker) can also lack the system libraries Chromium needs; then# it prints the install-deps command for those instead.npx brandmd https://linear.app
# Install globallynpm i -g brandmd
# As an agent skill (Claude Code, Cursor, Gemini CLI, Copilot, Codex, 30+ platforms)npx skills add yuvrajangadsingh/brandmd

Usage

bash
# DESIGN.md (default)brandmd https://stripe.combrandmd https://stripe.com -o DESIGN.md
# Multiple pages (merges tokens)brandmd https://stripe.com https://stripe.com/pricing https://stripe.com/docs
# Dark mode extractionbrandmd https://github.com --dark
# Vision: capture illustration style, photography mood, copywriting voice# (CSS can't see these; Gemini reads the screenshot.)export GEMINI_API_KEY=your-key-here  # free: https://aistudio.google.com/apikeybrandmd https://linear.app --vision# Sites behind Cloudflare or AWS WAF: brandmd waits up to 20s for the JS# challenge to auto-resolve. If your site needs longer, pass --cf-wait-ms 30000.# If the challenge persists, brandmd refuses (exit 2) rather than writing garbage tokens.
# CSS custom propertiesbrandmd https://vercel.com --css
# Tailwind v4 @themebrandmd https://linear.app --tailwind
# HTML brand guide (visual, shareable)brandmd https://github.com --html
# Raw tokens as JSONbrandmd https://stripe.com --json
# Compare two extractionsbrandmd diff examples/stripe.md examples/vercel.md --out BRAND_DIFF.md

Refusals and exit codes

brandmd fails closed. If a page is a bot-block / access-denied page (a Cloudflare challenge that never clears included), a login / sign-in wall landing, or too thin to describe (no rendered text, empty palette), it refuses in every format and writes no artifact, so a bad capture can't silently overwrite a good DESIGN.md. A login wall means a password field on screen when the page loads, or sign-in copy on an auth page: a /login-style URL, or another host the request was sent to. A hidden or off-screen sign-up form, or a "Sign up free" headline on an ordinary page, is not one. A plain cross-origin redirect (say marketing -> app on another domain) doesn't refuse: it warns on stderr and is flagged in the output's provenance block, per page.

Every capture goes through the same gate, page by page. One refused page (a block page, a login wall, or a page with almost no text, even next to a rich one) refuses a multi-page run, and with --dark the dark capture is checked too. A page that fails to load (a timeout, a DNS error) is left out with a warning and the rest are merged; if none load, the run exits 1. --dark that gets no dark capture, like --vision that gets no vision result, still writes the light output and exits 1.

Exit codeMeaning
0success
1operational or validation error (bad URL, bad flag, browser launch failure, no page loaded, --dark or --vision came back empty)
2refused: block page, insufficient evidence, or a login-wall landing, on any page or theme
3brandmd check only: the page drifted from the spec

Pass --allow-blocked to force output anyway; whatever the refusal was, the artifact carries a refused-source marker in every format (Markdown callout, CSS/Tailwind comment header, HTML banner, JSON blockLikely). Writes are transactional (temp file + rename with rollback), including the --agent set and brandmd diff, so a partial failure never truncates an existing file. Atomicity is per process: concurrent brandmd runs against the same output directory are not coordinated, though collision-safe temp/backup names mean one run can never eat another's files.

Gallery

See 4 real DESIGN.md snapshots in the browser (Stripe, Vercel, Linear, Mintlify), or scan the examples/ folder for all 29. Each snapshot is generated from a single public page visit and is observed, not canonical.

Check (drift detection in CI)

brandmd diff compares two brands. brandmd check asks a different question: has what we shipped drifted from the spec we agreed on?

bash
brandmd check https://staging.acme.com --against DESIGN.md
Design drift: 2 major, 1 minorhttps://staging.acme.com vs DESIGN.md
  major  color      role "secondary" repainted: #29227D -> #7F7DFC  major  color      role "on-secondary" repainted: #FFFFFF -> #1A1A1A  minor  theme      mood reads as "...vivid blue accents", spec says "...blue accents"

Colours are compared by role, not as a set of hexes. One hex legitimately fills several roles (#FFFFFF is often background and on-primary), so a set comparison silently misses the drift that matters: the hex behind primary changing while that hex still appears elsewhere.

What fails the build. Losing or repainting a role, and a changed primary or secondary font, are major. New roles, extra spacing steps and reworded theme descriptions are minor and pass by default, because they are usually a new component landing rather than a regression. Theme wording is brandmd's own interpretation, so it never fails a build on its own.

Flag
--against <file>the committed DESIGN.md (required)
--report <file>write the drift as JSON for CI annotations
--fail-on <level>major (default), any, or none
--allow-blockedcompare anyway if the page is refused (blocked, thin, or a login wall)
--cf-wait-ms <ms>how long to wait for a Cloudflare or AWS WAF challenge (default 20000)
--debugprint the raw error instead of a one-line remediation
--viewport <WxH>pin the viewport (default 1440x900)
--locale <tag>pin the browser locale
--reduced-motionrequest prefers-reduced-motion: reduce

Pin the viewport in CI. It decides which breakpoint renders, so an unpinned run can legitimately extract a different design system on a different machine and report drift that isn't there.

A blocked page, an evidence-thin page, or a login-wall landing refuses with exit 2 rather than reporting drift. Diffing a Cloudflare challenge against a real spec would flag every token as changed, and a check that cries wolf is one people turn off. An empty or truncated DESIGN.md exits 1, because a gate that silently stops gating is worse than no gate.

What check does not see

Worth knowing before you rely on it:

  • Component drift is reported but does not fail a build by default. Background, text colour and radius are compared on the machine-token components; height and padding are not. Components are the least certain part of an extraction (a button that fades in, or one that paints no fill at rest, is missed; see Known limits), so component drift is always minor. Opt in with --fail-on any.
  • secondaryFont is unreliable. The live side is round-tripped through the same generator and parser as a written DESIGN.md, which keeps both sides honestly comparable but drops fields the markdown does not carry cleanly. A secondary-font change can pass unnoticed.
  • Majors can flip on sites that change between visits. Each colour cluster is led by its most-used colour, so the order a page lists its colours in no longer changes the palette or the text roles, but rotating hero imagery or an A/B test that changes how often a colour is used can still repaint a role. On static marketing pages runs are byte-identical; on dynamic ones, re-run before believing a surprising major.

In GitHub Actions

yaml
- name: Check design drift  run: npx brandmd check "${{ steps.deploy.outputs.preview-url }}" \         --against DESIGN.md --report drift.json

Exit 3 fails the job on drift; 1 and 2 still mean the tool errored or refused, so a broken preview never reads as a design change. When drift is intentional, update DESIGN.md in the same PR and the check goes green.

Diff

bash
brandmd diff examples/stripe.md examples/vercel.md --out BRAND_DIFF.md

Compares two DESIGN.md files and writes a markdown diff: shared and unique colors, typography table, spacing and radii table, per-component property diff, and a "what to copy / what to avoid" synthesis. Local files only. See examples/diff-stripe-vs-vercel.md for the output format.

Output formats

DESIGN.md (default)

The official DESIGN.md format: YAML frontmatter (machine-readable colors, typography, rounded, spacing, components tokens) plus canonical sections (Overview, Colors, Typography, Layout, Elevation & Depth, Shapes, Components, Do's and Don'ts). Validates clean under @google/design.md lint. Drop it in your project root and AI coding agents use it to generate on-brand UI.

CSS custom properties (--css)

css
:root {  --color-accents-1: #FAFAFA;  --color-blue-600: #0075DE;  --font-primary: "Geist";  --space-1: 4px;  --radius-sm: 4px;  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);}

Tailwind v4 (--tailwind)

css
@import "tailwindcss";
@theme {  --color-primary: #0075DE;  --font-sans: "Geist", system-ui, sans-serif;  /* Spacing observed: 4px, 8px, 16px, 24px. Not written as --spacing-* keys. */  --radius-sm: 4px;}

HTML brand guide (--html)

A self-contained dark-themed HTML page with color swatches, font specimens, spacing visualization, and shadow examples. Open it in a browser or share it with stakeholders.

JSON (--json)

Raw extracted tokens for programmatic use. The shape is brandmd's internal one and can change in any 1.x release; see Stability.

Multi-page extraction

Pass multiple URLs to merge tokens from different pages into one DESIGN.md. Each page contributes equally regardless of length: token frequencies are normalized per page, and components carry a per-page weight, so a docs page with fifty buttons can't outvote a homepage with three. What that decides is on how many pages a pattern appears, not how many times it appears anywhere, which is the right question to ask of a brand pattern.

bash
brandmd https://stripe.com https://stripe.com/pricing https://stripe.com/docs

Failed pages are skipped with a warning. Mixed domains show a warning.

Dark mode

Extract dark theme tokens as a separate section:

bash
brandmd https://github.com --dark

Adds a "Dark Mode Overrides" section to the DESIGN.md with the dark color palette. Uses prefers-color-scheme: dark via Playwright, so it captures what users actually see in dark mode. Affects DESIGN.md and --json output (ignored for --css, --tailwind and --html).

What it extracts

  • CSS custom properties from :root and @media rules (uses actual variable names when available)
  • Colors with semantic roles (background, text, accent, border)
  • Typography with role-aware Primary detection (display > heading > body), per-role font breakdown, and a full "all detected fonts" frequency list. Skips monospace, default fallbacks, and icon fonts when picking Primary.
  • Spacing scale and base grid unit
  • Border radii and shadow styles
  • Component patterns (buttons, cards, inputs). A button is a <button>, a role="button", or a link classed or painted like one (a fill, a gradient or a border other than an underline, with a short visible label); hidden and off-page controls, skip links and controls inside a <table> (a sample email, a data grid) are left out, unless every control on the page sits in a table, and a control with no label text (an icon-only launcher, or only an icon font's glyphs) is never the primary button. The primary button is the most repeated fill with real colour (LCH chroma), unless that colour appears once and a neutral fill repeats more; then, as when every fill is neutral, it is the neutral that stands out most against the page. colors.primary is that button's fill as the page shows it (a translucent fill composited over the page, a gradient button's first stop); with no solid button, or one in the page's own colour, it falls back to the most vivid accent colour. The prose names the element each button was sampled from.

Known limits

  • A hero button that fades in as the page loads or scrolls can be missed. A headless capture may never run the page's reveal, so the button stays at opacity 0 and is left out, and the primary comes from the other buttons.
  • A page whose buttons paint no fill at rest (transparent until hovered) has no primary button to sample. Unless another accent colour stands out on the page, colors.primary falls back to the page colour and the output says it is low confidence. clerk.com is one in the gallery.

How it works

  1. Renders each page in a headless browser (Playwright)
  2. Scrolls through to trigger lazy-loaded content
  3. Dismisses cookie banners and overlays
  4. Extracts CSS custom properties from :root (recurses into @media rules)
  5. Extracts computed styles from every visible element
  6. Clusters similar colors, identifies the spacing scale
  7. Merges tokens across pages (normalized per page)
  8. Outputs in your chosen format

No LLM calls, no API keys, runs entirely on your machine.

Agent skill

brandmd ships as an Agent Skill that works across 30+ platforms including Claude Code, Cursor, VS Code/Copilot, Codex, and Gemini CLI.

bash
npx skills add yuvrajangadsingh/brandmd

Then tell your agent: "extract the design system from https://linear.app"

If brandmd saved you time, consider starring the repo.

Built by Yuvraj Angad Singh. Also: vibecheck (catch AI-generated code smells) and vemb (embeddings from the command line). Follow on GitHub for new dev-tool experiments.

Stability

From 1.0.0 brandmd follows semver for the parts below. A 1.x release can add to them (a flag, a section, a tool input); removing or changing one waits for 2.0.

Covered:

  • Commands and flags. brandmd <urls...>, brandmd diff, brandmd check and brandmd mcp, with every flag their --help lists: -o, --json, --css, --tailwind, --html, --dark, --vision, --agent, --allow-blocked, --debug, --cf-wait-ms, --viewport, --locale and --reduced-motion; diff --out; check --against, --report and --fail-on.
  • Exit codes. 0, 1, 2 and 3, as in the table above.
  • The DESIGN.md shape. The frontmatter keys version, name, description, colors, typography, rounded, spacing and components, and the sections Overview, Colors, Typography, Layout, Elevation & Depth, Shapes, Components and Do's and Don'ts, plus Dark Mode Overrides with --dark and Visual Identity Beyond CSS with --vision. The linter it is checked against is @google/[email protected]: CI fails on any error or warning it reports for generated test files, and every example in the gallery lints clean. A later linter version counts as supported once a brandmd release says so.
  • The MCP tool. extract_design_md, its urls and dark inputs, and its result: the DESIGN.md as the first text block, warnings as a second one, and refusals and failures as tool errors carrying the CLI's message.
  • Environment variables. GEMINI_API_KEY or GOOGLE_API_KEY and BRANDMD_VISION_MODEL for --vision, and BRANDMD_MCP_TIMEOUT for the MCP server.
  • Node 20 or newer.

Not covered, so any 1.x release can change them:

  • The values. brandmd measures a live page, so a site change or a better extraction changes colours, fonts and components. Pin the brandmd version and commit the DESIGN.md when you need the same file twice.
  • The JSON from --json and check --report. Both are internal JSON formats (--json prints the extracted tokens, --report the drift list), so field names and nesting can change. The MCP tool returns DESIGN.md, not this JSON. When a script needs something stable, read the DESIGN.md, or check's exit code.
  • What --css, --tailwind and --html print. The flags stay; the variable names and layout follow the page.
  • Wording. Prose sentences, warnings and error messages.
  • The MCP limits. Today a call runs for at most 180 seconds (BRANDMD_MCP_TIMEOUT), two run at once, and a cancelled or timed-out call stops its run and closes its browser on macOS and Linux. Every extraction's scroll pass stops after 50 screens and starts no new screen after 15 seconds. These numbers can be tuned.

License

MIT

Source: README.md at commit 12595eb

Tools

1

extract_design_md

Render a website in headless Chromium and return its design system as a spec-valid DESIGN.md: colors, typography, spacing, radii and components, measured from the page. Several pages of one site merge into one design system. Refuses block pages, login walls and near-empty pages instead of guessing.

Parametersdarkurlsrequired

Version history

1
  1. v1.0.1LatestNov 14, 2026

Score

90% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts
  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts