CLI tool
dembrandt/dembrandt avatar
dembrandt/dembrandt

Dembrandt: extracting a site's design tokens with Playwright and enforcing them in CI

Extract any website’s design system into tokens in seconds: logo, colors, typography, borders & more. One command.

3,474 stars308 forksTypeScriptMIT

At a glance

What is it?
Dembrandt is a TypeScript CLI that renders a URL in Chromium, reads computed styles from the DOM, and emits W3C design tokens. Its CI mode turns token drift into a failing exit code, but only for sites whose styles live in the DOM.
Who is it for?
Adopt Dembrandt if you need a token baseline for a DOM-rendered site and want drift to fail a build rather than be noticed in review; the CI gate is an exit code plus per-token changes[], which fits any runner. Do not adopt it for canvas or WebGL surfaces, where the README states there is no DOM to read, and do not expect dark mode without passing --dark-mode.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap Dembrandt fills between a live site and a token file

Design tokens usually exist in one of two places: a Figma library that has drifted from production, or a stylesheet nobody has read end to end. Dembrandt takes a third route. It points a headless browser at a URL, lets the page render, and reads the computed styles back out of the DOM. The output is a token set covering colors, typography, spacing, borders, shadows, motion, components, breakpoints, and icons. The README frames the audience in the tag list as much as the prose: design engineers, design ops, and frontend developers doing brand audits or reverse-engineering a site they do not control. The obvious use case is a competitor benchmark or a migration where the source of truth is the deployed site, not a design file. The less obvious one is drift detection, where the same extraction runs against a preview deployment on every pull request and the diff becomes the signal.

Playwright renders, then the analyser reads computed styles

The mechanism is a two-stage pipeline. Playwright loads the page in Chromium; dembrandt then reads computed styles from the DOM and runs analysis over them. According to the README, that analysis covers color usage and confidence scoring, grouping of similar typography, spacing pattern detection, and finally token emission. Two details matter for interpreting the output. First, confidence scoring means a color seen once in a hover state is weighted differently from one used across the page, so the token file is a ranked view rather than a raw dump. Second, the crawl mode merges several pages into one output with what the README calls cross-page confidence boosting, which implies single-page extraction is the weaker mode. The MCP section says the same thing more directly: setting pages above 1 produces a markedly stronger token set than one page. If you extract a single URL and treat the result as the system, you are reading a sample, not the system.

Install, the browser step, and the flags that change the output

Installation is a global npm package plus a one-time browser fetch. The README gives the sequence: npm install -g dembrandt, then dembrandt install-browser, then dembrandt dembrandt.com. The browser step is not optional and the failure mode is explicit: dembrandt drives Chromium through playwright-core, which ships no browser binaries, so a fresh install has nothing to launch and fails with browser engine not available. The npx route has the same requirement, so run npx dembrandt install-browser first. Browsers land in a shared Playwright cache, so either route needs the step once. Node.js 18 or later is required. The default output is formatted terminal text only. Flags change the shape of the result: --save-output writes JSON to output/dembrandt.com/TIMESTAMP.json, --dtcg emits W3C Design Tokens for Style Dictionary or Tokens Studio, --design-md writes a DESIGN.md for AI agents, --tailwind emits Tailwind v4 @theme CSS using observed values only, --wcag adds WCAG 2.1 contrast checks over real DOM pairs with AA and AAA grades, --crawl 10 merges ten pages, and --slow triples timeouts for JavaScript-heavy sites. The docs/usage.md file covers mobile, dark mode, browser selection and CDP, brand guide PDF, motion tokens, and fingerprint options. Note that --tailwind is scoped to observed values, which is a deliberate limit: it will not invent a scale you did not ship.

The CI gate is an exit code, not a dashboard

The drift workflow has two forms. On GitHub, a step uses dembrandt/dembrandt@v0.32.2 with a url and a baseline path, and the action annotates the pull request with the drifted tokens. On any other runner, the README describes the platform-neutral version: dembrandt URL --compare baseline.json --json-only exits 1 on drift and prints per-token changes[]. That is the whole gate. There is no server component required, no dashboard to check, and no state beyond the committed baseline file. The exit code table and the Action inputs live in docs/ci.md. This design has a consequence worth naming: the baseline is a file in your repository, so drift detection is only as good as your willingness to review baseline changes. If a failing job is routinely fixed by regenerating the baseline, the gate has become a formality. The compare mode also depends on the extraction being stable, which is why the --slow flag and the crawl settings matter more in CI than they do in a one-off audit.

