npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@agent-scope/tokens

v1.20.0

Published

Design token file parser, validator, and resolution engine for Scope

Readme

@agent-scope/tokens

Design token file parser, validator, and resolution engine for Scope.

Parses reactscope.tokens.json / .yaml files into a flat, fully-resolved Token[], and provides lookup, nearest-match search, compliance auditing, impact analysis, theme overlays, and multi-format export.


Installation

npm install @agent-scope/tokens

What it does / when to use it

| Need | Use | |------|-----| | Parse a token file and resolve all {path.to.token} references | parseTokenFile / parseTokenFileSync | | Look up a token by path | TokenResolver.resolve | | Find the token that matches a CSS value | TokenResolver.match / TokenResolver.nearest | | Audit component styles against the token set | ComplianceEngine | | Predict which components break when a token changes | ImpactAnalyzer | | Export tokens to CSS / TypeScript / SCSS / Tailwind / Figma | exportTokens | | Resolve values across dark/brand themes | ThemeResolver | | Validate a raw token file object | validateTokenFile |


Token file format

Token files are JSON or YAML. The top-level shape is:

{
  "$schema": "https://reactscope.dev/token-schema.json",  // optional
  "version": "0.1",                                        // required
  "meta": {                                                // optional
    "name": "My Design Tokens",
    "lastUpdated": "2024-01-01",
    "updatedBy": "[email protected]"
  },
  "tokens": { ... },                                       // required
  "themes": { ... }                                        // optional
}

Token tree

The tokens object is a nested tree. Every leaf node must have value and type:

{
  "version": "0.1",
  "tokens": {
    "color": {
      "primary": {
        "500": { "value": "#3B82F6", "type": "color" },
        "600": { "value": "#2563EB", "type": "color" }
      },
      "neutral": {
        "0":   { "value": "#FFFFFF", "type": "color" },
        "900": { "value": "#111827", "type": "color" }
      },
      "alias": {
        "brand": { "value": "{color.primary.500}", "type": "color" }
      }
    },
    "spacing": {
      "4": { "value": "16px", "type": "dimension" },
      "8": { "value": "32px", "type": "dimension" }
    },
    "typography": {
      "fontFamily": {
        "sans": { "value": "Inter, sans-serif", "type": "fontFamily", "description": "Primary font" }
      },
      "fontWeight": {
        "bold":   { "value": 700, "type": "fontWeight" },
        "normal": { "value": 400, "type": "fontWeight" }
      }
    },
    "radius": { "md": { "value": "6px", "type": "dimension" } },
    "shadow": { "md": { "value": "0 4px 6px rgba(0,0,0,0.1)", "type": "shadow" } },
    "motion": {
      "duration": { "fast": { "value": "150ms", "type": "duration" } },
      "easing":   { "standard": { "value": "cubic-bezier(0.4, 0, 0.2, 1)", "type": "cubicBezier" } }
    }
  }
}

Leaf node fields

| Field | Type | Required | Description | |-------|------|----------|-------------| | value | string \| number | Yes | Raw value. May be a {path.to.token} reference. | | type | TokenType | Yes | One of the 8 supported types (see below). | | description | string | No | Human-readable annotation. Passed through to exports. |

Token types

| Type | Example value | |------|---------------| | color | "#3B82F6" | | dimension | "16px", "1.5rem" | | fontFamily | "Inter, sans-serif" | | fontWeight | 700 | | number | 1.5 | | shadow | "0 4px 6px rgba(0,0,0,0.1)" | | duration | "150ms" | | cubicBezier | "cubic-bezier(0.4, 0, 0.2, 1)" |


Reference syntax — {path.to.token}

A token's value can point to another token using curly-brace dot-notation:

