Skip to main content
Automation

One drift report, three destinations

DeveloperDesignerEnterprise

Overview

A Dembrandt run produces a JSON, an HTML report and an exit code. None of them care where they end up. Commit the report into a wiki as the record, post the changed tokens as a PR comment as the nudge, and pin a Figma comment only when drift grows. Any CI that can write a file can do it; the example below is Azure Pipelines, the GitHub Actions version is in the CI drift gate recipe.

Two sibling web apps share one Figma library: one on Tailwind v3 with years of hand-picked palette classes, the other on Tailwind v4 with its own token block. The question the design system owner kept asking was how far either codebase had drifted from the design system, and whether the two apps still looked like siblings. The answer is to stop thinking of the report as something you open and start treating it as a file the pipeline routes. Dembrandt writes JSON with --json-only, a self-contained HTML report with --html and exits 1 when --compare finds drift, so every destination a pipeline can write to is a destination for the report. Three drift views feed three destinations. A source scan per app counts colour uses that bypass the tokens. A cross-app comparison resolves every colour in both apps to hex and matches it to the nearest token of the same role. Dembrandt supplies the third view: what the browser actually paints per route, compared against a snapshot of an approved release, which catches colours inherited from component libraries, chart palettes that inline their own colours, runtime-computed styles and dark mode that was never wired. Where the reports go depends on who reads them. The wiki is the record, updated on every push to main. The PR comment is the nudge, naming the exact off-token classes a change added. The Figma comment fires only when drift grows, so designers see regressions and never noise. The first run found all 17 shared roles differing between the two apps, including the same wrong shadcn accent colour in both.

Figma page showing a cross-app token comparison card: 14 roles, the colour each app uses for each role with its OKLab delta from the token, and a pinned comment reading Drift grew +12
The same comparison, pasted into a Figma page where designers already look. The pinned comment is the only automated part: it fires when drift grows and stays silent otherwise.

How it works

Loading diagram...

Snapshot an approved release of each app as its baseline

Terminal
dembrandt https://app-b.test.example.com --wcag --json-only > app-b-baseline.json

On every test deploy: compare, exit 1 on drift, write the wiki report

Terminal
dembrandt https://app-b.test.example.com --compare app-b-baseline.json --html docs/drift/app-b-rendered.html --no-sandbox

Accept an intentional design-system release as the new baseline

Terminal
dembrandt https://app-b.test.example.com --compare app-b-baseline.json --approve

Rendered drift runs as a pipeline job after the test deploy. Apps behind Entra ID keep their session in sessionStorage, where cookies and headers do not reach, so point Dembrandt at a test-only unauthenticated design route or a storage-state login.

Example, Azure Pipelines: rendered drift after deploy
- job: rendered_drift
dependsOn: deploy_test
steps:
- script: npm i dembrandt && npx playwright-core install --with-deps chromium
- script: |
npx dembrandt "$(TEST_URL)/design" \
--compare app-b-baseline.json \
--html drift.html --no-sandbox
continueOnError: true # exit 1 on drift; the wiki commit below still runs
- script: |
# Commit the report into the design system wiki repo as the record
cp drift.html ../ds-wiki/drift/app-b-rendered.html
cd ../ds-wiki && git add . && git commit -m "App B rendered drift" && git push
One report, routed by reader
# Destination Updates Reader Role
# Wiki page per app + comparison every push to main developers, PM the record: commit drift.html next to the source scan
# PR thread every PR the PR author the nudge: post the changed tokens from --compare JSON
# Figma comment on a Token drift page only when drift grows DS owner "App B: 64 → 76 (+12) in abc123"; silent when it falls
# Figma page cards on request, per release designers the canvas is not scriptable, so paste the summary by hand
# Issue (Azure Boards, GitHub, Linear) on regression the owning team one issue per drifted token, closed when --compare is clean again
# Slack, Teams, email on regression whoever is on call same JSON, different webhook
Output

A wiki page per app with drift history and worst files, a PR comment naming the off-token values a change added, a Figma comment only on regressions, and a rendered drift report per route with exit 1 when the browser output leaves the approved baseline.

Browse all

All recipes →

64 workflows