DTCG
Overview
The @styleframe/dtcg package is a spec-conformant parser, validator, and serializer for the W3C Design Tokens Community Group (DTCG) format (2025.10). It implements the Format, Color, and Resolver modules and is the interchange layer behind the styleframe dtcg export CLI command.
The package is runtime-agnostic — no filesystem, no DOM, no framework assumptions. You can use it directly to parse, validate, transform, or emit DTCG documents from any Node.js or browser environment.
What is DTCG?
DTCG is a portable JSON format for design tokens, standardised by the W3C Design Tokens Community Group. It defines how colours, dimensions, typography, shadows, and other design primitives should be represented so they can flow between tools — Figma, Tokens Studio, Style Dictionary, and any other DTCG-compatible system.
Why a dedicated package?
- Standards compliance: Implements all eight primitive types (
color,dimension,fontFamily,fontWeight,duration,cubicBezier,number,string) and all six composite types (border,strokeStyle,transition,shadow,gradient,typography). - Full color coverage: Supports all 14 color spaces from the Color Module (
srgb,oklch,display-p3,lab,lch,hsl,hwb, …). - Lossless aliases: Transitive alias resolution (
A → B → C) with circular-reference detection. - Multi-theme via Resolver: Native support for the Resolver Module — sets, modifiers, and explicit resolution order for theme bundles.
- Type-safe classification:
classifyValue()infers a DTCG$typefrom a raw value plus optional path hint, so untyped inputs get the right type stamp. - Framework-agnostic: No filesystem, DOM, or framework dependencies. Bring your own I/O.
What's included
The package is organised by spec concern. Each group below covers a slice of the DTCG specification.
Parse, validate, walk
| Symbol | Purpose |
|---|---|
parse(input) | Parse a .tokens.json document; throws ParseError on invalid shape |
validate(doc) | Validate against the spec; returns ValidationError[] |
validateResolver(doc) | Validate a .resolver.json document |
walk(doc) | Iterate every token and group with its path |
Aliases
| Symbol | Purpose |
|---|---|
parseAlias(string) | Extract the path from "{color.primary}" |
formatAlias(path) | Wrap a path in alias syntax |
resolveAliases(doc) | Flatten transitive aliases; throws on cycles or unknown references |
lookupToken(doc, path) | Find a token by path |
splitPath / joinPath / appendPath | Path-string utilities |
Inheritance
| Symbol | Purpose |
|---|---|
applyInheritance(doc) | Propagate $type and $deprecated from parent groups to children |
Type guards
Runtime guards for narrowing union types: isToken, isGroup, isAlias, isColorValue, isDimensionValue, isDurationValue, isFontFamilyValue, isFontWeightValue, isNumberValue, isCubicBezierValue, isBorderValue, isShadowValue, isGradientValue, isTransitionValue, isTypographyValue, isStrokeStyleValue, and more.
Classification
| Symbol | Purpose |
|---|---|
classifyValue(value, { path? }) | Detect a DTCG type from a raw value plus optional path hint |
parseCubicBezier(string) | Parse "cubic-bezier(0.42, 0, 1, 1)" into a tuple |
easingKeywordToBezier(name) | Convert CSS easing keywords (ease, ease-in-out, …) to cubic-bezier arrays |
Per-type namespaces
Each value type ships parse / format / convert helpers under its own namespace:
color.*— 14 color spaces, culori-backed conversiondimension.*— parse and format"16px","2rem", etc.duration.*— parse and format"100ms","1.5s"composite.*— helpers forborder,strokeStyle,transition,shadow,gradient,typography
Resolver Module
| Symbol | Purpose |
|---|---|
parseResolver(json) | Parse a .resolver.json document with sets, modifiers, and resolutionOrder |
resolveResolver(doc, context, fileLoader) | Resolve sets and apply modifiers; async file loading via callback |
mergeDocuments(...docs) | Merge multiple DTCG documents into one |
Extensions
| Symbol | Purpose |
|---|---|
isValidNamespace(string) | Validate reverse-DNS vendor namespaces (e.g. dev.styleframe.*) |
CLI integration
The styleframe dtcg export command is the primary consumer of this package. The flow is:
- Load your Styleframe configuration via
@styleframe/loader. - Evaluate every variable to a primitive or CSS expression.
- Use
classifyValue()to stamp the correct$typeon each token. - Use
formatAlias()to preserveref()references as DTCG aliases ({token.path}). - Emit a spec-conformant
tokens.json— and atokens.resolver.jsonif your project defines themes.
styleframe dtcg export -o tokens.json
The output is consumable by any DTCG-compatible tool — including Tokens Studio, Style Dictionary, and the Styleframe Figma plugin (which performs DTCG → Figma value conversion). For the full Figma round-trip workflow, see the Figma Plugin page.
@styleframe/dtcg. The package itself only emits and consumes spec-compliant DTCG.Programmatic usage
You can use @styleframe/dtcg directly as a library in any tooling pipeline.
pnpm add @styleframe/dtcg
yarn add @styleframe/dtcg
npm install @styleframe/dtcg
bun add @styleframe/dtcg
import {
parse,
validate,
applyInheritance,
resolveAliases,
walk,
} from '@styleframe/dtcg';
const doc = parse(jsonString);
const errors = validate(doc);
if (errors.length > 0) {
throw new Error(`Invalid DTCG document: ${errors[0].message}`);
}
const inherited = applyInheritance(doc);
const resolved = resolveAliases(inherited);
for (const entry of walk(resolved)) {
console.log(entry.path, entry.token);
}
Diagnostic extensions
When the CLI cannot losslessly express a Styleframe value as a primitive DTCG type, it preserves it via the standard $extensions mechanism under the dev.styleframe namespace:
| Extension | Meaning |
|---|---|
dev.styleframe.unknownType | The value's DTCG type could not be inferred — token kept as-is |
dev.styleframe.expression | Original CSS expression (e.g. clamp(), calc()) preserved alongside a fallback |
dev.styleframe.fluidBound | A fluid value was normalised to its min or max bound |
Downstream tools that don't recognise these extensions ignore them safely. Tools that do (such as the Styleframe Figma plugin) use them to round-trip without information loss.