"color": {
  "primary": { "500": { "value": "#3B82F6", "type": "color" } },
  "alias": {
    "brand": { "value": "{color.primary.500}", "type": "color" }
  }
}
  • The parser resolves {color.primary.500}"#3B82F6" and stores it in resolvedValue.
  • References can chain: A → B → C (all fully resolved).
  • Circular references (e.g. A → B → A) throw TokenParseError with code "CIRCULAR_REFERENCE".
  • References to non-existent paths throw TokenParseError with code "INVALID_REFERENCE".
  • resolvedValue is always a string, even for numeric tokens (fontWeight: 700"700").

API reference

parseTokenFile(input, format?) — async

async function parseTokenFile(
  input: string,
  format?: "json" | "yaml",
): Promise<ParsedTokens>

Parses a token file (JSON or YAML) and returns a flat resolved token array.

  • If format is omitted, JSON is tried first, then YAML as a fallback.
  • Throws TokenParseError on reference or circular reference errors.
  • Throws TokenValidationError on schema violations.
import { parseTokenFile } from "@agent-scope/tokens";
import { readFileSync } from "node:fs";

const source = readFileSync("tokens.json", "utf8");
const { tokens, rawFile } = await parseTokenFile(source);
// tokens: Token[]    — flat, resolved
// rawFile: TokenFile — the validated raw file object

parseTokenFileSync(input) — sync, JSON only

function parseTokenFileSync(input: string): ParsedTokens

Synchronous version. Only supports JSON (YAML requires an async import).

import { parseTokenFileSync } from "@agent-scope/tokens";

const { tokens } = parseTokenFileSync(source);
// tokens[0] → { path: "color.primary.500", value: "#3B82F6", resolvedValue: "#3B82F6", type: "color" }

ParsedTokens

interface ParsedTokens {
  tokens: Token[];
  rawFile: TokenFile;
}

Token

interface Token {
  path: string;          // dot-notation, e.g. "color.primary.500"
  value: string | number; // raw value from the file (may be a reference)
  resolvedValue: string;  // fully resolved, always a string
  type: TokenType;
  description?: string;
}

TokenResolver

Wraps a Token[] for fast path-based lookup and nearest-match search.

import { TokenResolver } from "@agent-scope/tokens";

const resolver = new TokenResolver(tokens);

resolve(path)

resolve(path: string): string

Returns the resolved value for a known token path. Throws TokenParseError ("INVALID_REFERENCE") if not found.

resolver.resolve("color.primary.500");          // "#3B82F6"
resolver.resolve("spacing.4");                  // "16px"
resolver.resolve("typography.fontWeight.bold"); // "700"
resolver.resolve("color.alias.brand");          // "#3B82F6" (alias resolved)

match(value, type)

match(value: string, type: TokenType): TokenMatch | null

Returns an exact-match TokenMatch if any token of the given type has a resolvedValue equal to value (case-insensitive for colors). Returns null if no exact match exists.

resolver.match("#3B82F6", "color");
// { token: { path: "color.primary.500", ... }, exact: true, distance: 0 }

resolver.match("#3b82f6", "color"); // case-insensitive — same result
resolver.match("16px", "dimension");
// { token: { path: "spacing.4", ... }, exact: true, distance: 0 }

resolver.match("#AABBCC", "color"); // null — no match

nearest(value, type)

nearest(value: string, type: TokenType): TokenMatch

Returns the TokenMatch with the smallest computed distance to value among all tokens of the given type. Always returns a result (never null); throws if no tokens of the specified type exist.

Distance computation per type:

  • color — Euclidean distance in CIE Lab space (perceptual)
  • dimension / duration|parsed numeric difference|
  • fontWeight / number|numeric difference|
  • shadow / fontFamily / cubicBezier — string equality (0 or 1)
// #3A82F5 is perceptually very close to #3B82F6
resolver.nearest("#3A82F5", "color");
// { token: { path: "color.primary.500", ... }, exact: false, distance: 0.42 }

// 15px — closest to 16px (spacing.4)
resolver.nearest("15px", "dimension");
// { token: { path: "spacing.4", resolvedValue: "16px", ... }, exact: false, distance: 1 }

list(type?, category?)

list(type?: TokenType, category?: string): Token[]

