English | Português (Brasil)
TokenScout helps small web agencies turn an undocumented live client website into an evidence-backed redesign baseline, so they can scope and start rebuilding without guessing what the browser actually renders.
When source design files are missing, stale, or incomplete, manual discovery creates unsupported assumptions, missed requirements, and avoidable rework. TokenScout studies the rendered site and writes a reviewable website rebuild evidence pack: W3C DTCG tokens, assets, motion, icons, topology, interactions, screenshots, Design DNA, and CSS/Tailwind output.
It records what the browser presents. It does not claim to recover the original design intent, replace design judgment, or generate a complete design system.
Three packages:
tokenscout(core), zero runtime dependencies. You give it style observations from a page (colors, font sizes, spacing) and it returns a deduplicated, structured token document. Color clustering, type and spacing scale detection, WCAG contrast-pair audit, and DTCG export are all implemented and tested.@tokenscout/extract, which drives a headless browser (Playwright) to collect those observations from a live URL for you: computed styles at one or more breakpoints, optional same-origin crawling, assets, CSS motion, stack, icons, topology, and the primary interaction model.studySitewrites the evidence and a versioned Design DNA brief to disk.@tokenscout/transform, which renders a DTCG token document as CSS custom properties or a Tailwind configuration without guessing semantic roles.
You can supply observations yourself, extract a report from a live page, or write a complete study bundle for a redesign. Design and status in ARCHITECTURE.md and ROADMAP.md; operating principles and validation gates in docs/METHODOLOGY.md.
- Scope from evidence. Start a client rebuild from a reviewable baseline, not a subjective walkthrough of an undocumented site.
- Computed, not source. What ships to a user's screen is not what is in the stylesheet. TokenScout reduces the resolved, painted values.
- Perceptual clustering.
#3a7bd5,#3b7cd6, andrgb(58,123,213)are three strings but one color. tokenscout clusters them in CIELAB by ΔE2000, so a sprawling declared palette collapses to the handful of colors a site really uses (the example below: 9 declared to 4 real). - Stable, name-hinted token ids. Color tokens are keyed by a content hash
of the canonical value plus the nearest CSS color name (e.g.
cornflowerblue-17rhtps), so ids stay put across runs and a token diff reflects real palette change, not list churn. - Zero runtime dependencies. The color math is ~120 lines of pure TypeScript (sRGB→Lab, ΔE2000, count-ordered nearest-leader clustering — cluster spread stays bounded by the threshold). No native deps.
npm install tokenscoutFor live extraction and a reusable study bundle, install the extract package and a browser:
npm install @tokenscout/extract playwright
npx playwright install chromiumTo turn DTCG tokens into implementation files:
npm install @tokenscout/transformESM, Node 20+. Playwright is a peer dependency of @tokenscout/extract, so the
core stays dependency-free.
The relaunch versions are prepared in this repository, but distribution is not
complete yet: npm currently has tokenscout@0.3.0; the scoped extract and
transform packages still await their first publication. Until Phase 7 is
complete, use the workspace checkout for the full three-package workflow.
The repository includes a cross-agent tokenscout skill
for URL-to-study, DTCG, CSS-variable, and Tailwind workflows. Install it directly
from GitHub with the skills CLI:
npx skills add Atroci/tokenscoutThen ask your agent, for example:
Use $tokenscout to study https://example.com and produce an evidence-backed redesign baseline.
The skill orchestrates the npm packages; it does not bundle Chromium or package code. External extraction therefore requires the relaunch packages to be live on npm. The public skill source is canonical; do not maintain a separate MCP or command implementation.
import { studySite } from "@tokenscout/extract";
await studySite("https://example.com", {
outDir: "./tokenscout-study",
breakpoints: [1440, 768, 390],
});This writes site-report.json, design-dna.json, design-dna.md, and
light/dark screenshot evidence. Design DNA separates measured observations,
conservative inferences, explicit unknowns, and keep/adapt/improve/do-not-copy
guidance. The contract is versioned and documented in
docs/design-dna-v0.1.md.
import { extractTokens } from "@tokenscout/extract";
// Crawl + read computed styles, then reduce to a DTCG token document.
const tokens = await extractTokens("https://example.com", {
breakpoints: [1280, 375],
top: 1, // same-origin pages to crawl from the entry URL
});extractSite(url, opts) is also exported if you want the raw PageExtract[]
before reduction.
For the full redesign bundle in one call, use inspectSite:
import { inspectSite } from "@tokenscout/extract";
const report = await inspectSite("https://example.com", {
breakpoints: [1280, 375],
top: 5,
sitemap: true, // discover pages from sitemap.xml instead of crawling links
});
report.tokens; // DTCG document (color, fontSize, spacing, and a duration group)
report.assets; // resolved, deduplicated image/asset manifest
report.animations; // CSS durations (ms), easings, @keyframes names
report.stack; // detected frameworks with confidenceLong multi-page inspections can expose structured lifecycle updates without changing their final Promise result or writing to stdout:
const report = await inspectSite("https://example.com", {
breakpoints: [1440, 768, 390],
onProgress(event) {
const position =
event.current === undefined ? "" : ` ${event.current}/${event.total}`;
const viewport =
event.breakpoint === undefined ? "" : ` @ ${event.breakpoint}px`;
process.stderr.write(
`[tokenscout] ${event.phase}.${event.status}${viewport}${position} ${JSON.stringify(event.detail ?? {})}\n`,
);
},
});extractSite, extractTokens, inspectSite, and captureSite accept the same
optional onProgress listener. Events report real phases, statuses, page and
breakpoint counters, elapsed time, and compact result counts. Disabled
collectors emit skipped; failures emit failed before the original error is
re-thrown. Listener errors are ignored so progress presentation cannot abort a
successful scan. Listeners are synchronous; returned promises are not awaited,
although rejected thenables are observed to prevent unhandled rejections.
inspectSite emits rendered-style viewport progress but does not take
screenshots. Only captureSite emits the screenshot phase.
To copy the site's images for a redesign, fetch the manifest to disk:
import { downloadAssets } from "@tokenscout/extract";
await downloadAssets(report.assets, "./out/assets"); // writes files + manifest.jsonimport { transform } from "@tokenscout/transform";
const css = transform(report.tokens, "css-vars");
const tailwind = transform(report.tokens, "tailwind");The transform deliberately stops at raw token families. It does not invent
semantic background, primary, or border aliases that the evidence cannot
support yet.
The individual collectors (discoverAssets, extractAnimations, profilePage,
discoverSitemapUrls) are exported too, if you want to run them on your own
page.
Best-effort, research-tier, and not part of inspectSite's default output:
import { detectPageMotion, captureMotion } from "@tokenscout/extract";
const libs = await detectPageMotion(page); // GSAP / Framer / AOS / Lottie ...
// captureMotion wraps Element.animate before navigation, so call it on a fresh page:
const motion = await captureMotion(freshPage, "https://example.com");
// { count, durations (ms), easings, properties } captured from the Web Animations APIFeed in page observations, get a DTCG token document back:
import { assembleTokens } from "tokenscout/tokens";
import type { PageExtract } from "tokenscout/schema";
const pages: PageExtract[] = [
{
url: "https://example.com/",
breakpoint: 1280,
colors: [
{ value: "#3a7bd5", role: "background-color", count: 40 },
{ value: "#3b7cd6", role: "color", count: 5 },
{ value: "rgb(58, 123, 213)", role: "border-color", count: 2 },
{ value: "#e23744", role: "color", count: 12 },
],
type: { sizes: ["16px", "20px", "25px", "31.25px"] },
spacing: { values: ["8px", "16px", "24px", "32px"] },
},
];
const tokens = assembleTokens(pages); // { color, fontSize, spacing }, DTCG-shapedThe full runnable version is in
packages/core/examples/quickstart.ts
(npx tsx packages/core/examples/quickstart.ts), and its output is checked in at
packages/core/examples/design-tokens.json.
Need just the color math? Import it directly:
import { parseColor, clusterColors } from "tokenscout/color";
const colors = ["#3a7bd5", "#3b7cd6", "rgb(58, 123, 213)", "#e23744"]
.map((value) => {
const p = parseColor(value);
return p ? { value, rgb: p.rgb } : null;
})
.filter((c) => c !== null);
const clusters = clusterColors(colors); // ΔE2000 ≤ 2.0 by default
// 2 clusters: one blue (3 members, canonical "#3a7bd5"), one red.Lower-level building blocks (rgbToLab, deltaE76, deltaE2000) are exported too.
The example above, run through assembleTokens, collapses 9 declared colors
into 4 real ones and emits clean type and spacing scales. The color group
records that collapse as an auditable sprawl metric in group-level
$extensions (9 analyzable → 4 distinct = 2.25×), and each color token has a
stable, name-hinted id, a DTCG structured $value, and $extensions metadata
(what it was authored as, how often it was used, which CSS properties it painted,
and the raw members that clustered into it):
{
"color": {
"$extensions": {
"com.tokenscout.analyzable": 9,
"com.tokenscout.unanalyzable": 0,
"com.tokenscout.distinct": 4,
"com.tokenscout.sprawl-ratio": 2.25
},
"cornflowerblue-17rhtps": {
"$value": { "colorSpace": "srgb", "components": [0.22745, 0.48235, 0.83529], "alpha": 1 },
"$type": "color",
"$extensions": {
"com.tokenscout.css-authored-as": "#3a7bd5",
"com.tokenscout.usage-count": 50,
"com.tokenscout.css-properties": ["background-color", "border-color", "color"],
"com.tokenscout.member-count": 4,
"com.tokenscout.members": ["#3A7BD4", "#3a7bd5", "#3b7cd6", "rgb(58, 123, 213)"]
}
}
// ... white-0z2ixva, black-0ugpfk2, crimson-09p9vbj elided
},
"fontSize": {
"font-size-1": { "$value": { "value": 16, "unit": "px" }, "$type": "dimension" },
"font-size-2": { "$value": { "value": 20, "unit": "px" }, "$type": "dimension" }
}
}Full document: packages/core/examples/design-tokens.json.
- Redesign from truth. Capture an existing site's real palette and scales so a rebuild starts from what users actually see, not a guess.
- Audit a design system's drift. Surface how many near-duplicate colors and off-grid spacing values a live site has accumulated versus its intended scale.
- Seed
design-tokens.json. Get a W3C DTCG starting point for Style Dictionary or any DTCG-aware tooling. - Ground an AI-assisted rebuild. Give an agent measured evidence, explicit unknowns, and transfer boundaries instead of asking it to imitate a screenshot from memory.
- Normalize scraped styles. Turn raw computed-style dumps from your own crawler into a deduplicated, structured token set.
Honest about the edges, since they affect output:
- JS-motion capture is experimental.
@tokenscout/extractreads color/type/spacing, an asset manifest (downloadAssetsfetches it to disk), CSS animation tokens, and a tech-stack profile. As experimental extras it also fingerprints animation libraries (detectPageMotion) and captures Web-Animations-API motion (captureMotion, wrappingElement.animate). These are best-effort and not part ofinspectSite's default output. Still on the roadmap: sampling rAF-driven motion that bypasses WAAPI, downloading Lottie JSON, and motion-reference video. - Motion tokens are durations only (in the DTCG
durationgroup). Easings and@keyframesnames are reported oninspectSite'sanimationsfield but are not yet emitted as DTCG tokens. - Most common CSS Color 4 functions are parsed. Hex,
rgb()/rgba(),hsl()/hsla(), named colors,oklch(),oklab(),lab(),lch(), andhwb()are supported. The parameterizedcolor()form is still dropped. - Lengths are
pxandremonly.em,%,vw, and keywords are dropped. - Fully transparent paints (alpha 0) are dropped from color tokens; alpha is otherwise not part of the cluster identity, so opaque and semi-transparent variants of the same RGB still merge.
- Color clustering is single-linkage, so it chains transitively: across a near-continuous gradient a cluster's perceptual spread can exceed the threshold. Lower the ΔE threshold if that matters for your input.
- No per-breakpoint or per-theme identity. Extracts from every breakpoint are flattened into one token set, so mobile/desktop differences dissolve, and light/dark themes are not captured separately. Both are next-focus work (see Roadmap).
- Contrast pairs are a proxy, not exhaustive.
com.tokenscout.contrast-pairscross-joins the top 3 clusters used asbackground-coloragainst the top 3 used ascolor— the combinations a designer would actually check, not every color pairing on the page. Element-level "this exact button fails contrast" reporting would need per-element geometry, which is out of scope for a computed-style/CSS-only tool (see touch-target geometry below). - No element-level geometry (button/target width, height, position). Page
topology reports section-level layout only. Fitts's-Law-style touch-target
auditing needs per-element boxes at a given breakpoint, which belongs in a
browser-driven layer (
@tokenscout/extractor a consumer), not core.
Three-package shape: a zero-dependency core, a Playwright extraction layer, and a small transform layer. Full detail in ROADMAP.md; design in ARCHITECTURE.md.
Core (tokenscout, zero deps):
- Color: parse, sRGB→Lab, ΔE2000, perceptual clustering
- Tests + CI
- Type scale reducer
- Spacing scale reducer
-
design-tokens.json(W3C DTCG) export - WCAG contrast-pair audit (background/text cross-join, 4.5:1 / 3:1 verdicts)
- Semantic role aliasing (
color.background.canvas,color.action.primary, …, inferred from usage) — prerequisite for ashadcntransform target - Radius / shadow / border / breakpoint token families
Extract (@tokenscout/extract, Playwright peer):
- Live crawl + computed-style extraction at breakpoints
- Image / asset harvesting and downloads
- CSS animation signals, stack, icons, topology, and interaction detection
- Versioned Design DNA study bundle with screenshot evidence
- [~] JavaScript motion capture (opt-in research tier; rAF remains incomplete)
- Responsive / multi-screen capture: configurable breakpoints, light/dark dual palette, and per-breakpoint token identity (currently flattened)
Transform (@tokenscout/transform):
- CSS custom properties
- Tailwind configuration
- Semantic aliases and
shadcnexport after role evidence exists
Next focus is responsive multi-screen capture and refined motion capture — plan in docs/next-steps-responsive-and-motion.md.
Release:
- Core is live on npm; the registry currently trails GitHub
- Public agent skill is versioned in this repository
- Install/index the skill on skills.sh after the scoped npm packages ship
- Publish the unified relaunch set:
tokenscout@0.6.0,@tokenscout/extract@0.6.0, and@tokenscout/transform@0.6.0
Issues and PRs welcome, especially around color science, CSS value parsing, token-scale heuristics, and evidence-based design transfer. The deterministic study and transform layers are MIT-licensed with the rest of the repository.
See CONTRIBUTING.md for the dev loop and the zero-runtime-dependency rule, and the Code of Conduct before participating. Changes are tracked in CHANGELOG.md.
MIT © Hugo Carvalho