Open Design Tokens
    Preparing search index...

    Open Design Tokens

    Open Design Tokens

    TypeScript toolkit for validating and linting DTCG design tokens.

    npm CI spec 2025.10 types included validator size license: MIT OpenSSF Scorecard OpenSSF Best Practices

    Supports the Design Tokens Format Module 2025.10 and the Color Module 2025.10. Zero runtime dependencies. Runs in Node 20.19+, evergreen browsers, and any modern bundler.

    Status: beta. Feature-complete for the 2025.10 specifications and tested against the spec text; the API will stay on 0.x until it has seen real-world use. Try it in the playground, and read the documentation.

    npm install open-design-tokens
    
    import { validateDtcg } from 'open-design-tokens/validator';
    import { readFileSync } from 'node:fs';

    const result = validateDtcg(readFileSync('tokens.json', 'utf8'));

    if (!result.valid) {
    for (const issue of result.errors) {
    console.log(`${issue.path.dot}: ${issue.message} [${issue.code}]`);
    }
    }

    With types:

    import { validateDtcg, type ValidationIssue } from 'open-design-tokens/validator';
    import type { DesignTokens } from 'open-design-tokens/types';

    const tokens: DesignTokens = {
    color: {
    $type: 'color',
    primary: { $value: { colorSpace: 'srgb', components: [0.2, 0.4, 1] } },
    },
    };

    const { valid, issues }: { valid: boolean; issues: ValidationIssue[] } = validateDtcg(tokens);

    Sample output for a file with a mis-cased color space and a missing unit:

    color.brand.primary.$value.colorSpace: Unsupported color space "sRGB". Supported: "srgb", "srgb-linear", "hsl", … Did you mean "srgb"? Values are case-sensitive. [color.color-space.invalid]
    spacing.small.$value: A dimension value must have a "unit" property, even when "value" is 0. [dimension.unit.missing]

    Every issue carries a stable code, a plain-text message, a severity, and a path as both a JSON pointer and a dotted path. Invalid input of any shape produces diagnostics; validateDtcg never throws on a document.

    The validator checks the specification. The linter checks your project's policies. Neither contains vendor-specific rules.

    Validator Linter
    Question "Is this valid DTCG?" "Does this follow our policies?"
    Rules Defined by the supported specification version Configured by you
    Examples Invalid value type, missing alias target, circular reference Name length, naming style, required descriptions
    Can the rules be changed? Not while claiming the same DTCG conformance Yes: enable, disable, configure, or change severity

    A token named x is valid DTCG. A project that requires names of 2 to 20 characters configures token-name-length as an error, and its CI rejects the file. The rejection means "not accepted by this project", not "invalid DTCG".

    lintDtcg() always validates first and skips policy rules on nodes with spec errors, so one spec mistake never cascades into misleading lint findings.

    import { lintDtcg, defineConfig } from 'open-design-tokens/linter';

    const config = defineConfig({
    extends: ['recommended'],
    rules: {
    'token-name-length': ['error', { min: 2, max: 40 }],
    'naming-convention': ['error', { tokens: 'kebab', groups: 'camel' }],
    'no-unused-tokens': ['warning', { ignore: ['component.**'] }],
    },
    });

    const result = lintDtcg(tokens, config);
    result.valid; // spec-valid and no lint errors
    result.validation; // the full validation result
    result.issues; // lint findings, each with a ruleId
    result.skipped; // rule checks skipped on invalid nodes
    npx dtcg-lint 'src/**/*.tokens.json'
    npx dtcg-lint tokens.json --config lint.config.json --format github

    dtcg-lint validates, then lints. It loads dtcg-lint.config.{json,mjs,js,ts} from the current directory only, or the file given with --config. Formats: pretty (default), json, github (workflow annotations). Exit codes: 0 clean, 1 errors, 2 usage or configuration problem.

    Each rule takes "error", "warning", "off", or [severity, options]. The recommended preset enables the rules marked ✔, all as warnings.

    Rule Options ✔ Checks
    token-name-length { min, max } Token names within a length range
    group-name-length { min, max } Group names within a length range
    description-length { min, max } $description within a length range
    naming-convention { tokens, groups }: kebab, camel, pascal, snake, or a pattern Names follow a style
    require-description Every token has a $description
    require-explicit-type Every token declares its own $type
    max-alias-depth { max } ✔ Alias chains resolve within max hops
    no-unused-tokens { ignore: [patterns] } ✔ Every token is referenced, except ignored layers
    no-deprecated-alias ✔ No alias points at a deprecated token
    no-empty-groups ✔ Groups contain at least one member
    extensions-allowed-vendors { vendors: [keys] } Only listed vendor keys in $extensions
    extensions-schema { vendors: { key: schema or function } } Vendor blocks match a declared shape

    Custom rules use defineRule and are passed through lintDtcg(tokens, config, { rules: [myRule] }).

    Entry points

    Import Contents
    open-design-tokens/validator validateDtcg, composable validate* functions, prepareDocument, resolver, spec tables, messages
    open-design-tokens/linter lintDtcg, defineConfig, defineRule, rules, presets, formatters
    open-design-tokens/types All TypeScript types, no runtime code
    open-design-tokens Everything above

    The package is ESM. Node 20.19+ and 22.12+ can require() it as well. Guides and concepts are at opendesigntokens.com/docs; the generated API reference is at /api/.

    Every normative section of both specifications is covered by fixtures derived from the spec text; see docs/spec-coverage.md.

    The 2025.10 specifications contain a few examples that contradict their own normative rules, for example a $type of "string" that no section defines, a % dimension unit, and typography values that omit a required sub-value. This toolkit follows the rules, not the examples: those examples are kept as expected-failure test cases and listed, with the codes emitted, under Known spec issues. Decisions on genuinely ambiguous points are recorded in docs/spec-decisions.md.

    Not supported yet: the Resolver module. See ROADMAP.md.

    Token files are treated as untrusted input: no code evaluation, bounded nesting, cycle-safe resolution, document-local references only, escaped diagnostics, and zero runtime dependencies. Details in docs/security.md; report vulnerabilities as described in SECURITY.md.

    See CONTRIBUTING.md. Versioning and the public API policy are in docs/versioning.md.

    MIT