Returns all tokens, optionally filtered by type and/or category (the first path segment, e.g. "color" in "color.primary.500").

resolver.list();                        // all tokens
resolver.list("color");                // only color tokens
resolver.list(undefined, "spacing");   // all tokens in the "spacing" category
resolver.list("dimension", "spacing"); // dimension tokens in "spacing"

TokenMatch

interface TokenMatch {
  token: Token;
  exact: boolean;    // true when resolvedValue === queried value
  distance: number;  // 0 for exact; computed distance otherwise
}

ComplianceEngine

Audits rendered component CSS styles against the resolved token set. Reports per-property compliance status and an aggregate compliance percentage.

import { ComplianceEngine } from "@agent-scope/tokens";

const engine = new ComplianceEngine(resolver);
// With custom tolerances:
const engine = new ComplianceEngine(resolver, {
  colorTolerance: 3,       // default: max CIE Lab distance for on-system
  dimensionTolerance: 2,   // default: max px difference for on-system
  fontWeightTolerance: 0,  // default: exact match only
});

audit(styles)

audit(styles: ComputedStyles): ComplianceReport

Audits a single component's computed styles.

const report = engine.audit({
  colors:     { background: "#3B82F6", color: "#ffffff" },
  spacing:    { paddingTop: "16px", gap: "8px" },
  typography: { fontFamily: "Inter, sans-serif", fontSize: "14px", fontWeight: "700" },
  borders:    { borderRadius: "4px" },
  shadows:    { boxShadow: "0 1px 3px rgba(0,0,0,0.1)" },
});

report.compliance; // 0.83 (fraction of on-system properties)
report.total;      // 8 (properties audited, excluding skipped values)
report.onSystem;   // 7
report.offSystem;  // 1

ComputedStyles

type ComputedStyles = {
  colors:     Record<string, string>; // e.g. { background: "#3B82F6" }
  spacing:    Record<string, string>; // e.g. { paddingTop: "16px" }
  typography: Record<string, string>; // fontFamily, fontSize, fontWeight, lineHeight
  borders:    Record<string, string>; // borderRadius, borderWidth
  shadows:    Record<string, string>; // boxShadow
};

Skipped values (not counted toward totals): "none", "inherit", "initial", "unset", "auto", "transparent", "currentColor", "", "normal".

ComplianceReport

interface ComplianceReport {
  properties: Record<string, PropertyResult>; // per-property result
  total:      number;  // properties audited
  onSystem:   number;
  offSystem:  number;
  compliance: number;  // onSystem / total (1 when total === 0)
  auditedAt:  string;  // ISO timestamp
}

PropertyResult — on_system example

// report.properties["background"] when background: "#3B82F6" (exact token match)
{
  property: "background",
  value: "#3B82F6",
  status: "on_system",
  token: "color.primary.500",
  nearest: { token: "color.primary.500", value: "#3B82F6", distance: 0 }
}

PropertyResult — OFF_SYSTEM example

// report.properties["background"] when background: "#FF0000" (no match)
{
  property: "background",
  value: "#FF0000",
  status: "OFF_SYSTEM",
  // token is absent (undefined) for OFF_SYSTEM results
  nearest: { token: "color.neutral.900", value: "#111827", distance: 74.3 }
}

auditBatch(components)

auditBatch(components: Map<string, ComputedStyles>): BatchReport

Audits multiple components at once:

const batch = engine.auditBatch(new Map([
  ["Button", { colors: { background: "#3B82F6" }, spacing: {}, typography: {}, borders: {}, shadows: {} }],
  ["Input",  { colors: { background: "#FF0000" }, spacing: {}, typography: {}, borders: {}, shadows: {} }],
]));

batch.aggregateCompliance; // 0.5
batch.components.Button.compliance; // 1
batch.components.Input.compliance;  // 0

ComplianceEngine.toJSON(report)

static toJSON(report: ComplianceReport | BatchReport): string

Serializes a report to indented JSON (2-space).


ImpactAnalyzer

Analyses the downstream effects of a design token change on audited components.