MCP tools, job ids, and the authenticated-page options

Dembrandt exposes an MCP server for Claude Code, Cursor, Windsurf, and other compatible clients, registered either with claude mcp add --transport stdio dembrandt -- npx -y --package dembrandt dembrandt-mcp or through an .mcp.json entry. The tool list splits into extraction tools such as get_design_tokens, get_color_palette, get_typography, get_component_styles, get_surfaces, get_spacing, and get_brand_identity, and pure analysis tools such as compute_drift, get_findings, export_dtcg, generate_design_md, and render_report. Extraction returns a job_id; you poll get_job_status and then pass the same id to the pure tools rather than feeding the extraction back in as an argument. That indirection is the part most likely to trip up an agent integration, because it means the tools are stateful across calls. Extraction tools accept slow, mobile, darkMode, wcag, cookie and header for authenticated pages, userAgent, and noSandbox for Docker and most CI containers. The cookie and header options are what make internal or gated pages reachable, and noSandbox is the flag that keeps container runs from failing.

Where the DOM-reading approach breaks down

The limitations section is short and specific, and it should shape your decision more than the feature list. Dark mode is not automatically detected and requires the --dark-mode flag, so a single default run describes the light theme only. Hover and focus states are extracted from CSS rather than by interacting with the page, which means states produced by JavaScript at runtime may not appear. Canvas and WebGL-rendered sites cannot be analyzed at all, because there is no DOM to read. That last one rules out a whole class of marketing sites and product UIs. The README text is truncated at the JavaScript-heavy point, so there may be further caveats beyond what is quoted here; treat the list as incomplete rather than exhaustive. A second, softer limitation comes from the confidence model itself. Tokens are inferred from usage, so a color that appears once in a rarely visited component may be underweighted, and a color used heavily in a footer may be overrepresented. Crawling more pages is the documented mitigation, not a fix.

How it differs from hand-auditing CSS or using a design-token linter

The alternative most teams already have is a manual audit: open devtools, read the stylesheet, copy hex values into a tokens file. That approach reads the source CSS, which means it can see rules that never render, including unused classes and dead variables. Dembrandt reads the opposite surface. It sees only what the browser computed for the page you loaded, so unused CSS is invisible and runtime-injected styles are visible. Neither view is complete, and the difference decides which tool fits. If your goal is to clean up a stylesheet, a source-level linter or a manual pass is the better instrument, because Dembrandt cannot tell you about a token that is defined but never applied. If your goal is to know what a deployed site actually looks like, including styles injected at runtime, the rendered-DOM approach is the only one that answers the question. A second difference is the drift gate. A linter checks your source for violations of rules you wrote; Dembrandt compares two extractions of the same URL and reports per-token changes, which catches drift that no style rule forbids. The cost is that it needs a browser and a network path to the target on every run.

Maintenance cost, licensing, and what to verify before you commit

The project ships frequently, with v0.32.2, v0.32.1, and v0.32.0 all released within a few days of each other in September 2026, and the GitHub Action is pinned by tag, so the version you call is the version you get until you bump it. That cadence is a maintenance consideration: pin the Action tag and the npm version, and read the release notes before moving, because a token-shape change would ripple into any baseline comparison. The licence is MIT, which permits commercial use and modification; that is a statement about the licence text, not legal advice, and you should read the LICENSE file yourself if the distinction matters to your organisation. Two operational costs are worth budgeting. The browser install is per machine and per container image, so a CI image that runs the gate needs the Playwright cache populated or the job fails with browser engine not available. And the baseline file is a committed artefact that will change whenever the design changes, which means someone owns reviewing those diffs. The optional cloud app adds a second cost surface: pass --key and every run uploads a snapshot scored against the previous one for that domain, with drift tracking, baselines, visual diffs, and a snapshot timeline. Local use needs no login and keeps data in the browser, so the cloud tier is opt-in rather than required for the CLI or the CI gate.

Editorial conclusion

Adopt Dembrandt if you need a token baseline for a DOM-rendered site and want drift to fail a build rather than be noticed in review; the CI gate is an exit code plus per-token changes[], which fits any runner. Do not adopt it for canvas or WebGL surfaces, where the README states there is no DOM to read, and do not expect dark mode without passing --dark-mode. Before committing a baseline, verify three things: that dembrandt install-browser has populated the Playwright cache on every machine and container that will run the gate, that the pages you crawl are the ones whose tokens you actually care about, and that the baseline file is regenerated deliberately rather than whenever a job fails.

Official sources

  1. dembrandt/dembrandt on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Community notes

Community notes