extract_tokens_from_json
Parse W3C Design Tokens Community Group (DTCG) format JSON to extract universal design tokens for cross-platform translation.
Instructions
Parse W3C Design Tokens Community Group (DTCG) format JSON into universal tokens
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| json | Yes | W3C DTCG format JSON string |
Implementation Reference
- src/tools/extract-dtcg.ts:20-136 (handler)Main handler function `extractTokensFromDTCG` that parses W3C DTCG format JSON and extracts design tokens. It flattens nested token structures, resolves aliases, and maps tokens to colors, typography, spacing, radii, elevation, and motion categories.
export function extractTokensFromDTCG(jsonString: string): DesignTokens { let data: Record<string, unknown>; try { data = JSON.parse(jsonString); } catch { throw new Error("Invalid JSON: could not parse DTCG token file."); } // Flatten the nested token tree into a flat map const flatTokens = new Map<string, DTCGToken>(); flattenTokens(data, [], flatTokens); if (flatTokens.size === 0) { throw new Error("No DTCG tokens found (no objects with $value)."); } const tokens: DesignTokens = {}; const colors: Record<string, ColorToken> = {}; const typography: Record<string, TypographyToken> = {}; const spacing: Record<string, number> = {}; const radii: Record<string, number> = {}; const elevation: Record<string, ElevationToken> = {}; const motion: Record<string, MotionToken> = {}; for (const [path, token] of flatTokens) { const resolvedValue = resolveAliases(token.$value, flatTokens); const type = token.$type ?? inferType(resolvedValue); const name = path.split(".").pop()!; switch (type) { case "color": { if (typeof resolvedValue === "string") { const hex = normalizeColor(resolvedValue); if (hex) { colors[name] = { value: hex, ...(token.$description ? { description: token.$description } : {}), }; } } break; } case "dimension": { const px = parseDimension(resolvedValue); if (px !== null) { const lowerPath = path.toLowerCase(); if (lowerPath.includes("radius") || lowerPath.includes("corner")) { radii[name] = px; } else { spacing[name] = px; } } break; } case "fontFamily": case "fontSize": case "fontWeight": case "typography": { if (type === "typography" && typeof resolvedValue === "object" && resolvedValue !== null) { const comp = resolvedValue as Record<string, unknown>; const typo: TypographyToken = { fontSize: parseDimension(comp.fontSize) ?? 16, }; if (comp.fontFamily) typo.fontFamily = String(comp.fontFamily); if (comp.fontWeight) typo.fontWeight = Number(comp.fontWeight); if (comp.lineHeight) typo.lineHeight = parseDimension(comp.lineHeight) ?? undefined; if (comp.letterSpacing) typo.letterSpacing = parseDimension(comp.letterSpacing) ?? undefined; typography[name] = typo; } else if (type === "fontSize") { const px = parseDimension(resolvedValue); if (px !== null) typography[name] = { ...typography[name], fontSize: px }; } else if (type === "fontWeight" && typeof resolvedValue === "number") { typography[name] = { ...typography[name], fontSize: typography[name]?.fontSize ?? 16, fontWeight: resolvedValue }; } else if (type === "fontFamily" && typeof resolvedValue === "string") { typography[name] = { ...typography[name], fontSize: typography[name]?.fontSize ?? 16, fontFamily: resolvedValue }; } break; } case "shadow": { const shadow = parseShadow(resolvedValue); if (shadow) elevation[name] = shadow; break; } case "duration": { const ms = parseDuration(resolvedValue); if (ms !== null) { motion[name] = { duration: ms, easing: "ease" }; } break; } case "cubicBezier": { if (Array.isArray(resolvedValue) && resolvedValue.length === 4) { const [x1, y1, x2, y2] = resolvedValue; motion[name] = { duration: motion[name]?.duration ?? 300, easing: `cubic-bezier(${x1}, ${y1}, ${x2}, ${y2})`, }; } break; } } } if (Object.keys(colors).length > 0) tokens.colors = colors; if (Object.keys(typography).length > 0) tokens.typography = typography; if (Object.keys(spacing).length > 0) tokens.spacing = spacing; if (Object.keys(radii).length > 0) tokens.radii = radii; if (Object.keys(elevation).length > 0) tokens.elevation = elevation; if (Object.keys(motion).length > 0) tokens.motion = motion; const hasContent = Object.values(tokens).some((v) => v !== undefined); if (!hasContent) { throw new Error("DTCG tokens found but none could be mapped to design tokens."); } return tokens; } - src/index.ts:96-113 (registration)Tool registration for 'extract_tokens_from_json' with schema defining a 'json' string input parameter. The handler calls extractTokensFromDTCG and returns formatted JSON output or error.
server.registerTool( "extract_tokens_from_json", { description: "Parse W3C Design Tokens Community Group (DTCG) format JSON into universal tokens", inputSchema: { json: z.string().describe("W3C DTCG format JSON string"), }, }, async ({ json }) => { try { const tokens = extractTokensFromDTCG(json); return toolResult(JSON.stringify(tokens, null, 2)); } catch (e) { return errorResult(e); } } ); - src/types/tokens.ts:41-48 (schema)DesignTokens interface defining the canonical output structure with optional colors, typography, spacing, radii, elevation, and motion token collections.
export interface DesignTokens { colors?: Record<string, ColorToken>; typography?: Record<string, TypographyToken>; spacing?: Record<string, number>; // px values radii?: Record<string, number>; // px values elevation?: Record<string, ElevationToken>; motion?: Record<string, MotionToken>; } - src/tools/extract-dtcg.ts:138-166 (helper)Helper function `flattenTokens` that recursively traverses the DTCG token tree and extracts tokens with $value fields into a flat map, handling inherited $type from parent groups.
function flattenTokens( obj: Record<string, unknown>, path: string[], result: Map<string, DTCGToken>, inheritedType?: string ): void { // Check if this node has a group-level $type const groupType = typeof obj.$type === "string" ? obj.$type : inheritedType; for (const [key, value] of Object.entries(obj)) { if (key.startsWith("$")) continue; // Skip meta fields if (typeof value === "object" && value !== null) { const record = value as Record<string, unknown>; if ("$value" in record) { // This is a token const token: DTCGToken = { $value: record.$value, $type: (record.$type as string) ?? groupType, $description: record.$description as string | undefined, }; result.set([...path, key].join("."), token); } else { // This is a group — recurse flattenTokens(record, [...path, key], result, groupType); } } } } - src/tools/extract-dtcg.ts:168-182 (helper)Helper function `resolveAliases` that resolves DTCG alias references (e.g., '{path.to.token}') by looking up and dereferencing token values from the flat token map.
function resolveAliases( value: unknown, tokens: Map<string, DTCGToken>, depth = 0 ): unknown { if (depth > 10) return value; if (typeof value === "string" && value.startsWith("{") && value.endsWith("}")) { const ref = value.slice(1, -1); const target = tokens.get(ref); if (target) { return resolveAliases(target.$value, tokens, depth + 1); } } return value; }