import { ImpactAnalyzer } from "@agent-scope/tokens";

const analyzer = new ImpactAnalyzer(resolver, componentReports);
// componentReports: Map<string, ComplianceReport> — from ComplianceEngine.auditBatch

impactOf(tokenPath, newValue)

impactOf(tokenPath: string, newValue: string): ImpactReport

Returns an ImpactReport describing which components and properties would be affected by changing the specified token to newValue.

const report = analyzer.impactOf("color.primary.500", "#1D4ED8");

report.tokenPath;              // "color.primary.500"
report.oldValue;               // "#3B82F6"
report.newValue;               // "#1D4ED8"
report.tokenType;              // "color"
report.colorDelta;             // CIE Lab distance (only for color tokens)
report.affectedComponentCount; // e.g. 2
report.overallSeverity;        // "subtle" | "moderate" | "significant" | "none"
report.components;             // AffectedComponent[]

ImpactReport

interface ImpactReport {
  tokenPath: string;
  oldValue: string;
  newValue: string;
  tokenType: TokenType;
  affectedComponentCount: number;
  components: AffectedComponent[];
  overallSeverity: VisualSeverity; // max severity across all components
  colorDelta?: number;             // CIE Lab distance (color tokens only)
}

interface AffectedComponent {
  name: string;
  affectedProperties: string[];    // e.g. ["background", "borderColor"]
  severity: VisualSeverity;
}

type VisualSeverity = "none" | "subtle" | "moderate" | "significant";

Severity thresholds for color tokens (CIE Lab distance):

  • "none" — distance 0 (no change)
  • "subtle" — distance < 5
  • "moderate" — distance < 20
  • "significant" — distance ≥ 20

For dimension tokens: ≤2px → subtle, ≤8px → moderate, >8px → significant.


exportTokens(tokens, format, options?)

function exportTokens(
  tokens: Token[],
  format: ExportFormat,
  options?: ExportOptions,
): string

Exports a resolved token set to the specified format.

type ExportFormat = "css" | "ts" | "scss" | "tailwind" | "flat-json" | "figma";

interface ExportOptions {
  themes?: Map<string, Map<string, string>>; // theme name → (tokenPath → overrideValue)
  prefix?: string;       // CSS/SCSS: prefix for custom property / variable names
  rootSelector?: string; // CSS: override ":root" selector
}

CSS export

exportTokens(tokens, "css");
// :root {
//   --color-primary-500: #3B82F6;
//   --color-primary-600: #2563EB;
//   --spacing-4: 16px;
//   ...
// }

exportTokens(tokens, "css", { prefix: "scope" });
// :root { --scope-color-primary-500: #3B82F6; ... }

exportTokens(tokens, "css", { rootSelector: "html" });
// html { --color-primary-500: #3B82F6; ... }

// With themes:
exportTokens(tokens, "css", { themes: themeMap });
// :root { --color-primary-500: #3B82F6; ... }
// [data-theme="dark"] { --color-primary-500: #60A5FA; ... }
// [data-theme="brand-b"] { --color-primary-500: #8B5CF6; ... }

TypeScript export

exportTokens(tokens, "ts");
// // Auto-generated design tokens — do not edit manually
//
// export const colorPrimary500 = "#3B82F6" as const;
// export const colorPrimary600 = "#2563EB" as const;
// export const spacing4 = "16px" as const;
// ...

// With themes:
// export const themes = {
//   "dark": { colorPrimary500: "#60A5FA" as const, ... },
//   "brand-b": { colorPrimary500: "#8B5CF6" as const, ... },
// } as const;

SCSS export

exportTokens(tokens, "scss");
// // Auto-generated design tokens — do not edit manually
//
// $color-primary-500: #3B82F6;
// $spacing-4: 16px;
// ...

exportTokens(tokens, "scss", { prefix: "tok" });
// $tok-color-primary-500: #3B82F6;

// With themes — emits [data-theme] blocks using CSS custom properties:
// [data-theme="dark"] { --color-primary-500: #60A5FA; }

Tailwind export

exportTokens(tokens, "tailwind");
// // Auto-generated design tokens — do not edit manually
// module.exports = {
//   "theme": {
//     "extend": {
//       "color": { "primary": { "500": "#3B82F6", "600": "#2563EB" } },
//       "spacing": { "4": "16px", "8": "32px" }
//     }
//   }
// };

flat-json export

exportTokens(tokens, "flat-json");
// {
//   "color.primary.500": "#3B82F6",
//   "color.primary.600": "#2563EB",
//   "spacing.4": "16px"
// }

Figma export

exportTokens(tokens, "figma");
// {
//   "global": {
//     "color": {
//       "primary": {
//         "500": { "value": "#3B82F6", "type": "color" }
//       }
//     },
//     "typography": {
//       "fontFamily": {
//         "sans": { "value": "Inter, sans-serif", "type": "fontFamily", "description": "Primary font" }
//       }
//     }
//   },
//   "dark": { "color": { "primary": { "500": { "value": "#60A5FA", "type": "color" } } } }
// }

ThemeResolver

Extends TokenResolver with named theme overlays.

Token file format — themes

Two supported theme formats:

Flat override map (original format):

{
  "version": "0.1",
  "tokens": { ... },
  "themes": {
    "dark": {
      "color.primary.500": "#60A5FA",
      "color.neutral.0": "#0F172A"
    },
    "brand-b": {
      "color.primary.500": "#8B5CF6"
    }
  }
}

Nested DTCG-style (structured format):

{
  "version": "0.1",
  "tokens": { ... },
  "themes": {
    "dark": {
      "color": {
        "primary": { "500": { "$value": "#60A5FA" } },
        "neutral": { "0":   { "$value": "#0F172A" } }
      }
    }
  }
}

ThemeResolver.fromTokenFile(baseResolver, rawFile)

static fromTokenFile(baseResolver: TokenResolver, rawFile: ThemedTokenFile): ThemeResolver

Constructs a ThemeResolver from a TokenResolver and a raw token file (supports both flat and nested formats).

import { ThemeResolver } from "@agent-scope/tokens";

const { tokens, rawFile } = parseTokenFileSync(source);
const resolver = new TokenResolver(tokens);
const themeResolver = ThemeResolver.fromTokenFile(resolver, rawFile);

themeResolver.listThemes();                          // ["dark", "brand-b"]
themeResolver.resolveThemed("color.primary.500", "dark");   // "#60A5FA"
themeResolver.resolveThemed("spacing.4", "dark");           // "16px" (falls back to base)
themeResolver.resolveAllThemes("color.primary.500");
// { base: "#3B82F6", dark: "#60A5FA", "brand-b": "#8B5CF6" }

ThemeResolver.fromThemeMap(baseResolver, themes)

static fromThemeMap(
  baseResolver: TokenResolver,
  themes: Map<string, Map<string, string>>,
): ThemeResolver

Programmatic construction from a pre-built theme map.

resolveThemed(path, themeName)

Returns the value for the path in the given theme, falling back to base if the theme doesn't override it. Throws if the theme name is not registered.

resolveAllThemes(path)

Returns { base: string, [themeName]: string, ... } — the resolved value in every theme.

buildThemedTokens(themeName)

Returns a full Token[] with the theme overrides applied (base values for non-overridden tokens).

Delegated methods

ThemeResolver also exposes resolve(path) and list(type?, category?) which delegate to the underlying TokenResolver.


validateTokenFile(raw)

function validateTokenFile(raw: unknown): asserts raw is TokenFile

Validates a raw parsed object against the TokenFile schema. Throws TokenValidationError with all collected issues if validation fails (collects all errors before throwing, not just the first).

Validation rules:

  • Root value must be a non-null object
  • version must be a string field
  • tokens must be an object field
  • Every leaf node inside tokens must have value (string or number) and type (one of the 8 valid types)
  • meta, if present, must be an object
  • themes, if present, must be a Record<string, Record<string, string>>
import { validateTokenFile, TokenValidationError } from "@agent-scope/tokens";

try {
  validateTokenFile(raw);
  // raw is now asserted as TokenFile
} catch (err) {
  if (err instanceof TokenValidationError) {
    for (const error of err.errors) {
      console.error(`${error.path}: ${error.message} [${error.code}]`);
    }
  }
}

Error types

class TokenParseError extends Error {
  readonly code: "CIRCULAR_REFERENCE" | "INVALID_REFERENCE" | "INVALID_SCHEMA" | "PARSE_ERROR";
  readonly path?: string;
}

class TokenValidationError extends Error {
  readonly errors: ValidationError[];
}

interface ValidationError {
  path: string;
  message: string;
  code: string;
}

Complete example

import {
  parseTokenFileSync,
  TokenResolver,
  ComplianceEngine,
  ImpactAnalyzer,
  exportTokens,
  ThemeResolver,
} from "@agent-scope/tokens";
import { readFileSync } from "node:fs";

// 1. Parse token file
const source = readFileSync("reactscope.tokens.json", "utf8");
const { tokens, rawFile } = parseTokenFileSync(source);

// 2. Build resolver
const resolver = new TokenResolver(tokens);
resolver.resolve("color.primary.500"); // "#3B82F6"
resolver.match("#3B82F6", "color");    // exact match → TokenMatch
resolver.nearest("#3A80F0", "color"); // perceptually closest color

// 3. Export tokens
const css  = exportTokens(tokens, "css");
const ts   = exportTokens(tokens, "ts");
const scss = exportTokens(tokens, "scss");

// 4. Compliance audit
const engine = new ComplianceEngine(resolver);
const report = engine.audit({
  colors:     { background: "#3B82F6" },
  spacing:    { paddingTop: "16px" },
  typography: { fontFamily: "Inter, sans-serif" },
  borders:    { borderRadius: "6px" },
  shadows:    { boxShadow: "0 4px 6px rgba(0,0,0,0.1)" },
});
console.log(report.compliance); // e.g. 1

// 5. Batch audit + impact analysis
const batchReport = engine.auditBatch(new Map([
  ["Button", { colors: { background: "#3B82F6", color: "#FFFFFF" }, spacing: {}, typography: {}, borders: {}, shadows: {} }],
  ["Card",   { colors: { background: "#FFFFFF" }, spacing: { gap: "32px" }, typography: {}, borders: { borderRadius: "6px" }, shadows: {} }],
]));

const analyzer = new ImpactAnalyzer(resolver, new Map(Object.entries(batchReport.components)));
const impact = analyzer.impactOf("color.primary.500", "#1D4ED8");
console.log(impact.affectedComponentCount); // 1
console.log(impact.overallSeverity);        // "moderate"

// 6. Theme resolution
const themeResolver = ThemeResolver.fromTokenFile(resolver, rawFile);
themeResolver.resolveThemed("color.primary.500", "dark"); // "#60A5FA"
themeResolver.resolveAllThemes("color.primary.500");      // { base, dark, "brand-b" }

Internal architecture

| Module | Responsibility | |--------|----------------| | types.ts | All TypeScript types and error classes | | validator.ts | Schema validation — collects all errors before throwing | | parser.ts | JSON/YAML parsing → flattenTokensresolveValue (DFS with cycle detection) | | resolver.ts | TokenResolver — path lookup, match, nearest, list | | compliance.ts | ComplianceEngine — style auditing against the token set | | impact.ts | ImpactAnalyzer — downstream change analysis | | export.ts | exportTokens — CSS, TS, SCSS, Tailwind, flat-JSON, Figma | | themes.ts | ThemeResolver — flat and DTCG-style theme overlay resolution | | color-utils.ts | hexToLab, labDistance, parseColorToLab — perceptual color math |


Used by

  • @agent-scope/cli — token compliance commands (scope tokens compliance, scope tokens export, scope tokens impact, scope tokens preview)
  • @agent-scope/site — type imports for the Scope web UI