@7onic-ui/tokens 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli/sync.js ADDED
@@ -0,0 +1,2533 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // scripts/cli-sync.ts
27
+ var fs2 = __toESM(require("node:fs"));
28
+ var path2 = __toESM(require("node:path"));
29
+
30
+ // scripts/sync-tokens.ts
31
+ var fs = __toESM(require("node:fs"));
32
+ var path = __toESM(require("node:path"));
33
+ var readline = __toESM(require("node:readline"));
34
+ var ROOT = path.resolve(__dirname, "..");
35
+ var TOKENS_PATH = path.join(ROOT, "tokens/figma-tokens.json");
36
+ var VARIABLES_CSS_PATH = path.join(ROOT, "tokens/css/variables.css");
37
+ var V3_PRESET_PATH = path.join(ROOT, "tokens/tailwind/v3-preset.js");
38
+ var V4_THEME_PATH = path.join(ROOT, "tokens/tailwind/v4-theme.css");
39
+ var GLOBALS_CSS_PATH = path.join(ROOT, "src/styles/globals.css");
40
+ var THEME_LIGHT_PATH = path.join(ROOT, "tokens/css/themes/light.css");
41
+ var THEME_DARK_PATH = path.join(ROOT, "tokens/css/themes/dark.css");
42
+ var JS_CJS_PATH = path.join(ROOT, "tokens/js/index.js");
43
+ var JS_ESM_PATH = path.join(ROOT, "tokens/js/index.mjs");
44
+ var TYPES_PATH = path.join(ROOT, "tokens/types/index.d.ts");
45
+ var JSON_PATH = path.join(ROOT, "tokens/json/tokens.json");
46
+ var CSS_ALL_PATH = path.join(ROOT, "tokens/css/all.css");
47
+ var V4_ALL_PATH = path.join(ROOT, "tokens/tailwind/v4.css");
48
+ var BRANDS_DIR = path.join(ROOT, "tokens/brands");
49
+ var DOCS_SITE_CSS_PATH = path.join(ROOT, "src/styles/docs-site.css");
50
+ function validateTokens(tokens) {
51
+ const warnings = [];
52
+ const p = tokens.primitive;
53
+ const colorData = p.color;
54
+ const validPrimitivePaths = /* @__PURE__ */ new Set();
55
+ if (colorData) {
56
+ for (const [palette, paletteData] of Object.entries(colorData)) {
57
+ if (palette.startsWith("$")) continue;
58
+ if (paletteData && typeof paletteData === "object" && "value" in paletteData) {
59
+ validPrimitivePaths.add(`primitive.color.${palette}`);
60
+ } else if (paletteData && typeof paletteData === "object") {
61
+ for (const shade of Object.keys(paletteData)) {
62
+ if (shade.startsWith("$")) continue;
63
+ validPrimitivePaths.add(`primitive.color.${palette}.${shade}`);
64
+ }
65
+ }
66
+ }
67
+ }
68
+ for (const theme of ["light", "dark"]) {
69
+ const themeData = tokens[theme];
70
+ if (!themeData?.color) continue;
71
+ for (const [category, catData] of Object.entries(themeData.color)) {
72
+ if (category.startsWith("$") || !catData) continue;
73
+ for (const [variant, token] of Object.entries(catData)) {
74
+ if (variant.startsWith("$")) continue;
75
+ const val = String(token.value);
76
+ if (val.startsWith("#") || val.match(/^[0-9a-fA-F]{3,8}$/) && !val.startsWith("{")) {
77
+ warnings.push({
78
+ level: "warn",
79
+ theme,
80
+ category,
81
+ variant,
82
+ value: val,
83
+ message: `Hardcoded hex value. Should reference {primitive.color.*}`
84
+ });
85
+ continue;
86
+ }
87
+ if (val.startsWith("{") && val.endsWith("}")) {
88
+ const refPath = val.slice(1, -1);
89
+ if (refPath.startsWith("primitive.color.") && !validPrimitivePaths.has(refPath)) {
90
+ warnings.push({
91
+ level: "error",
92
+ theme,
93
+ category,
94
+ variant,
95
+ value: val,
96
+ message: `References non-existent primitive: ${refPath}`
97
+ });
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ return warnings;
104
+ }
105
+ function printTokenWarnings(warnings) {
106
+ if (warnings.length === 0) return;
107
+ const errors = warnings.filter((w) => w.level === "error");
108
+ const warns = warnings.filter((w) => w.level === "warn");
109
+ if (warns.length > 0) {
110
+ console.log("");
111
+ console.log(`\u26A0\uFE0F Hardcoded hex in semantic colors (${warns.length} found):`);
112
+ console.log(" These tokens reference raw hex instead of primitive color variables.");
113
+ console.log(" Fix in figma-tokens.json by using {primitive.color.*} references.\n");
114
+ for (const w of warns) {
115
+ console.log(` ${w.theme}.color.${w.category}.${w.variant}: ${w.value}`);
116
+ }
117
+ }
118
+ if (errors.length > 0) {
119
+ console.log("");
120
+ console.log(`\u274C Invalid references (${errors.length} found):
121
+ `);
122
+ for (const w of errors) {
123
+ console.log(` ${w.theme}.color.${w.category}.${w.variant}: ${w.message}`);
124
+ }
125
+ }
126
+ }
127
+ function readJsonFile(filePath) {
128
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
129
+ }
130
+ function camelToKebab(str) {
131
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
132
+ }
133
+ function pxToRem(px) {
134
+ const n = typeof px === "string" ? parseFloat(px) : px;
135
+ if (n === 0) return "0";
136
+ const rem = n / 16;
137
+ return `${parseFloat(rem.toFixed(4))}rem`;
138
+ }
139
+ function pxComment(px) {
140
+ const n = typeof px === "string" ? parseFloat(px) : px;
141
+ if (n === 0) return "";
142
+ return `/* ${n}px */`;
143
+ }
144
+ function resolveReference(ref, allTokens, depth = 0) {
145
+ if (depth > 10) throw new Error(`Circular reference detected: ${ref}`);
146
+ if (!ref.startsWith("{") || !ref.endsWith("}")) return ref;
147
+ const tokenPath = ref.slice(1, -1).split(".");
148
+ let current = allTokens;
149
+ for (const key of tokenPath) {
150
+ if (current && typeof current === "object" && key in current) {
151
+ current = current[key];
152
+ } else {
153
+ return ref;
154
+ }
155
+ }
156
+ if (current && typeof current === "object" && "value" in current) {
157
+ const val = current.value;
158
+ if (typeof val === "string" && val.startsWith("{")) {
159
+ return resolveReference(val, allTokens, depth + 1);
160
+ }
161
+ return String(val);
162
+ }
163
+ return ref;
164
+ }
165
+ function semanticColorVar(category, variant) {
166
+ return variant === "default" ? `--color-${category}` : `--color-${category}-${variant}`;
167
+ }
168
+ function stripDefaultFromPath(dotPath) {
169
+ return dotPath.replace(/\.default$/, "").replace(/\./g, "-");
170
+ }
171
+ function referenceToVar(ref) {
172
+ if (!ref.startsWith("{") || !ref.endsWith("}")) return null;
173
+ const tokenPath = ref.slice(1, -1);
174
+ if (tokenPath.startsWith("primitive.color.")) {
175
+ const colorPath = tokenPath.replace("primitive.color.", "");
176
+ const varName = colorPath.replace(/\./g, "-");
177
+ return `var(--color-${varName})`;
178
+ }
179
+ return null;
180
+ }
181
+ function resolveCompositionToColorMix(value) {
182
+ const colorPath = value.color.slice(1, -1);
183
+ const colorMatch = colorPath.match(/^(?:light|dark)\.color\.(.+)$/);
184
+ const colorVar = colorMatch ? `var(--color-${stripDefaultFromPath(colorMatch[1])})` : value.color;
185
+ const opacityMatch = value.opacity.match(/\{primitive\.opacity\.(\d+)\}/);
186
+ const pct = opacityMatch ? opacityMatch[1] : "0";
187
+ return `color-mix(in srgb, ${colorVar} ${pct}%, transparent)`;
188
+ }
189
+ function resolveToVar(value, tokens) {
190
+ if (!value.startsWith("{") || !value.endsWith("}")) return value;
191
+ const varRef = referenceToVar(value);
192
+ if (varRef) return varRef;
193
+ const tokenPath = value.slice(1, -1).split(".");
194
+ let current = tokens;
195
+ for (const key of tokenPath) {
196
+ if (current && typeof current === "object" && key in current) {
197
+ current = current[key];
198
+ } else {
199
+ return resolveReference(value, tokens);
200
+ }
201
+ }
202
+ if (current && typeof current === "object" && "value" in current) {
203
+ const innerVal = current.value;
204
+ if (typeof innerVal === "string" && innerVal.startsWith("{")) {
205
+ const innerVar = referenceToVar(innerVal);
206
+ if (innerVar) return innerVar;
207
+ return resolveToVar(innerVal, tokens);
208
+ }
209
+ return String(innerVal);
210
+ }
211
+ return resolveReference(value, tokens);
212
+ }
213
+ function parseOpacityFromRgba(color) {
214
+ const match = color.match(/rgba?\([^)]*,\s*([\d.]+)\s*\)/);
215
+ return match ? parseFloat(match[1]) : 1;
216
+ }
217
+ function formatShadowLayer(s, ext) {
218
+ const x = s.x === "0" ? "0" : `${s.x}px`;
219
+ const y = s.y === "0" ? "0" : `${s.y}px`;
220
+ const blur = s.blur === "0" ? "0" : `${s.blur}px`;
221
+ const spread = s.spread === "0" ? "0" : `${s.spread}px`;
222
+ if (ext?.colorReference) {
223
+ const varName = `var(--color-${stripDefaultFromPath(ext.colorReference)})`;
224
+ const opacity = ext.colorOpacity ?? parseOpacityFromRgba(s.color);
225
+ return `${x} ${y} ${blur} ${spread} color-mix(in srgb, ${varName} ${Math.round(opacity * 100)}%, transparent)`;
226
+ }
227
+ return `${x} ${y} ${blur} ${spread} ${s.color}`;
228
+ }
229
+ function formatShadowLayerDark(s, ext) {
230
+ const x = s.x === "0" ? "0" : `${s.x}px`;
231
+ const y = s.y === "0" ? "0" : `${s.y}px`;
232
+ const blur = s.blur === "0" ? "0" : `${s.blur}px`;
233
+ const spread = s.spread === "0" ? "0" : `${s.spread}px`;
234
+ if (ext?.colorReference) {
235
+ const varName = `var(--color-${stripDefaultFromPath(ext.colorReference)})`;
236
+ const opacity = ext.darkColorOpacity ?? ext.colorOpacity ?? parseOpacityFromRgba(s.color);
237
+ return `${x} ${y} ${blur} ${spread} color-mix(in srgb, ${varName} ${Math.round(opacity * 100)}%, transparent)`;
238
+ }
239
+ return `${x} ${y} ${blur} ${spread} ${s.color}`;
240
+ }
241
+ function formatShadow(shadow, ext) {
242
+ if (Array.isArray(shadow)) {
243
+ return shadow.map((s) => formatShadowLayer(s, ext)).join(", ");
244
+ }
245
+ return formatShadowLayer(shadow, ext);
246
+ }
247
+ function formatShadowDark(shadow, ext) {
248
+ if (ext?.darkValue) {
249
+ const dv = ext.darkValue;
250
+ if (Array.isArray(dv)) {
251
+ return dv.map((s) => formatShadowLayerDark(s, ext)).join(", ");
252
+ }
253
+ return formatShadowLayerDark(dv, ext);
254
+ }
255
+ if (Array.isArray(shadow)) {
256
+ return shadow.map((s) => formatShadowLayerDark(s, ext)).join(", ");
257
+ }
258
+ return formatShadowLayerDark(shadow, ext);
259
+ }
260
+ function formatValue(value, type) {
261
+ if (type === "color") return value;
262
+ if (type === "fontSizes" || type === "spacing") {
263
+ return pxToRem(value);
264
+ }
265
+ if (type === "borderRadius" || type === "borderWidth" || type === "dimension" || type === "sizing") {
266
+ return `${value}px`;
267
+ }
268
+ return value;
269
+ }
270
+ function spacingKeyToCssKey(key) {
271
+ return key.replace(/\./g, "-");
272
+ }
273
+ function orderedKeys(data, strategy, sectionLabel) {
274
+ const allKeys = Object.keys(data).filter((k) => !k.startsWith("$"));
275
+ switch (strategy.type) {
276
+ case "known": {
277
+ const known = strategy.order.filter((k) => allKeys.includes(k));
278
+ const unknown = allKeys.filter((k) => !strategy.order.includes(k));
279
+ if (unknown.length > 0 && sectionLabel) {
280
+ console.warn(`\u26A0\uFE0F [${sectionLabel}] New keys detected: ${unknown.join(", ")}`);
281
+ }
282
+ return [...known, ...unknown];
283
+ }
284
+ case "numeric":
285
+ return allKeys.sort((a, b) => parseFloat(a) - parseFloat(b));
286
+ case "json":
287
+ return allKeys;
288
+ }
289
+ }
290
+ function sortByKnownOrder(entries, order) {
291
+ return [...entries].sort((a, b) => {
292
+ const ai = order.indexOf(a[0]);
293
+ const bi = order.indexOf(b[0]);
294
+ return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
295
+ });
296
+ }
297
+ function resolveLineHeight(v, fontSizeData) {
298
+ if (!v.lineHeight) return null;
299
+ if (v.fontSize && v.fontSize.startsWith("{") && v.fontSize.endsWith("}")) {
300
+ const sizeRef = v.fontSize.slice(1, -1).split(".").pop();
301
+ const primToken = fontSizeData[sizeRef];
302
+ if (primToken) {
303
+ const primLh = primToken.$extensions?.lineHeight;
304
+ if (primLh && String(primLh) === String(v.lineHeight)) {
305
+ return { value: `var(--line-height-${sizeRef})`, comment: `${primLh}px` };
306
+ }
307
+ if (primLh) {
308
+ return { value: pxToRem(v.lineHeight), comment: `override: ${sizeRef} default = ${primLh}px` };
309
+ }
310
+ }
311
+ }
312
+ return { value: pxToRem(v.lineHeight) };
313
+ }
314
+ function inlineComment(comment) {
315
+ return comment ? ` /* ${comment} */` : "";
316
+ }
317
+ var KNOWN_ORDERS = {
318
+ colorPalettes: ["white", "black", "gray", "primary", "secondary", "blue", "green", "yellow", "red", "chart"],
319
+ fontSize: ["2xs", "xs", "sm", "md", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
320
+ borderRadius: ["none", "sm", "base", "md", "lg", "xl", "2xl", "3xl", "full"],
321
+ shadow: ["xs", "sm", "md", "lg", "xl", "primary-glow"],
322
+ iconSize: ["xs", "sm", "md", "lg", "xl"],
323
+ zIndex: ["0", "10", "20", "30", "40", "50", "sticky", "dropdown", "overlay", "modal", "popover", "tooltip", "toast"],
324
+ duration: ["instant", "fast", "micro", "normal", "slow", "slower", "slowest"],
325
+ easing: ["linear", "ease", "easeIn", "easeOut", "easeInOut"],
326
+ breakpoint: ["sm", "md", "lg", "xl", "2xl"],
327
+ shadeOrder: ["50", "100", "200", "300", "400", "500", "600", "700", "800", "900"],
328
+ semanticColorCategories: [
329
+ "background",
330
+ "text",
331
+ "primary",
332
+ "secondary",
333
+ "success",
334
+ "warning",
335
+ "error",
336
+ "info",
337
+ "border",
338
+ "disabled",
339
+ "focus",
340
+ "chart"
341
+ ],
342
+ semanticColorVariants: [
343
+ "default",
344
+ "paper",
345
+ "elevated",
346
+ "muted",
347
+ "hover",
348
+ "active",
349
+ "tint",
350
+ "text",
351
+ "subtle",
352
+ "link",
353
+ "primary",
354
+ "info",
355
+ "success",
356
+ "error",
357
+ "warning",
358
+ "strong",
359
+ "ring",
360
+ "ring-error",
361
+ "bg",
362
+ "1",
363
+ "2",
364
+ "3",
365
+ "4",
366
+ "5"
367
+ ],
368
+ typographyCategories: ["heading", "body", "label"],
369
+ typographyOrders: { heading: ["1", "2", "3", "4", "5", "6"], body: ["lg", "default", "md", "sm", "xs", "2xs"], label: ["lg", "md", "default", "sm", "xs"] }
370
+ };
371
+ function readAnimationTokens(tokens) {
372
+ const sem = tokens.semantic;
373
+ const animation = sem?.animation;
374
+ if (!animation) return null;
375
+ const p = tokens.primitive;
376
+ const result = [];
377
+ for (const [name, entry] of Object.entries(animation)) {
378
+ if (name.startsWith("$")) continue;
379
+ const token = entry;
380
+ if (token.type !== "composition") continue;
381
+ const val = token.value;
382
+ const ext = token.$extensions;
383
+ const durationKey = extractRefKey(val.duration);
384
+ const easingKey = extractRefKey(val.easing);
385
+ const durationVar = `var(--duration-${durationKey})`;
386
+ const easingVar = `var(--easing-${camelToKebab(easingKey)})`;
387
+ const animationType = ext?.animationType;
388
+ if (animationType === "spin") {
389
+ result.push({ name, type: "spin", opacity: "", scale: "", translateY: "", translateYNegative: false, heightVar: "", durationVar, easingVar });
390
+ continue;
391
+ }
392
+ if (animationType === "height-expand" || animationType === "height-collapse") {
393
+ result.push({ name, type: animationType, opacity: "", scale: "", translateY: "", translateYNegative: false, heightVar: val.heightVar, durationVar, easingVar });
394
+ continue;
395
+ }
396
+ const direction = ext?.direction;
397
+ const type = direction === "exit" ? "exit" : "enter";
398
+ const opacityRaw = val.opacity ? resolveRef(val.opacity, p) : "";
399
+ const scaleRaw = val.scale ? resolveRef(val.scale, p) : "";
400
+ const translateYRaw = val.translateY ? resolveRef(val.translateY, p) : "";
401
+ const translateYNegative = ext?.translateYNegative === true;
402
+ result.push({ name, type, opacity: opacityRaw, scale: scaleRaw, translateY: translateYRaw, translateYNegative, heightVar: "", durationVar, easingVar });
403
+ }
404
+ return result.length > 0 ? result : null;
405
+ }
406
+ function resolveRef(ref, primitive) {
407
+ const match = ref.match(/^\{primitive\.(\w+)\.([\w.]+)\}$/);
408
+ if (!match) return ref;
409
+ const [, section, key] = match;
410
+ const sectionData = primitive[section];
411
+ return sectionData?.[key]?.value != null ? String(sectionData[key].value) : ref;
412
+ }
413
+ function extractRefKey(ref) {
414
+ const match = ref.match(/^\{primitive\.\w+\.(\w+)\}$/);
415
+ return match ? match[1] : ref;
416
+ }
417
+ function generateAnimationCss(a, format) {
418
+ const lines = [];
419
+ if (a.type === "spin") {
420
+ lines.push(`@keyframes ${a.name} {`);
421
+ lines.push(` from { transform: rotate(0deg); }`);
422
+ lines.push(` to { transform: rotate(360deg); }`);
423
+ lines.push(`}`);
424
+ if (format === "v4") {
425
+ lines.push(`@utility animate-${a.name} {`);
426
+ } else {
427
+ lines.push(`.animate-${a.name} {`);
428
+ }
429
+ lines.push(` animation: ${a.name} ${a.durationVar} ${a.easingVar} infinite;`);
430
+ lines.push(`}`);
431
+ return lines.join("\n");
432
+ }
433
+ if (a.type === "height-expand" || a.type === "height-collapse") {
434
+ const isExpand = a.type === "height-expand";
435
+ lines.push(`@keyframes ${a.name} {`);
436
+ lines.push(` from { height: ${isExpand ? "0" : `var(${a.heightVar})`}; }`);
437
+ lines.push(` to { height: ${isExpand ? `var(${a.heightVar})` : "0"}; }`);
438
+ lines.push(`}`);
439
+ } else {
440
+ const isEnter = a.type === "enter";
441
+ const fromProps = [];
442
+ const toProps = [];
443
+ if (a.opacity) {
444
+ fromProps.push(`opacity: ${isEnter ? a.opacity : "1"}`);
445
+ toProps.push(`opacity: ${isEnter ? "1" : a.opacity}`);
446
+ }
447
+ const fromT = [], toT = [];
448
+ if (a.scale) {
449
+ fromT.push(isEnter ? `scale(${a.scale})` : "scale(1)");
450
+ toT.push(isEnter ? "scale(1)" : `scale(${a.scale})`);
451
+ }
452
+ if (a.translateY) {
453
+ const px = `${a.translateYNegative ? "-" : ""}${a.translateY}px`;
454
+ fromT.push(isEnter ? `translateY(${px})` : "translateY(0)");
455
+ toT.push(isEnter ? "translateY(0)" : `translateY(${px})`);
456
+ }
457
+ if (fromT.length) {
458
+ fromProps.push(`transform: ${fromT.join(" ")}`);
459
+ toProps.push(`transform: ${toT.join(" ")}`);
460
+ }
461
+ lines.push(`@keyframes ${a.name} {`);
462
+ lines.push(` from { ${fromProps.join("; ")}; }`);
463
+ lines.push(` to { ${toProps.join("; ")}; }`);
464
+ lines.push(`}`);
465
+ }
466
+ if (format === "v4") {
467
+ lines.push(`@utility animate-${a.name} {`);
468
+ } else {
469
+ lines.push(`.animate-${a.name} {`);
470
+ }
471
+ lines.push(` animation: ${a.name} ${a.durationVar} ${a.easingVar};`);
472
+ lines.push(`}`);
473
+ return lines.join("\n");
474
+ }
475
+ function hexToRgb(hex) {
476
+ const match = hex.replace("#", "").match(/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i);
477
+ if (!match) return null;
478
+ return `${parseInt(match[1], 16)} ${parseInt(match[2], 16)} ${parseInt(match[3], 16)}`;
479
+ }
480
+ function generateVariablesCss(tokens) {
481
+ const lines = [];
482
+ const p = tokens.primitive;
483
+ lines.push(`/**`);
484
+ lines.push(` * Design System - CSS Variables`);
485
+ lines.push(` * \u26A0\uFE0F Auto-generated from figma-tokens.json \u2014 DO NOT EDIT`);
486
+ lines.push(` *`);
487
+ lines.push(` * No Tailwind required. Just import:`);
488
+ lines.push(` * @import '@7onic-ui/tokens/css/variables.css';`);
489
+ lines.push(` *`);
490
+ lines.push(` * Regenerate: npx sync-tokens`);
491
+ lines.push(` */`);
492
+ lines.push(``);
493
+ lines.push(`:root {`);
494
+ lines.push(` /* ========================================`);
495
+ lines.push(` Primitive Colors`);
496
+ lines.push(` Raw color palette \u2014 the building blocks`);
497
+ lines.push(` for semantic color tokens`);
498
+ lines.push(` ======================================== */`);
499
+ lines.push(``);
500
+ const colorData = p.color;
501
+ const colorPalettes = orderedKeys(colorData, { type: "known", order: KNOWN_ORDERS.colorPalettes }, "primitive.color");
502
+ for (const palette of colorPalettes) {
503
+ const paletteData = colorData[palette];
504
+ if (!paletteData) continue;
505
+ if (typeof paletteData === "object" && "value" in paletteData) {
506
+ if (palette === "white" || palette === "black") {
507
+ if (palette === "white") lines.push(` /* Base */`);
508
+ const hexVal = String(paletteData.value);
509
+ lines.push(` --color-${palette}: ${hexVal};`);
510
+ const rgb = hexToRgb(hexVal);
511
+ if (rgb) lines.push(` --color-${palette}-rgb: ${rgb};`);
512
+ if (palette === "black") lines.push(``);
513
+ }
514
+ } else {
515
+ const shades = paletteData;
516
+ const paletteLabel = palette.charAt(0).toUpperCase() + palette.slice(1);
517
+ lines.push(` /* ${paletteLabel} */`);
518
+ const shadeKeys = orderedKeys(shades, { type: "numeric" }, `primitive.color.${palette}`);
519
+ for (const shade of shadeKeys) {
520
+ const token = shades[shade];
521
+ if (token && token.value) {
522
+ const hexVal = String(token.value);
523
+ lines.push(` --color-${palette}-${shade}: ${hexVal};`);
524
+ const rgb = hexToRgb(hexVal);
525
+ if (rgb) lines.push(` --color-${palette}-${shade}-rgb: ${rgb};`);
526
+ }
527
+ }
528
+ lines.push(``);
529
+ }
530
+ }
531
+ const fontFamilyData = p.fontFamily;
532
+ lines.push(` /* ========================================`);
533
+ lines.push(` Font Family`);
534
+ lines.push(` ======================================== */`);
535
+ for (const [name, token] of Object.entries(fontFamilyData)) {
536
+ if (name.startsWith("$")) continue;
537
+ lines.push(` --font-family-${name}: ${token.value};`);
538
+ }
539
+ lines.push(``);
540
+ const fontSizeData = p.fontSize;
541
+ lines.push(` /* ========================================`);
542
+ lines.push(` Typography`);
543
+ lines.push(` ======================================== */`);
544
+ for (const name of orderedKeys(fontSizeData, { type: "known", order: KNOWN_ORDERS.fontSize }, "primitive.fontSize")) {
545
+ const token = fontSizeData[name];
546
+ if (!token) continue;
547
+ const lh = token.$extensions?.lineHeight;
548
+ lines.push(` --font-size-${name}: ${formatValue(String(token.value), "fontSizes")}; ${pxComment(String(token.value))}`);
549
+ if (lh) lines.push(` --line-height-${name}: ${pxToRem(lh)}; ${pxComment(lh)}`);
550
+ }
551
+ lines.push(``);
552
+ const fontWeightData = p.fontWeight;
553
+ lines.push(` /* ========================================`);
554
+ lines.push(` Font Weight`);
555
+ lines.push(` ======================================== */`);
556
+ for (const [name, token] of Object.entries(fontWeightData)) {
557
+ if (name.startsWith("$")) continue;
558
+ lines.push(` --font-weight-${name}: ${token.value};`);
559
+ }
560
+ lines.push(``);
561
+ const spacingData = p.spacing;
562
+ lines.push(` /* ========================================`);
563
+ lines.push(` Spacing`);
564
+ lines.push(` 0~12px: 2px increments, 12px~: 4px increments`);
565
+ lines.push(` ======================================== */`);
566
+ for (const key of orderedKeys(spacingData, { type: "numeric" }, "primitive.spacing")) {
567
+ const token = spacingData[key];
568
+ lines.push(` --spacing-${spacingKeyToCssKey(key)}: ${formatValue(String(token.value), "spacing")}; ${pxComment(String(token.value))}`);
569
+ }
570
+ lines.push(``);
571
+ const radiusData = p.borderRadius;
572
+ lines.push(` /* ========================================`);
573
+ lines.push(` Border Radius`);
574
+ lines.push(` ======================================== */`);
575
+ for (const name of orderedKeys(radiusData, { type: "known", order: KNOWN_ORDERS.borderRadius }, "primitive.borderRadius")) {
576
+ const token = radiusData[name];
577
+ if (!token) continue;
578
+ lines.push(` --radius-${name}: ${formatValue(String(token.value), "borderRadius")};`);
579
+ }
580
+ lines.push(``);
581
+ const borderWidthData = p.borderWidth;
582
+ lines.push(` /* ========================================`);
583
+ lines.push(` Border Width`);
584
+ lines.push(` ======================================== */`);
585
+ for (const key of orderedKeys(borderWidthData, { type: "numeric" }, "primitive.borderWidth")) {
586
+ lines.push(` --border-width-${key}: ${formatValue(String(borderWidthData[key].value), "borderWidth")};`);
587
+ }
588
+ lines.push(``);
589
+ const shadowData = p.shadow;
590
+ lines.push(` /* ========================================`);
591
+ lines.push(` Shadows (Light Mode)`);
592
+ lines.push(` ======================================== */`);
593
+ for (const name of orderedKeys(shadowData, { type: "known", order: KNOWN_ORDERS.shadow }, "primitive.shadow")) {
594
+ const token = shadowData[name];
595
+ if (!token) continue;
596
+ const ext = token.$extensions;
597
+ lines.push(` --shadow-${name}: ${formatShadow(token.value, ext)};`);
598
+ }
599
+ lines.push(``);
600
+ const iconSizeData = p.iconSize;
601
+ lines.push(` /* ========================================`);
602
+ lines.push(` Icon Size (5-step scale)`);
603
+ lines.push(` ======================================== */`);
604
+ for (const name of orderedKeys(iconSizeData, { type: "known", order: KNOWN_ORDERS.iconSize }, "primitive.iconSize")) {
605
+ const token = iconSizeData[name];
606
+ if (!token) continue;
607
+ lines.push(` --icon-size-${name}: ${pxToRem(String(token.value))}; ${pxComment(String(token.value))}`);
608
+ }
609
+ lines.push(``);
610
+ const scaleData = p.scale;
611
+ lines.push(` /* ========================================`);
612
+ lines.push(` Scale`);
613
+ lines.push(` ======================================== */`);
614
+ for (const [name, token] of Object.entries(scaleData)) {
615
+ if (name.startsWith("$")) continue;
616
+ lines.push(` --scale-${name}: ${token.value};`);
617
+ }
618
+ lines.push(``);
619
+ const zIndexData = p.zIndex;
620
+ lines.push(` /* ========================================`);
621
+ lines.push(` Z-Index`);
622
+ lines.push(` ======================================== */`);
623
+ for (const name of orderedKeys(zIndexData, { type: "known", order: KNOWN_ORDERS.zIndex }, "primitive.zIndex")) {
624
+ const token = zIndexData[name];
625
+ if (!token) continue;
626
+ lines.push(` --z-index-${name}: ${token.value};`);
627
+ }
628
+ lines.push(``);
629
+ const opacityData = p.opacity;
630
+ lines.push(` /* ========================================`);
631
+ lines.push(` Opacity`);
632
+ lines.push(` ======================================== */`);
633
+ for (const key of orderedKeys(opacityData, { type: "numeric" }, "primitive.opacity")) {
634
+ const pct = Math.round(Number(opacityData[key].value) * 100);
635
+ lines.push(` --opacity-${key}: ${pct}%;`);
636
+ }
637
+ lines.push(``);
638
+ const durationData = p.duration;
639
+ lines.push(` /* ========================================`);
640
+ lines.push(` Duration`);
641
+ lines.push(` ======================================== */`);
642
+ for (const name of orderedKeys(durationData, { type: "known", order: KNOWN_ORDERS.duration }, "primitive.duration")) {
643
+ const token = durationData[name];
644
+ if (!token) continue;
645
+ lines.push(` --duration-${name}: ${token.value};`);
646
+ }
647
+ lines.push(``);
648
+ const easingData = p.easing;
649
+ lines.push(` /* ========================================`);
650
+ lines.push(` Easing`);
651
+ lines.push(` ======================================== */`);
652
+ for (const name of orderedKeys(easingData, { type: "known", order: KNOWN_ORDERS.easing }, "primitive.easing")) {
653
+ const token = easingData[name];
654
+ if (!token) continue;
655
+ lines.push(` --easing-${camelToKebab(name)}: ${token.value};`);
656
+ }
657
+ lines.push(``);
658
+ const breakpointData = p.breakpoint;
659
+ lines.push(` /* ========================================`);
660
+ lines.push(` Breakpoints`);
661
+ lines.push(` ======================================== */`);
662
+ for (const name of orderedKeys(breakpointData, { type: "known", order: KNOWN_ORDERS.breakpoint }, "primitive.breakpoint")) {
663
+ const token = breakpointData[name];
664
+ if (!token) continue;
665
+ lines.push(` --breakpoint-${name}: ${formatValue(String(token.value), "dimension")};`);
666
+ }
667
+ lines.push(``);
668
+ const sem = tokens.semantic;
669
+ const compSizeData = sem?.componentSize;
670
+ if (compSizeData) {
671
+ lines.push(` /* ========================================`);
672
+ lines.push(` Component Size`);
673
+ lines.push(` ======================================== */`);
674
+ for (const comp of Object.keys(compSizeData)) {
675
+ const compData = compSizeData[comp];
676
+ if (!compData) continue;
677
+ for (const part of Object.keys(compData)) {
678
+ const partData = compData[part];
679
+ if (!partData) continue;
680
+ for (const size of Object.keys(partData)) {
681
+ const token = partData[size];
682
+ if (!token) continue;
683
+ const val = token.value;
684
+ if (typeof val === "object" && val !== null) {
685
+ const obj = val;
686
+ if (obj.width) lines.push(` --component-${comp}-${part}-${size}-width: ${obj.width}px;`);
687
+ if (obj.height) lines.push(` --component-${comp}-${part}-${size}-height: ${obj.height}px;`);
688
+ } else {
689
+ lines.push(` --component-${comp}-${part}-${size}: ${val}px;`);
690
+ }
691
+ }
692
+ }
693
+ }
694
+ lines.push(``);
695
+ }
696
+ const semTypo = tokens.semantic.typography;
697
+ lines.push(` /* ========================================`);
698
+ lines.push(` Semantic Typography`);
699
+ lines.push(` ======================================== */`);
700
+ const typoCategories = orderedKeys(semTypo, { type: "known", order: [...KNOWN_ORDERS.typographyCategories, "caption", "code"] }, "semantic.typography").filter((k) => KNOWN_ORDERS.typographyCategories.includes(k));
701
+ const typoOrders = {
702
+ ...KNOWN_ORDERS.typographyOrders
703
+ };
704
+ for (const cat of typoCategories) {
705
+ const catData = semTypo[cat];
706
+ if (!catData) continue;
707
+ const order = typoOrders[cat];
708
+ for (const name of order) {
709
+ const token = catData[name];
710
+ if (!token || !token.value) continue;
711
+ const v = token.value;
712
+ const prefix = `--typography-${cat}-${name}`;
713
+ if (v.fontSize) {
714
+ const sizeRef = v.fontSize.slice(1, -1).split(".").pop();
715
+ const fsVal = fontSizeData[sizeRef]?.value;
716
+ lines.push(` ${prefix}-font-size: var(--font-size-${sizeRef});${inlineComment(fsVal ? `${fsVal}px` : void 0)}`);
717
+ }
718
+ const lhResolved = resolveLineHeight(v, fontSizeData);
719
+ if (lhResolved) lines.push(` ${prefix}-line-height: ${lhResolved.value};${inlineComment(lhResolved.comment)}`);
720
+ if (v.fontWeight) {
721
+ const weightRef = v.fontWeight.slice(1, -1).split(".").pop();
722
+ const fwVal = fontWeightData[weightRef]?.value;
723
+ lines.push(` ${prefix}-font-weight: var(--font-weight-${weightRef});${inlineComment(fwVal ? String(fwVal) : void 0)}`);
724
+ }
725
+ if (v.fontFamily) {
726
+ const familyRef = v.fontFamily.slice(1, -1).split(".").pop();
727
+ lines.push(` ${prefix}-font-family: var(--font-family-${familyRef});`);
728
+ }
729
+ }
730
+ }
731
+ const captionToken = semTypo.caption;
732
+ if (captionToken?.value) {
733
+ const cv = captionToken.value;
734
+ if (cv.fontSize) {
735
+ const sizeRef = cv.fontSize.slice(1, -1).split(".").pop();
736
+ const fsVal = fontSizeData[sizeRef]?.value;
737
+ lines.push(` --typography-caption-font-size: var(--font-size-${sizeRef});${inlineComment(fsVal ? `${fsVal}px` : void 0)}`);
738
+ }
739
+ const captionLh = resolveLineHeight(cv, fontSizeData);
740
+ if (captionLh) lines.push(` --typography-caption-line-height: ${captionLh.value};${inlineComment(captionLh.comment)}`);
741
+ if (cv.fontWeight) {
742
+ const weightRef = cv.fontWeight.slice(1, -1).split(".").pop();
743
+ const fwVal = fontWeightData[weightRef]?.value;
744
+ lines.push(` --typography-caption-font-weight: var(--font-weight-${weightRef});${inlineComment(fwVal ? String(fwVal) : void 0)}`);
745
+ }
746
+ if (cv.fontFamily) {
747
+ const familyRef = cv.fontFamily.slice(1, -1).split(".").pop();
748
+ lines.push(` --typography-caption-font-family: var(--font-family-${familyRef});`);
749
+ }
750
+ }
751
+ const codeData = semTypo.code;
752
+ if (codeData) {
753
+ for (const name of ["block", "inline"]) {
754
+ const token = codeData[name];
755
+ if (!token?.value) continue;
756
+ const v = token.value;
757
+ const prefix = `--typography-code-${name}`;
758
+ if (v.fontSize) {
759
+ const sizeRef = v.fontSize.slice(1, -1).split(".").pop();
760
+ const fsVal = fontSizeData[sizeRef]?.value;
761
+ lines.push(` ${prefix}-font-size: var(--font-size-${sizeRef});${inlineComment(fsVal ? `${fsVal}px` : void 0)}`);
762
+ }
763
+ const codeLh = resolveLineHeight(v, fontSizeData);
764
+ if (codeLh) lines.push(` ${prefix}-line-height: ${codeLh.value};${inlineComment(codeLh.comment)}`);
765
+ if (v.fontWeight) {
766
+ const weightRef = v.fontWeight.slice(1, -1).split(".").pop();
767
+ const fwVal = fontWeightData[weightRef]?.value;
768
+ lines.push(` ${prefix}-font-weight: var(--font-weight-${weightRef});${inlineComment(fwVal ? String(fwVal) : void 0)}`);
769
+ }
770
+ if (v.fontFamily) {
771
+ const familyRef = v.fontFamily.slice(1, -1).split(".").pop();
772
+ lines.push(` ${prefix}-font-family: var(--font-family-${familyRef});`);
773
+ }
774
+ }
775
+ }
776
+ lines.push(``);
777
+ lines.push(`}`);
778
+ lines.push(``);
779
+ const animTokens = readAnimationTokens(tokens);
780
+ if (animTokens) {
781
+ lines.push(`/* ========================================`);
782
+ lines.push(` Component Animations`);
783
+ lines.push(` Generated from semantic.animation in figma-tokens.json`);
784
+ lines.push(` Token name = keyframe name = class name (1:1)`);
785
+ lines.push(` ======================================== */`);
786
+ lines.push(``);
787
+ for (const a of animTokens) {
788
+ lines.push(generateAnimationCss(a, "css"));
789
+ lines.push(``);
790
+ }
791
+ }
792
+ return lines.join("\n");
793
+ }
794
+ function generateThemeLight(tokens) {
795
+ const lines = [];
796
+ lines.push(`/**`);
797
+ lines.push(` * Light Theme \u2014 Semantic color overrides`);
798
+ lines.push(` * \u26A0\uFE0F Auto-generated \u2014 DO NOT EDIT`);
799
+ lines.push(` *`);
800
+ lines.push(` * Usage: @import '@7onic-ui/tokens/css/themes/light.css';`);
801
+ lines.push(` */`);
802
+ lines.push(``);
803
+ lines.push(`:root {`);
804
+ const lightColor = tokens.light.color;
805
+ const semanticColorOrder = orderedKeys(lightColor, { type: "known", order: KNOWN_ORDERS.semanticColorCategories }, "light.color");
806
+ for (const category of semanticColorOrder) {
807
+ const catData = lightColor[category];
808
+ if (!catData) continue;
809
+ const catLabel = category.charAt(0).toUpperCase() + category.slice(1);
810
+ lines.push(` /* ${catLabel} */`);
811
+ const entries = Object.entries(catData).filter(([k]) => !k.startsWith("$"));
812
+ const sortedEntries = sortByKnownOrder(entries, KNOWN_ORDERS.semanticColorVariants);
813
+ for (const [variant, token] of sortedEntries) {
814
+ const tv = token;
815
+ if (tv.type === "composition" && typeof tv.value === "object" && tv.value !== null) {
816
+ const comp = tv.value;
817
+ lines.push(` ${semanticColorVar(category, variant)}: ${resolveCompositionToColorMix(comp)};`);
818
+ } else {
819
+ const resolved = typeof tv.value === "string" && tv.value.startsWith("{") ? resolveToVar(tv.value, tokens) : String(tv.value);
820
+ lines.push(` ${semanticColorVar(category, variant)}: ${resolved};`);
821
+ const resolvedHex = typeof tv.value === "string" && tv.value.startsWith("{") ? resolveReference(tv.value, tokens) : String(tv.value);
822
+ const rgb = hexToRgb(resolvedHex);
823
+ if (rgb) lines.push(` ${semanticColorVar(category, variant)}-rgb: ${rgb};`);
824
+ }
825
+ }
826
+ lines.push(``);
827
+ }
828
+ lines.push(`}`);
829
+ lines.push(``);
830
+ return lines.join("\n");
831
+ }
832
+ function generateThemeDark(tokens) {
833
+ const lines = [];
834
+ lines.push(`/**`);
835
+ lines.push(` * Dark Theme \u2014 Semantic color overrides`);
836
+ lines.push(` * \u26A0\uFE0F Auto-generated \u2014 DO NOT EDIT`);
837
+ lines.push(` *`);
838
+ lines.push(` * Supports three dark mode strategies:`);
839
+ lines.push(` * 1. OS auto-detection: follows prefers-color-scheme`);
840
+ lines.push(` * 2. Manual dark: <html data-theme="dark"> or <html class="dark">`);
841
+ lines.push(` * 3. Manual light: <html data-theme="light"> (overrides OS dark)`);
842
+ lines.push(` *`);
843
+ lines.push(` * Usage: @import '@7onic-ui/tokens/css/themes/dark.css';`);
844
+ lines.push(` */`);
845
+ lines.push(``);
846
+ const declLines = [];
847
+ const darkColor = tokens.dark.color;
848
+ const lightColor = tokens.light.color;
849
+ const semanticColorOrder = orderedKeys(lightColor, { type: "known", order: KNOWN_ORDERS.semanticColorCategories }, "dark.color");
850
+ for (const category of semanticColorOrder) {
851
+ const catData = darkColor[category];
852
+ if (!catData) continue;
853
+ const catLabel = category.charAt(0).toUpperCase() + category.slice(1);
854
+ declLines.push(` /* ${catLabel} */`);
855
+ const entries = Object.entries(catData).filter(([k]) => !k.startsWith("$"));
856
+ const sortedEntries = sortByKnownOrder(entries, KNOWN_ORDERS.semanticColorVariants);
857
+ for (const [variant, token] of sortedEntries) {
858
+ const tv = token;
859
+ if (tv.type === "composition" && typeof tv.value === "object" && tv.value !== null) {
860
+ const comp = tv.value;
861
+ declLines.push(` ${semanticColorVar(category, variant)}: ${resolveCompositionToColorMix(comp)};`);
862
+ } else {
863
+ const resolved = typeof tv.value === "string" && tv.value.startsWith("{") ? resolveToVar(tv.value, tokens) : String(tv.value);
864
+ declLines.push(` ${semanticColorVar(category, variant)}: ${resolved};`);
865
+ const resolvedHex = typeof tv.value === "string" && tv.value.startsWith("{") ? resolveReference(tv.value, tokens) : String(tv.value);
866
+ const rgb = hexToRgb(resolvedHex);
867
+ if (rgb) declLines.push(` ${semanticColorVar(category, variant)}-rgb: ${rgb};`);
868
+ }
869
+ }
870
+ declLines.push(``);
871
+ }
872
+ declLines.push(` /* Dark Mode Shadows - increased opacity for visibility */`);
873
+ const darkShadowData = tokens.primitive;
874
+ const darkShadows = darkShadowData.shadow;
875
+ for (const name of orderedKeys(darkShadows, { type: "known", order: KNOWN_ORDERS.shadow }, "dark.shadow")) {
876
+ const token = darkShadows[name];
877
+ if (!token) continue;
878
+ const ext = token.$extensions;
879
+ declLines.push(` --shadow-${name}: ${formatShadowDark(token.value, ext)};`);
880
+ }
881
+ const declarations = declLines.join("\n");
882
+ const mediaDeclarations = declLines.map((l) => l ? ` ${l}` : l).join("\n");
883
+ lines.push(`@media (prefers-color-scheme: dark) {`);
884
+ lines.push(` :root:not([data-theme="light"]) {`);
885
+ lines.push(mediaDeclarations);
886
+ lines.push(` }`);
887
+ lines.push(`}`);
888
+ lines.push(``);
889
+ lines.push(`:root[data-theme="dark"],`);
890
+ lines.push(`:root.dark {`);
891
+ lines.push(declarations);
892
+ lines.push(`}`);
893
+ lines.push(``);
894
+ return lines.join("\n");
895
+ }
896
+ function generateJsTokens(tokens) {
897
+ const p = tokens.primitive;
898
+ const data = {};
899
+ const colors = {};
900
+ const colorData = p.color;
901
+ for (const palette of orderedKeys(colorData, { type: "known", order: KNOWN_ORDERS.colorPalettes }, "js.color")) {
902
+ const paletteData = colorData[palette];
903
+ if (!paletteData) continue;
904
+ if (typeof paletteData === "object" && "value" in paletteData) {
905
+ colors[palette] = paletteData.value;
906
+ } else {
907
+ const shades = paletteData;
908
+ const shadeObj = {};
909
+ for (const shade of orderedKeys(shades, { type: "numeric" }, `js.color.${palette}`)) {
910
+ const token = shades[shade];
911
+ if (token?.value) shadeObj[shade] = String(token.value);
912
+ }
913
+ colors[palette] = shadeObj;
914
+ }
915
+ }
916
+ data.colors = colors;
917
+ const spacing = {};
918
+ const spacingData = p.spacing;
919
+ for (const key of orderedKeys(spacingData, { type: "numeric" }, "js.spacing")) {
920
+ spacing[key] = pxToRem(String(spacingData[key].value));
921
+ }
922
+ data.spacing = spacing;
923
+ const fontSize = {};
924
+ const fontSizeData = p.fontSize;
925
+ for (const name of orderedKeys(fontSizeData, { type: "known", order: KNOWN_ORDERS.fontSize }, "js.fontSize")) {
926
+ const token = fontSizeData[name];
927
+ if (!token) continue;
928
+ const lh = token.$extensions?.lineHeight;
929
+ fontSize[name] = {
930
+ size: pxToRem(String(token.value)),
931
+ lineHeight: pxToRem(String(lh || token.value))
932
+ };
933
+ }
934
+ data.fontSize = fontSize;
935
+ const borderRadius = {};
936
+ const radiusData = p.borderRadius;
937
+ for (const name of orderedKeys(radiusData, { type: "known", order: KNOWN_ORDERS.borderRadius }, "js.borderRadius")) {
938
+ const token = radiusData[name];
939
+ if (!token) continue;
940
+ borderRadius[name] = formatValue(String(token.value), "borderRadius");
941
+ }
942
+ data.borderRadius = borderRadius;
943
+ const shadow = {};
944
+ const shadowData = p.shadow;
945
+ for (const name of orderedKeys(shadowData, { type: "known", order: KNOWN_ORDERS.shadow }, "js.shadow")) {
946
+ const token = shadowData[name];
947
+ if (!token) continue;
948
+ const ext = token.$extensions;
949
+ shadow[name] = formatShadow(token.value, ext);
950
+ }
951
+ data.shadow = shadow;
952
+ const zIndex = {};
953
+ const zData = p.zIndex;
954
+ for (const name of orderedKeys(zData, { type: "known", order: KNOWN_ORDERS.zIndex }, "js.zIndex")) {
955
+ const token = zData[name];
956
+ if (!token) continue;
957
+ zIndex[name] = token.value;
958
+ }
959
+ data.zIndex = zIndex;
960
+ const duration = {};
961
+ const durationData = p.duration;
962
+ for (const name of orderedKeys(durationData, { type: "known", order: KNOWN_ORDERS.duration }, "js.duration")) {
963
+ const token = durationData[name];
964
+ if (!token) continue;
965
+ duration[name] = String(token.value);
966
+ }
967
+ data.duration = duration;
968
+ const iconSize = {};
969
+ const iconSizeData = p.iconSize;
970
+ for (const name of orderedKeys(iconSizeData, { type: "known", order: KNOWN_ORDERS.iconSize }, "js.iconSize")) {
971
+ const token = iconSizeData[name];
972
+ if (!token) continue;
973
+ iconSize[name] = pxToRem(String(token.value));
974
+ }
975
+ data.iconSize = iconSize;
976
+ const opacity = {};
977
+ const opacityData = p.opacity;
978
+ if (opacityData) {
979
+ for (const name of orderedKeys(opacityData, { type: "numeric" }, "js.opacity")) {
980
+ const token = opacityData[name];
981
+ if (!token) continue;
982
+ opacity[name] = String(token.value);
983
+ }
984
+ data.opacity = opacity;
985
+ }
986
+ const fontWeight = {};
987
+ const fontWeightData = p.fontWeight;
988
+ if (fontWeightData) {
989
+ for (const name of Object.keys(fontWeightData)) {
990
+ const token = fontWeightData[name];
991
+ if (!token) continue;
992
+ fontWeight[name] = String(token.value);
993
+ }
994
+ data.fontWeight = fontWeight;
995
+ }
996
+ const borderWidth = {};
997
+ const borderWidthData = p.borderWidth;
998
+ if (borderWidthData) {
999
+ for (const name of orderedKeys(borderWidthData, { type: "numeric" }, "js.borderWidth")) {
1000
+ const token = borderWidthData[name];
1001
+ if (!token) continue;
1002
+ borderWidth[name] = `${token.value}px`;
1003
+ }
1004
+ data.borderWidth = borderWidth;
1005
+ }
1006
+ const scale = {};
1007
+ const scaleData = p.scale;
1008
+ if (scaleData) {
1009
+ for (const name of Object.keys(scaleData)) {
1010
+ const token = scaleData[name];
1011
+ if (!token) continue;
1012
+ scale[name] = String(token.value);
1013
+ }
1014
+ data.scale = scale;
1015
+ }
1016
+ const easing = {};
1017
+ const easingData = p.easing;
1018
+ if (easingData) {
1019
+ for (const name of orderedKeys(easingData, { type: "known", order: KNOWN_ORDERS.easing }, "js.easing")) {
1020
+ const token = easingData[name];
1021
+ if (!token) continue;
1022
+ easing[name] = String(token.value);
1023
+ }
1024
+ data.easing = easing;
1025
+ }
1026
+ const breakpoint = {};
1027
+ const breakpointData = p.breakpoint;
1028
+ if (breakpointData) {
1029
+ for (const name of orderedKeys(breakpointData, { type: "known", order: KNOWN_ORDERS.breakpoint }, "js.breakpoint")) {
1030
+ const token = breakpointData[name];
1031
+ if (!token) continue;
1032
+ breakpoint[name] = `${token.value}px`;
1033
+ }
1034
+ data.breakpoint = breakpoint;
1035
+ }
1036
+ const fontFamily = {};
1037
+ const fontFamilyData = p.fontFamily;
1038
+ if (fontFamilyData) {
1039
+ for (const name of Object.keys(fontFamilyData)) {
1040
+ const token = fontFamilyData[name];
1041
+ if (!token) continue;
1042
+ fontFamily[name] = String(token.value);
1043
+ }
1044
+ data.fontFamily = fontFamily;
1045
+ }
1046
+ const compSizeData = tokens.semantic?.componentSize;
1047
+ if (compSizeData) {
1048
+ const componentSize = {};
1049
+ for (const comp of Object.keys(compSizeData)) {
1050
+ const compData = compSizeData[comp];
1051
+ if (!compData) continue;
1052
+ componentSize[comp] = {};
1053
+ for (const part of Object.keys(compData)) {
1054
+ const partData = compData[part];
1055
+ if (!partData) continue;
1056
+ componentSize[comp][part] = {};
1057
+ for (const size of Object.keys(partData)) {
1058
+ const token = partData[size];
1059
+ if (!token) continue;
1060
+ const val = token.value;
1061
+ if (typeof val === "object" && val !== null) {
1062
+ const obj = val;
1063
+ componentSize[comp][part][size] = {
1064
+ ...obj.width ? { width: `${obj.width}px` } : {},
1065
+ ...obj.height ? { height: `${obj.height}px` } : {}
1066
+ };
1067
+ } else {
1068
+ componentSize[comp][part][size] = `${val}px`;
1069
+ }
1070
+ }
1071
+ }
1072
+ }
1073
+ data.componentSize = componentSize;
1074
+ }
1075
+ const animSem = tokens.semantic?.animation;
1076
+ if (animSem) {
1077
+ const animData = {};
1078
+ for (const [name, entry] of Object.entries(animSem)) {
1079
+ if (name.startsWith("$")) continue;
1080
+ const token = entry;
1081
+ if (token.type !== "composition") continue;
1082
+ const val = token.value;
1083
+ const durationRaw = resolveRef(val.duration, p);
1084
+ const easingRaw = resolveRef(val.easing, p);
1085
+ const obj = {
1086
+ duration: durationRaw.endsWith("ms") ? durationRaw : `${durationRaw}ms`,
1087
+ easing: easingRaw
1088
+ };
1089
+ if (val.opacity) obj.opacity = resolveRef(val.opacity, p);
1090
+ if (val.scale) obj.scale = resolveRef(val.scale, p);
1091
+ if (val.translateY) obj.translateY = resolveRef(val.translateY, p);
1092
+ if (val.heightVar) obj.heightVar = val.heightVar;
1093
+ animData[name] = obj;
1094
+ }
1095
+ data.animation = animData;
1096
+ }
1097
+ const sem = tokens.semantic;
1098
+ const typoData = sem?.typography;
1099
+ if (typoData) {
1100
+ const typography = {};
1101
+ for (const category of Object.keys(typoData)) {
1102
+ const catData = typoData[category];
1103
+ if (!catData) continue;
1104
+ if (catData.type === "typography" && catData.value) {
1105
+ const val = catData.value;
1106
+ typography[category] = {
1107
+ fontFamily: resolveRef(val.fontFamily, p),
1108
+ fontSize: pxToRem(resolveRef(val.fontSize, p)),
1109
+ fontWeight: resolveRef(val.fontWeight, p),
1110
+ lineHeight: pxToRem(val.lineHeight)
1111
+ };
1112
+ } else {
1113
+ const catObj = {};
1114
+ for (const name of Object.keys(catData)) {
1115
+ const entry = catData[name];
1116
+ if (!entry || entry.type !== "typography") continue;
1117
+ const val = entry.value;
1118
+ catObj[name] = {
1119
+ fontFamily: resolveRef(val.fontFamily, p),
1120
+ fontSize: pxToRem(resolveRef(val.fontSize, p)),
1121
+ fontWeight: resolveRef(val.fontWeight, p),
1122
+ lineHeight: pxToRem(val.lineHeight)
1123
+ };
1124
+ }
1125
+ typography[category] = catObj;
1126
+ }
1127
+ }
1128
+ data.typography = typography;
1129
+ }
1130
+ const json = JSON.stringify(data, null, 2);
1131
+ const cjsLines = [];
1132
+ cjsLines.push(`/**`);
1133
+ cjsLines.push(` * 7onic Design Tokens \u2014 JavaScript export`);
1134
+ cjsLines.push(` * \u26A0\uFE0F Auto-generated \u2014 DO NOT EDIT`);
1135
+ cjsLines.push(` */`);
1136
+ cjsLines.push(`'use strict';`);
1137
+ cjsLines.push(``);
1138
+ cjsLines.push(`const tokens = ${json};`);
1139
+ cjsLines.push(``);
1140
+ for (const key of Object.keys(data)) {
1141
+ cjsLines.push(`module.exports.${key} = tokens.${key};`);
1142
+ }
1143
+ cjsLines.push(`module.exports.default = tokens;`);
1144
+ cjsLines.push(``);
1145
+ const esmLines = [];
1146
+ esmLines.push(`/**`);
1147
+ esmLines.push(` * 7onic Design Tokens \u2014 JavaScript export (ESM)`);
1148
+ esmLines.push(` * \u26A0\uFE0F Auto-generated \u2014 DO NOT EDIT`);
1149
+ esmLines.push(` */`);
1150
+ esmLines.push(``);
1151
+ esmLines.push(`const tokens = ${json};`);
1152
+ esmLines.push(``);
1153
+ for (const key of Object.keys(data)) {
1154
+ esmLines.push(`export const ${key} = tokens.${key};`);
1155
+ }
1156
+ esmLines.push(`export default tokens;`);
1157
+ esmLines.push(``);
1158
+ return { cjs: cjsLines.join("\n"), esm: esmLines.join("\n") };
1159
+ }
1160
+ function generateTypeDefinitions(tokens) {
1161
+ const p = tokens.primitive;
1162
+ const lines = [];
1163
+ lines.push(`/**`);
1164
+ lines.push(` * 7onic Design Tokens \u2014 TypeScript type definitions`);
1165
+ lines.push(` * \u26A0\uFE0F Auto-generated \u2014 DO NOT EDIT`);
1166
+ lines.push(` */`);
1167
+ lines.push(``);
1168
+ lines.push(`type Shade = '50' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';`);
1169
+ lines.push(`type ShadeRecord = Record<Shade, string>;`);
1170
+ lines.push(``);
1171
+ const colorData = p.color;
1172
+ const colorPalettes = orderedKeys(colorData, { type: "known", order: KNOWN_ORDERS.colorPalettes }, "types.color");
1173
+ lines.push(`export declare const colors: {`);
1174
+ for (const palette of colorPalettes) {
1175
+ const paletteData = colorData[palette];
1176
+ if (!paletteData) continue;
1177
+ if (typeof paletteData === "object" && "value" in paletteData) {
1178
+ lines.push(` ${palette}: string;`);
1179
+ } else {
1180
+ lines.push(` ${palette}: ShadeRecord;`);
1181
+ }
1182
+ }
1183
+ lines.push(`};`);
1184
+ lines.push(``);
1185
+ const spacingData = p.spacing;
1186
+ const spacingKeys = orderedKeys(spacingData, { type: "numeric" }, "types.spacing");
1187
+ lines.push(`export declare const spacing: Record<${spacingKeys.map((k) => `'${k}'`).join(" | ")}, string>;`);
1188
+ lines.push(``);
1189
+ const fontSizeData = p.fontSize;
1190
+ const fsKeys = orderedKeys(fontSizeData, { type: "known", order: KNOWN_ORDERS.fontSize }, "types.fontSize");
1191
+ lines.push(`export declare const fontSize: Record<${fsKeys.map((k) => `'${k}'`).join(" | ")}, { size: string; lineHeight: string }>;`);
1192
+ lines.push(``);
1193
+ const radiusData = p.borderRadius;
1194
+ const rKeys = orderedKeys(radiusData, { type: "known", order: KNOWN_ORDERS.borderRadius }, "types.borderRadius");
1195
+ lines.push(`export declare const borderRadius: Record<${rKeys.map((k) => `'${k}'`).join(" | ")}, string>;`);
1196
+ lines.push(``);
1197
+ lines.push(`export declare const shadow: Record<${KNOWN_ORDERS.shadow.map((k) => `'${k}'`).join(" | ")}, string>;`);
1198
+ lines.push(``);
1199
+ const zData = p.zIndex;
1200
+ const zKeys = orderedKeys(zData, { type: "known", order: KNOWN_ORDERS.zIndex }, "types.zIndex");
1201
+ lines.push(`export declare const zIndex: Record<${zKeys.map((k) => `'${k}'`).join(" | ")}, string | number>;`);
1202
+ lines.push(``);
1203
+ lines.push(`export declare const duration: Record<${KNOWN_ORDERS.duration.map((k) => `'${k}'`).join(" | ")}, string>;`);
1204
+ lines.push(``);
1205
+ lines.push(`export declare const iconSize: Record<${KNOWN_ORDERS.iconSize.map((k) => `'${k}'`).join(" | ")}, string>;`);
1206
+ lines.push(``);
1207
+ const opacityData = p.opacity;
1208
+ if (opacityData) {
1209
+ const opKeys = orderedKeys(opacityData, { type: "numeric" }, "types.opacity");
1210
+ lines.push(`export declare const opacity: Record<${opKeys.map((k) => `'${k}'`).join(" | ")}, string>;`);
1211
+ lines.push(``);
1212
+ }
1213
+ const fontWeightData = p.fontWeight;
1214
+ if (fontWeightData) {
1215
+ const fwKeys = Object.keys(fontWeightData);
1216
+ lines.push(`export declare const fontWeight: Record<${fwKeys.map((k) => `'${k}'`).join(" | ")}, string>;`);
1217
+ lines.push(``);
1218
+ }
1219
+ const borderWidthData = p.borderWidth;
1220
+ if (borderWidthData) {
1221
+ const bwKeys = orderedKeys(borderWidthData, { type: "numeric" }, "types.borderWidth");
1222
+ lines.push(`export declare const borderWidth: Record<${bwKeys.map((k) => `'${k}'`).join(" | ")}, string>;`);
1223
+ lines.push(``);
1224
+ }
1225
+ const scaleData = p.scale;
1226
+ if (scaleData) {
1227
+ const scKeys = Object.keys(scaleData);
1228
+ lines.push(`export declare const scale: Record<${scKeys.map((k) => `'${k}'`).join(" | ")}, string>;`);
1229
+ lines.push(``);
1230
+ }
1231
+ const easingDataT = p.easing;
1232
+ if (easingDataT) {
1233
+ const eKeys = orderedKeys(easingDataT, { type: "known", order: KNOWN_ORDERS.easing }, "types.easing");
1234
+ lines.push(`export declare const easing: Record<${eKeys.map((k) => `'${k}'`).join(" | ")}, string>;`);
1235
+ lines.push(``);
1236
+ }
1237
+ const breakpointDataT = p.breakpoint;
1238
+ if (breakpointDataT) {
1239
+ const bpKeys = orderedKeys(breakpointDataT, { type: "known", order: KNOWN_ORDERS.breakpoint }, "types.breakpoint");
1240
+ lines.push(`export declare const breakpoint: Record<${bpKeys.map((k) => `'${k}'`).join(" | ")}, string>;`);
1241
+ lines.push(``);
1242
+ }
1243
+ const fontFamilyData = p.fontFamily;
1244
+ if (fontFamilyData) {
1245
+ const ffKeys = Object.keys(fontFamilyData);
1246
+ lines.push(`export declare const fontFamily: Record<${ffKeys.map((k) => `'${k}'`).join(" | ")}, string>;`);
1247
+ lines.push(``);
1248
+ }
1249
+ const compSizeDataT = tokens.semantic?.componentSize;
1250
+ if (compSizeDataT) {
1251
+ lines.push(`export declare const componentSize: {`);
1252
+ for (const comp of Object.keys(compSizeDataT)) {
1253
+ const compData = compSizeDataT[comp];
1254
+ if (!compData) continue;
1255
+ lines.push(` ${comp}: {`);
1256
+ for (const part of Object.keys(compData)) {
1257
+ const partData = compData[part];
1258
+ if (!partData) continue;
1259
+ lines.push(` ${part}: {`);
1260
+ for (const size of Object.keys(partData)) {
1261
+ const token = partData[size];
1262
+ if (!token) continue;
1263
+ const val = token.value;
1264
+ if (typeof val === "object" && val !== null) {
1265
+ lines.push(` ${size}: { width: string; height: string };`);
1266
+ } else {
1267
+ lines.push(` ${size}: string;`);
1268
+ }
1269
+ }
1270
+ lines.push(` };`);
1271
+ }
1272
+ lines.push(` };`);
1273
+ }
1274
+ lines.push(`};`);
1275
+ lines.push(``);
1276
+ }
1277
+ const animSemT = tokens.semantic?.animation;
1278
+ if (animSemT) {
1279
+ lines.push(`export declare const animation: {`);
1280
+ for (const [name, entry] of Object.entries(animSemT)) {
1281
+ if (name.startsWith("$")) continue;
1282
+ const token = entry;
1283
+ if (token.type !== "composition") continue;
1284
+ const val = token.value;
1285
+ const fields = ["duration: string", "easing: string"];
1286
+ if (val.opacity) fields.unshift("opacity: string");
1287
+ if (val.scale) fields.unshift("scale: string");
1288
+ if (val.translateY) fields.unshift("translateY: string");
1289
+ if (val.heightVar) fields.unshift("heightVar: string");
1290
+ lines.push(` '${name}': { ${fields.join("; ")} };`);
1291
+ }
1292
+ lines.push(`};`);
1293
+ lines.push(``);
1294
+ }
1295
+ const semT = tokens.semantic;
1296
+ const typoDataT = semT?.typography;
1297
+ if (typoDataT) {
1298
+ const typoType = `{ fontFamily: string; fontSize: string; fontWeight: string; lineHeight: string }`;
1299
+ lines.push(`export declare const typography: {`);
1300
+ for (const category of Object.keys(typoDataT)) {
1301
+ const catData = typoDataT[category];
1302
+ if (!catData) continue;
1303
+ if (catData.type === "typography") {
1304
+ lines.push(` ${category}: ${typoType};`);
1305
+ } else {
1306
+ const childKeys = Object.keys(catData).filter((k) => {
1307
+ const entry = catData[k];
1308
+ return entry?.type === "typography";
1309
+ });
1310
+ lines.push(` ${category}: {`);
1311
+ for (const name of childKeys) {
1312
+ lines.push(` '${name}': ${typoType};`);
1313
+ }
1314
+ lines.push(` };`);
1315
+ }
1316
+ }
1317
+ lines.push(`};`);
1318
+ lines.push(``);
1319
+ }
1320
+ lines.push(`// Named type aliases for convenience`);
1321
+ lines.push(`export type Colors = typeof colors;`);
1322
+ lines.push(`export type Spacing = typeof spacing;`);
1323
+ lines.push(`export type FontSize = typeof fontSize;`);
1324
+ lines.push(`export type BorderRadius = typeof borderRadius;`);
1325
+ lines.push(`export type Shadow = typeof shadow;`);
1326
+ lines.push(`export type ZIndex = typeof zIndex;`);
1327
+ lines.push(`export type Duration = typeof duration;`);
1328
+ lines.push(`export type IconSize = typeof iconSize;`);
1329
+ lines.push(`export type Opacity = typeof opacity;`);
1330
+ lines.push(`export type FontWeight = typeof fontWeight;`);
1331
+ lines.push(`export type BorderWidth = typeof borderWidth;`);
1332
+ lines.push(`export type Scale = typeof scale;`);
1333
+ lines.push(`export type Easing = typeof easing;`);
1334
+ lines.push(`export type Breakpoint = typeof breakpoint;`);
1335
+ lines.push(`export type FontFamily = typeof fontFamily;`);
1336
+ lines.push(`export type ComponentSize = typeof componentSize;`);
1337
+ lines.push(`export type Animation = typeof animation;`);
1338
+ lines.push(`export type Typography = typeof typography;`);
1339
+ lines.push(``);
1340
+ lines.push(`declare const tokens: {`);
1341
+ lines.push(` colors: typeof colors;`);
1342
+ lines.push(` spacing: typeof spacing;`);
1343
+ lines.push(` fontSize: typeof fontSize;`);
1344
+ lines.push(` borderRadius: typeof borderRadius;`);
1345
+ lines.push(` shadow: typeof shadow;`);
1346
+ lines.push(` zIndex: typeof zIndex;`);
1347
+ lines.push(` duration: typeof duration;`);
1348
+ lines.push(` iconSize: typeof iconSize;`);
1349
+ lines.push(` opacity: typeof opacity;`);
1350
+ lines.push(` fontWeight: typeof fontWeight;`);
1351
+ lines.push(` borderWidth: typeof borderWidth;`);
1352
+ lines.push(` scale: typeof scale;`);
1353
+ lines.push(` easing: typeof easing;`);
1354
+ lines.push(` breakpoint: typeof breakpoint;`);
1355
+ lines.push(` fontFamily: typeof fontFamily;`);
1356
+ lines.push(` componentSize: typeof componentSize;`);
1357
+ lines.push(` animation: typeof animation;`);
1358
+ lines.push(` typography: typeof typography;`);
1359
+ lines.push(`};`);
1360
+ lines.push(`export default tokens;`);
1361
+ lines.push(``);
1362
+ return lines.join("\n");
1363
+ }
1364
+ function generateNormalizedJson(tokens) {
1365
+ const p = tokens.primitive;
1366
+ const result = {};
1367
+ const colorFlat = {};
1368
+ const colorData = p.color;
1369
+ for (const palette of orderedKeys(colorData, { type: "known", order: KNOWN_ORDERS.colorPalettes }, "json.color")) {
1370
+ const paletteData = colorData[palette];
1371
+ if (!paletteData) continue;
1372
+ if (typeof paletteData === "object" && "value" in paletteData) {
1373
+ colorFlat[palette] = String(paletteData.value);
1374
+ } else {
1375
+ const shades = paletteData;
1376
+ for (const shade of orderedKeys(shades, { type: "numeric" }, `json.color.${palette}`)) {
1377
+ const token = shades[shade];
1378
+ if (token?.value) colorFlat[`${palette}-${shade}`] = String(token.value);
1379
+ }
1380
+ }
1381
+ }
1382
+ result.color = colorFlat;
1383
+ const spacingFlat = {};
1384
+ const spacingData = p.spacing;
1385
+ for (const key of orderedKeys(spacingData, { type: "numeric" }, "json.spacing")) {
1386
+ spacingFlat[key] = pxToRem(String(spacingData[key].value));
1387
+ }
1388
+ result.spacing = spacingFlat;
1389
+ const fontSizeFlat = {};
1390
+ const fontSizeData = p.fontSize;
1391
+ for (const name of orderedKeys(fontSizeData, { type: "known", order: KNOWN_ORDERS.fontSize }, "json.fontSize")) {
1392
+ const token = fontSizeData[name];
1393
+ if (!token) continue;
1394
+ fontSizeFlat[name] = pxToRem(String(token.value));
1395
+ }
1396
+ result.fontSize = fontSizeFlat;
1397
+ const lineHeightFlat = {};
1398
+ for (const name of orderedKeys(fontSizeData, { type: "known", order: KNOWN_ORDERS.fontSize }, "json.lineHeight")) {
1399
+ const token = fontSizeData[name];
1400
+ if (!token) continue;
1401
+ const lh = token.$extensions?.lineHeight;
1402
+ if (lh) lineHeightFlat[name] = pxToRem(lh);
1403
+ }
1404
+ result.lineHeight = lineHeightFlat;
1405
+ const radiusFlat = {};
1406
+ const radiusData = p.borderRadius;
1407
+ for (const name of orderedKeys(radiusData, { type: "known", order: KNOWN_ORDERS.borderRadius }, "json.borderRadius")) {
1408
+ const token = radiusData[name];
1409
+ if (!token) continue;
1410
+ radiusFlat[name] = formatValue(String(token.value), "borderRadius");
1411
+ }
1412
+ result.borderRadius = radiusFlat;
1413
+ const shadowFlat = {};
1414
+ const shadowData = p.shadow;
1415
+ for (const name of orderedKeys(shadowData, { type: "known", order: KNOWN_ORDERS.shadow }, "json.shadow")) {
1416
+ const token = shadowData[name];
1417
+ if (!token) continue;
1418
+ const ext = token.$extensions;
1419
+ shadowFlat[name] = formatShadow(token.value, ext);
1420
+ }
1421
+ result.shadow = shadowFlat;
1422
+ const iconSizeFlat = {};
1423
+ const iconSizeData = p.iconSize;
1424
+ for (const name of orderedKeys(iconSizeData, { type: "known", order: KNOWN_ORDERS.iconSize }, "json.iconSize")) {
1425
+ const token = iconSizeData[name];
1426
+ if (!token) continue;
1427
+ iconSizeFlat[name] = pxToRem(String(token.value));
1428
+ }
1429
+ result.iconSize = iconSizeFlat;
1430
+ const zFlat = {};
1431
+ const zData = p.zIndex;
1432
+ for (const name of orderedKeys(zData, { type: "known", order: KNOWN_ORDERS.zIndex }, "json.zIndex")) {
1433
+ const token = zData[name];
1434
+ if (!token) continue;
1435
+ zFlat[name] = token.value;
1436
+ }
1437
+ result.zIndex = zFlat;
1438
+ const durationFlat = {};
1439
+ const durationData = p.duration;
1440
+ for (const name of orderedKeys(durationData, { type: "known", order: KNOWN_ORDERS.duration }, "json.duration")) {
1441
+ const token = durationData[name];
1442
+ if (!token) continue;
1443
+ durationFlat[name] = String(token.value);
1444
+ }
1445
+ result.duration = durationFlat;
1446
+ const opacityFlat = {};
1447
+ const opacityData = p.opacity;
1448
+ if (opacityData) {
1449
+ for (const name of orderedKeys(opacityData, { type: "numeric" }, "json.opacity")) {
1450
+ const token = opacityData[name];
1451
+ if (!token) continue;
1452
+ opacityFlat[name] = String(token.value);
1453
+ }
1454
+ result.opacity = opacityFlat;
1455
+ }
1456
+ const fontWeightFlat = {};
1457
+ const fontWeightData = p.fontWeight;
1458
+ if (fontWeightData) {
1459
+ for (const name of Object.keys(fontWeightData)) {
1460
+ const token = fontWeightData[name];
1461
+ if (!token) continue;
1462
+ fontWeightFlat[name] = String(token.value);
1463
+ }
1464
+ result.fontWeight = fontWeightFlat;
1465
+ }
1466
+ const borderWidthFlat = {};
1467
+ const borderWidthData = p.borderWidth;
1468
+ if (borderWidthData) {
1469
+ for (const name of orderedKeys(borderWidthData, { type: "numeric" }, "json.borderWidth")) {
1470
+ const token = borderWidthData[name];
1471
+ if (!token) continue;
1472
+ borderWidthFlat[name] = `${token.value}px`;
1473
+ }
1474
+ result.borderWidth = borderWidthFlat;
1475
+ }
1476
+ const scaleFlat = {};
1477
+ const scaleData = p.scale;
1478
+ if (scaleData) {
1479
+ for (const name of Object.keys(scaleData)) {
1480
+ const token = scaleData[name];
1481
+ if (!token) continue;
1482
+ scaleFlat[name] = String(token.value);
1483
+ }
1484
+ result.scale = scaleFlat;
1485
+ }
1486
+ const easingFlat = {};
1487
+ const easingData = p.easing;
1488
+ if (easingData) {
1489
+ for (const name of orderedKeys(easingData, { type: "known", order: KNOWN_ORDERS.easing }, "json.easing")) {
1490
+ const token = easingData[name];
1491
+ if (!token) continue;
1492
+ easingFlat[name] = String(token.value);
1493
+ }
1494
+ result.easing = easingFlat;
1495
+ }
1496
+ const breakpointFlat = {};
1497
+ const breakpointData = p.breakpoint;
1498
+ if (breakpointData) {
1499
+ for (const name of orderedKeys(breakpointData, { type: "known", order: KNOWN_ORDERS.breakpoint }, "json.breakpoint")) {
1500
+ const token = breakpointData[name];
1501
+ if (!token) continue;
1502
+ breakpointFlat[name] = `${token.value}px`;
1503
+ }
1504
+ result.breakpoint = breakpointFlat;
1505
+ }
1506
+ const fontFamilyFlat = {};
1507
+ const fontFamilyData = p.fontFamily;
1508
+ if (fontFamilyData) {
1509
+ for (const name of Object.keys(fontFamilyData)) {
1510
+ const token = fontFamilyData[name];
1511
+ if (!token) continue;
1512
+ fontFamilyFlat[name] = String(token.value);
1513
+ }
1514
+ result.fontFamily = fontFamilyFlat;
1515
+ }
1516
+ const compSizeData = tokens.semantic?.componentSize;
1517
+ if (compSizeData) {
1518
+ const compFlat = {};
1519
+ for (const comp of Object.keys(compSizeData)) {
1520
+ const compData = compSizeData[comp];
1521
+ if (!compData) continue;
1522
+ for (const part of Object.keys(compData)) {
1523
+ const partData = compData[part];
1524
+ if (!partData) continue;
1525
+ for (const size of Object.keys(partData)) {
1526
+ const token = partData[size];
1527
+ if (!token) continue;
1528
+ const val = token.value;
1529
+ if (typeof val === "object" && val !== null) {
1530
+ const obj = val;
1531
+ if (obj.width) compFlat[`${comp}-${part}-${size}-width`] = `${obj.width}px`;
1532
+ if (obj.height) compFlat[`${comp}-${part}-${size}-height`] = `${obj.height}px`;
1533
+ } else {
1534
+ compFlat[`${comp}-${part}-${size}`] = `${val}px`;
1535
+ }
1536
+ }
1537
+ }
1538
+ }
1539
+ result.componentSize = compFlat;
1540
+ }
1541
+ const animSemJ = tokens.semantic?.animation;
1542
+ if (animSemJ) {
1543
+ const animFlat = {};
1544
+ for (const [name, entry] of Object.entries(animSemJ)) {
1545
+ if (name.startsWith("$")) continue;
1546
+ const token = entry;
1547
+ if (token.type !== "composition") continue;
1548
+ const val = token.value;
1549
+ const durationRaw = resolveRef(val.duration, p);
1550
+ const easingRaw = resolveRef(val.easing, p);
1551
+ animFlat[`${name}-duration`] = durationRaw.endsWith("ms") ? durationRaw : `${durationRaw}ms`;
1552
+ animFlat[`${name}-easing`] = easingRaw;
1553
+ if (val.opacity) animFlat[`${name}-opacity`] = resolveRef(val.opacity, p);
1554
+ if (val.scale) animFlat[`${name}-scale`] = resolveRef(val.scale, p);
1555
+ if (val.translateY) animFlat[`${name}-translateY`] = resolveRef(val.translateY, p);
1556
+ if (val.heightVar) animFlat[`${name}-heightVar`] = val.heightVar;
1557
+ }
1558
+ result.animation = animFlat;
1559
+ }
1560
+ return JSON.stringify(result, null, 2) + "\n";
1561
+ }
1562
+ function generateV3Preset(tokens) {
1563
+ const p = tokens.primitive;
1564
+ const lines = [];
1565
+ lines.push(`/**`);
1566
+ lines.push(` * Design System - Tailwind CSS v3 Preset`);
1567
+ lines.push(` * \u26A0\uFE0F Auto-generated from figma-tokens.json \u2014 DO NOT EDIT`);
1568
+ lines.push(` *`);
1569
+ lines.push(` * Non-color values reference CSS variables from variables.css for auto-sync.`);
1570
+ lines.push(` * Primitive colors use HEX for Tailwind v3 opacity modifier support (bg-white/10, etc.).`);
1571
+ lines.push(` * Semantic colors use rgb() with RGB channel variables for opacity modifier support (bg-primary/50, etc.).`);
1572
+ lines.push(` *`);
1573
+ lines.push(` * Usage:`);
1574
+ lines.push(` * \`\`\`js`);
1575
+ lines.push(` * // tailwind.config.js`);
1576
+ lines.push(` * module.exports = {`);
1577
+ lines.push(` * presets: [require('@7onic-ui/react/tailwind-preset')],`);
1578
+ lines.push(` * }`);
1579
+ lines.push(` * \`\`\``);
1580
+ lines.push(` */`);
1581
+ lines.push(``);
1582
+ lines.push(`/** @type {import('tailwindcss').Config} */`);
1583
+ lines.push(`module.exports = {`);
1584
+ lines.push(` theme: {`);
1585
+ lines.push(` extend: {`);
1586
+ const colorData = p.color;
1587
+ lines.push(` colors: {`);
1588
+ lines.push(` // Standalone primitive colors (HEX for opacity modifier support: bg-white/10, etc.)`);
1589
+ const whiteToken = colorData.white;
1590
+ const blackToken = colorData.black;
1591
+ lines.push(` white: '${whiteToken?.value || "#FFFFFF"}',`);
1592
+ lines.push(` black: '${blackToken?.value || "#000000"}',`);
1593
+ lines.push(``);
1594
+ const rgbAlpha = (varName) => `'rgb(var(${varName}-rgb) / <alpha-value>)'`;
1595
+ lines.push(` // Semantic colors (rgb() with alpha-value for opacity modifier support)`);
1596
+ lines.push(` background: {`);
1597
+ lines.push(` DEFAULT: ${rgbAlpha("--color-background")},`);
1598
+ lines.push(` paper: ${rgbAlpha("--color-background-paper")},`);
1599
+ lines.push(` elevated: ${rgbAlpha("--color-background-elevated")},`);
1600
+ lines.push(` muted: ${rgbAlpha("--color-background-muted")},`);
1601
+ lines.push(` },`);
1602
+ lines.push(` foreground: ${rgbAlpha("--color-text")},`);
1603
+ lines.push(``);
1604
+ const brandColors = ["primary", "secondary"];
1605
+ for (const color of brandColors) {
1606
+ lines.push(` ${color}: {`);
1607
+ lines.push(` DEFAULT: ${rgbAlpha(`--color-${color}`)},`);
1608
+ lines.push(` hover: ${rgbAlpha(`--color-${color}-hover`)},`);
1609
+ lines.push(` active: ${rgbAlpha(`--color-${color}-active`)},`);
1610
+ lines.push(` tint: ${rgbAlpha(`--color-${color}-tint`)},`);
1611
+ lines.push(` foreground: ${rgbAlpha(`--color-${color}-text`)},`);
1612
+ const palette = colorData[color];
1613
+ if (palette) {
1614
+ for (const shade of orderedKeys(palette, { type: "known", order: KNOWN_ORDERS.shadeOrder }, `primitive.color.${color}`)) {
1615
+ const token = palette[shade];
1616
+ if (token?.value) {
1617
+ lines.push(` '${shade}': '${token.value}',`);
1618
+ }
1619
+ }
1620
+ }
1621
+ lines.push(` },`);
1622
+ }
1623
+ lines.push(``);
1624
+ lines.push(` // Status colors`);
1625
+ const semanticColors = ["success", "warning", "error", "info"];
1626
+ for (const color of semanticColors) {
1627
+ lines.push(` ${color}: {`);
1628
+ lines.push(` DEFAULT: ${rgbAlpha(`--color-${color}`)},`);
1629
+ lines.push(` hover: ${rgbAlpha(`--color-${color}-hover`)},`);
1630
+ lines.push(` active: ${rgbAlpha(`--color-${color}-active`)},`);
1631
+ lines.push(` tint: ${rgbAlpha(`--color-${color}-tint`)},`);
1632
+ lines.push(` foreground: ${rgbAlpha(`--color-${color}-text`)},`);
1633
+ if (color === "error") {
1634
+ lines.push(` bg: ${rgbAlpha("--color-error-bg")},`);
1635
+ }
1636
+ lines.push(` },`);
1637
+ }
1638
+ lines.push(``);
1639
+ lines.push(` // Primitive color palettes (HEX for opacity modifier support)`);
1640
+ const palettesOnly = ["gray", "blue", "green", "yellow", "red", "violet", "rose"];
1641
+ for (const palName of palettesOnly) {
1642
+ const palette = colorData[palName];
1643
+ if (!palette) continue;
1644
+ lines.push(` ${palName}: {`);
1645
+ for (const shade of orderedKeys(palette, { type: "known", order: KNOWN_ORDERS.shadeOrder }, `primitive.color.${palName}`)) {
1646
+ const token = palette[shade];
1647
+ if (token?.value) {
1648
+ lines.push(` '${shade}': '${token.value}',`);
1649
+ }
1650
+ }
1651
+ lines.push(` },`);
1652
+ }
1653
+ lines.push(``);
1654
+ lines.push(` // UI colors`);
1655
+ lines.push(` border: {`);
1656
+ lines.push(` DEFAULT: ${rgbAlpha("--color-border")},`);
1657
+ lines.push(` strong: ${rgbAlpha("--color-border-strong")},`);
1658
+ lines.push(` subtle: ${rgbAlpha("--color-border-subtle")},`);
1659
+ lines.push(` },`);
1660
+ lines.push(` ring: {`);
1661
+ lines.push(` DEFAULT: ${rgbAlpha("--color-focus-ring")},`);
1662
+ lines.push(` error: ${rgbAlpha("--color-focus-ring-error")},`);
1663
+ lines.push(` },`);
1664
+ lines.push(` muted: {`);
1665
+ lines.push(` DEFAULT: ${rgbAlpha("--color-background-muted")},`);
1666
+ lines.push(` foreground: ${rgbAlpha("--color-text-muted")},`);
1667
+ lines.push(` },`);
1668
+ lines.push(` disabled: {`);
1669
+ lines.push(` DEFAULT: ${rgbAlpha("--color-disabled")},`);
1670
+ lines.push(` foreground: ${rgbAlpha("--color-disabled-text")},`);
1671
+ lines.push(` },`);
1672
+ lines.push(``);
1673
+ lines.push(` // Chart colors`);
1674
+ lines.push(` chart: {`);
1675
+ lines.push(` '1': ${rgbAlpha("--color-chart-1")},`);
1676
+ lines.push(` '2': ${rgbAlpha("--color-chart-2")},`);
1677
+ lines.push(` '3': ${rgbAlpha("--color-chart-3")},`);
1678
+ lines.push(` '4': ${rgbAlpha("--color-chart-4")},`);
1679
+ lines.push(` '5': ${rgbAlpha("--color-chart-5")},`);
1680
+ lines.push(` },`);
1681
+ lines.push(``);
1682
+ lines.push(` // Text`);
1683
+ lines.push(` text: {`);
1684
+ lines.push(` DEFAULT: ${rgbAlpha("--color-text")},`);
1685
+ lines.push(` muted: ${rgbAlpha("--color-text-muted")},`);
1686
+ lines.push(` subtle: ${rgbAlpha("--color-text-subtle")},`);
1687
+ lines.push(` link: ${rgbAlpha("--color-text-link")},`);
1688
+ lines.push(` primary: ${rgbAlpha("--color-text-primary")},`);
1689
+ lines.push(` info: ${rgbAlpha("--color-text-info")},`);
1690
+ lines.push(` success: ${rgbAlpha("--color-text-success")},`);
1691
+ lines.push(` error: ${rgbAlpha("--color-text-error")},`);
1692
+ lines.push(` warning: ${rgbAlpha("--color-text-warning")},`);
1693
+ lines.push(` },`);
1694
+ lines.push(` },`);
1695
+ lines.push(``);
1696
+ const fontSizeData = p.fontSize;
1697
+ lines.push(` fontSize: {`);
1698
+ for (const name of orderedKeys(fontSizeData, { type: "known", order: KNOWN_ORDERS.fontSize }, "primitive.fontSize")) {
1699
+ const token = fontSizeData[name];
1700
+ if (!token) continue;
1701
+ lines.push(` '${name}': ['var(--font-size-${name})', { lineHeight: 'var(--line-height-${name})' }],`);
1702
+ }
1703
+ lines.push(` },`);
1704
+ lines.push(``);
1705
+ const fontFamilyData = p.fontFamily;
1706
+ if (fontFamilyData) {
1707
+ lines.push(` fontFamily: {`);
1708
+ for (const name of Object.keys(fontFamilyData).filter((k) => !k.startsWith("$"))) {
1709
+ const token = fontFamilyData[name];
1710
+ if (!token?.value) continue;
1711
+ lines.push(` '${name}': ['var(--font-family-${name})'],`);
1712
+ }
1713
+ lines.push(` },`);
1714
+ lines.push(``);
1715
+ }
1716
+ const spacingData = p.spacing;
1717
+ lines.push(` spacing: {`);
1718
+ for (const key of orderedKeys(spacingData, { type: "numeric" }, "primitive.spacing")) {
1719
+ const cssKey = key.replace(".", "-");
1720
+ lines.push(` '${key}': 'var(--spacing-${cssKey})',`);
1721
+ }
1722
+ lines.push(` },`);
1723
+ lines.push(``);
1724
+ const radiusData = p.borderRadius;
1725
+ lines.push(` borderRadius: {`);
1726
+ const radiusKeys = orderedKeys(radiusData, { type: "known", order: KNOWN_ORDERS.borderRadius }, "primitive.borderRadius");
1727
+ for (const name of radiusKeys) {
1728
+ const token = radiusData[name];
1729
+ if (!token) continue;
1730
+ lines.push(` '${name}': 'var(--radius-${name})',`);
1731
+ if (name === "base") {
1732
+ lines.push(` 'DEFAULT': 'var(--radius-base)',`);
1733
+ }
1734
+ }
1735
+ lines.push(` },`);
1736
+ lines.push(``);
1737
+ lines.push(` boxShadow: {`);
1738
+ for (const name of orderedKeys(p.shadow, { type: "known", order: KNOWN_ORDERS.shadow }, "primitive.shadow")) {
1739
+ lines.push(` '${name}': 'var(--shadow-${name})',`);
1740
+ }
1741
+ lines.push(` },`);
1742
+ lines.push(``);
1743
+ const zIndexData = p.zIndex;
1744
+ lines.push(` zIndex: {`);
1745
+ for (const name of orderedKeys(zIndexData, { type: "known", order: KNOWN_ORDERS.zIndex }, "primitive.zIndex")) {
1746
+ const token = zIndexData[name];
1747
+ if (!token) continue;
1748
+ lines.push(` '${name}': 'var(--z-index-${name})',`);
1749
+ }
1750
+ lines.push(` },`);
1751
+ lines.push(``);
1752
+ const durationData = p.duration;
1753
+ lines.push(` transitionDuration: {`);
1754
+ for (const name of orderedKeys(durationData, { type: "known", order: KNOWN_ORDERS.duration }, "primitive.duration")) {
1755
+ const token = durationData[name];
1756
+ if (!token) continue;
1757
+ lines.push(` '${name}': 'var(--duration-${name})',`);
1758
+ }
1759
+ lines.push(` },`);
1760
+ lines.push(``);
1761
+ const easingData = p.easing;
1762
+ lines.push(` transitionTimingFunction: {`);
1763
+ for (const name of orderedKeys(easingData, { type: "known", order: KNOWN_ORDERS.easing }, "primitive.easing")) {
1764
+ const token = easingData[name];
1765
+ if (!token) continue;
1766
+ lines.push(` '${camelToKebab(name)}': 'var(--easing-${camelToKebab(name)})',`);
1767
+ }
1768
+ lines.push(` },`);
1769
+ lines.push(``);
1770
+ const v3OpacityData = p.opacity;
1771
+ if (v3OpacityData) {
1772
+ lines.push(` opacity: {`);
1773
+ for (const name of orderedKeys(v3OpacityData, { type: "numeric" }, "v3.opacity")) {
1774
+ const token = v3OpacityData[name];
1775
+ if (!token) continue;
1776
+ lines.push(` '${name}': 'var(--opacity-${name})',`);
1777
+ }
1778
+ lines.push(` },`);
1779
+ lines.push(``);
1780
+ }
1781
+ const scaleData = p.scale;
1782
+ lines.push(` scale: {`);
1783
+ for (const [name, token] of Object.entries(scaleData)) {
1784
+ if (name.startsWith("$")) continue;
1785
+ lines.push(` '${name}': 'var(--scale-${name})',`);
1786
+ }
1787
+ lines.push(` },`);
1788
+ lines.push(``);
1789
+ const v3Anim = readAnimationTokens(tokens);
1790
+ lines.push(` keyframes: {`);
1791
+ if (v3Anim) {
1792
+ for (const a of v3Anim) {
1793
+ if (a.type === "height-expand" || a.type === "height-collapse") {
1794
+ const isExpand = a.type === "height-expand";
1795
+ lines.push(` '${a.name}': {`);
1796
+ lines.push(` from: { height: '${isExpand ? "0" : `var(${a.heightVar})`}' },`);
1797
+ lines.push(` to: { height: '${isExpand ? `var(${a.heightVar})` : "0"}' },`);
1798
+ lines.push(` },`);
1799
+ } else {
1800
+ const isEnter = a.type === "enter";
1801
+ const fromProps = [];
1802
+ const toProps = [];
1803
+ if (a.opacity) {
1804
+ fromProps.push(`'opacity': '${isEnter ? a.opacity : "1"}'`);
1805
+ toProps.push(`'opacity': '${isEnter ? "1" : a.opacity}'`);
1806
+ }
1807
+ const fromT = [], toT = [];
1808
+ if (a.scale) {
1809
+ fromT.push(isEnter ? `scale(${a.scale})` : "scale(1)");
1810
+ toT.push(isEnter ? "scale(1)" : `scale(${a.scale})`);
1811
+ }
1812
+ if (a.translateY) {
1813
+ const px = `${a.translateYNegative ? "-" : ""}${a.translateY}px`;
1814
+ fromT.push(isEnter ? `translateY(${px})` : "translateY(0)");
1815
+ toT.push(isEnter ? "translateY(0)" : `translateY(${px})`);
1816
+ }
1817
+ if (fromT.length) {
1818
+ fromProps.push(`'transform': '${fromT.join(" ")}'`);
1819
+ toProps.push(`'transform': '${toT.join(" ")}'`);
1820
+ }
1821
+ lines.push(` '${a.name}': {`);
1822
+ lines.push(` from: { ${fromProps.join(", ")} },`);
1823
+ lines.push(` to: { ${toProps.join(", ")} },`);
1824
+ lines.push(` },`);
1825
+ }
1826
+ }
1827
+ }
1828
+ lines.push(` },`);
1829
+ lines.push(``);
1830
+ lines.push(` animation: {`);
1831
+ if (v3Anim) {
1832
+ for (const a of v3Anim) {
1833
+ const infinite = a.type === "spin" ? " infinite" : "";
1834
+ lines.push(` '${a.name}': '${a.name} ${a.durationVar} ${a.easingVar}${infinite}',`);
1835
+ }
1836
+ }
1837
+ lines.push(` },`);
1838
+ lines.push(` },`);
1839
+ lines.push(` },`);
1840
+ lines.push(` plugins: [`);
1841
+ lines.push(` // Icon size utilities (icon-xs, icon-sm, icon-md, icon-lg, icon-xl)`);
1842
+ lines.push(` function({ addUtilities }) {`);
1843
+ lines.push(` addUtilities({`);
1844
+ for (const name of ["xs", "sm", "md", "lg", "xl"]) {
1845
+ lines.push(` '.icon-${name}': { width: 'var(--icon-size-${name})', height: 'var(--icon-size-${name})' },`);
1846
+ }
1847
+ lines.push(` })`);
1848
+ lines.push(` },`);
1849
+ lines.push(` // Scale reset for grouped buttons (override active:scale-pressed)`);
1850
+ lines.push(` function({ addUtilities }) {`);
1851
+ lines.push(` addUtilities({`);
1852
+ lines.push(` '.scale-none': { transform: 'none !important' },`);
1853
+ lines.push(` })`);
1854
+ lines.push(` },`);
1855
+ lines.push(` // Focus ring utility (v3/v4 compatible)`);
1856
+ lines.push(` function({ addUtilities }) {`);
1857
+ lines.push(` addUtilities({`);
1858
+ lines.push(` '.focus-ring': {`);
1859
+ lines.push(` outline: '2px solid transparent',`);
1860
+ lines.push(` 'outline-offset': '2px',`);
1861
+ lines.push(` 'box-shadow': '0 0 0 2px var(--color-focus-ring)',`);
1862
+ lines.push(` },`);
1863
+ lines.push(` })`);
1864
+ lines.push(` },`);
1865
+ if (v3Anim) {
1866
+ lines.push(` // Animation utilities (generated from semantic.animation)`);
1867
+ lines.push(` function({ addUtilities }) {`);
1868
+ lines.push(` addUtilities({`);
1869
+ for (const a of v3Anim) {
1870
+ const infinite = a.type === "spin" ? " infinite" : "";
1871
+ lines.push(` '.animate-${a.name}': { 'animation': '${a.name} ${a.durationVar} ${a.easingVar}${infinite}' },`);
1872
+ }
1873
+ lines.push(` })`);
1874
+ lines.push(` },`);
1875
+ }
1876
+ lines.push(` ],`);
1877
+ lines.push(`}`);
1878
+ lines.push(``);
1879
+ return lines.join("\n");
1880
+ }
1881
+ function generateV4Theme(tokens) {
1882
+ const p = tokens.primitive;
1883
+ const lines = [];
1884
+ lines.push(`/**`);
1885
+ lines.push(` * Design System - Tailwind CSS v4 Theme`);
1886
+ lines.push(` * \u26A0\uFE0F Auto-generated from figma-tokens.json \u2014 DO NOT EDIT`);
1887
+ lines.push(` *`);
1888
+ lines.push(` * This file is a MAPPING layer between variables.css and Tailwind v4 utilities.`);
1889
+ lines.push(` * Actual values live in variables.css (single source of truth).`);
1890
+ lines.push(` *`);
1891
+ lines.push(` * - var() references: auto-sync when variables.css changes`);
1892
+ lines.push(` * - Hardcoded values: same-name variables (overridden by variables.css at runtime)`);
1893
+ lines.push(` * - Dark mode: handled entirely by variables.css .dark block (no duplication here)`);
1894
+ lines.push(` *`);
1895
+ lines.push(` * Usage:`);
1896
+ lines.push(` * \`\`\`css`);
1897
+ lines.push(` * @import "tailwindcss";`);
1898
+ lines.push(` * @import "@7onic-ui/react/tokens"; <- variables.css (values)`);
1899
+ lines.push(` * @import "@7onic-ui/react/theme"; <- this file (mapping)`);
1900
+ lines.push(` * \`\`\``);
1901
+ lines.push(` */`);
1902
+ lines.push(``);
1903
+ lines.push(`@theme {`);
1904
+ lines.push(` /* =============================================`);
1905
+ lines.push(` Colors \u2014 var() references (different names \u2192 auto-sync)`);
1906
+ lines.push(` When variables.css changes, these follow automatically.`);
1907
+ lines.push(` Dark mode: variables.css .dark changes the source \u2192 auto-cascade.`);
1908
+ lines.push(` ============================================= */`);
1909
+ lines.push(``);
1910
+ const resolveLight = (cat, variant) => {
1911
+ const catData = tokens.light.color[cat];
1912
+ if (!catData) return "";
1913
+ const token = catData[variant];
1914
+ if (!token) return "";
1915
+ return typeof token.value === "string" && token.value.startsWith("{") ? resolveReference(token.value, tokens) : String(token.value);
1916
+ };
1917
+ lines.push(` /* Background */`);
1918
+ lines.push(` --color-background: ${resolveLight("background", "default")}; /* same name \u2192 overridden by variables.css */`);
1919
+ lines.push(` --color-background-paper: ${resolveLight("background", "paper")}; /* same name \u2192 overridden by variables.css */`);
1920
+ lines.push(` --color-background-elevated: ${resolveLight("background", "elevated")}; /* same name \u2192 overridden by variables.css */`);
1921
+ lines.push(` --color-background-muted: ${resolveLight("background", "muted")}; /* same name \u2192 overridden by variables.css */`);
1922
+ lines.push(``);
1923
+ lines.push(` /* Foreground */`);
1924
+ lines.push(` --color-foreground: var(--color-text);`);
1925
+ lines.push(``);
1926
+ const v4ColorGroups = ["primary", "secondary", "success", "warning", "error", "info"];
1927
+ for (const group of v4ColorGroups) {
1928
+ const label = group.charAt(0).toUpperCase() + group.slice(1);
1929
+ lines.push(` /* ${label} */`);
1930
+ lines.push(` --color-${group}: ${resolveLight(group, "default")}; /* same name \u2192 overridden by variables.css */`);
1931
+ lines.push(` --color-${group}-hover: ${resolveLight(group, "hover")}; /* same name \u2192 overridden by variables.css */`);
1932
+ lines.push(` --color-${group}-active: ${resolveLight(group, "active")}; /* same name \u2192 overridden by variables.css */`);
1933
+ lines.push(` --color-${group}-tint: ${resolveLight(group, "tint")}; /* same name \u2192 overridden by variables.css */`);
1934
+ lines.push(` --color-${group}-foreground: var(--color-${group}-text);`);
1935
+ if (group === "error") {
1936
+ lines.push(` --color-error-bg: var(--color-error-bg);`);
1937
+ }
1938
+ lines.push(``);
1939
+ }
1940
+ lines.push(` /* Border */`);
1941
+ lines.push(` --color-border: ${resolveLight("border", "default")}; /* same name \u2192 overridden by variables.css */`);
1942
+ lines.push(` --color-border-strong: ${resolveLight("border", "strong")}; /* same name \u2192 overridden by variables.css */`);
1943
+ lines.push(` --color-border-subtle: ${resolveLight("border", "subtle")}; /* same name \u2192 overridden by variables.css */`);
1944
+ lines.push(``);
1945
+ lines.push(` /* Muted */`);
1946
+ lines.push(` --color-muted: var(--color-background-muted);`);
1947
+ lines.push(` --color-muted-foreground: var(--color-text-muted);`);
1948
+ lines.push(``);
1949
+ lines.push(` /* Ring (focus) */`);
1950
+ lines.push(` --color-ring: var(--color-focus-ring);`);
1951
+ lines.push(` --color-ring-error: var(--color-focus-ring-error);`);
1952
+ lines.push(``);
1953
+ lines.push(` /* Disabled */`);
1954
+ lines.push(` --color-disabled: ${resolveLight("disabled", "default")}; /* same name \u2192 overridden by variables.css */`);
1955
+ lines.push(` --color-disabled-foreground: var(--color-disabled-text);`);
1956
+ lines.push(``);
1957
+ lines.push(` /* Chart */`);
1958
+ lines.push(` --color-chart-1: ${resolveLight("chart", "1")}; /* same name \u2192 overridden by variables.css */`);
1959
+ lines.push(` --color-chart-2: ${resolveLight("chart", "2")}; /* same name \u2192 overridden by variables.css */`);
1960
+ lines.push(` --color-chart-3: ${resolveLight("chart", "3")}; /* same name \u2192 overridden by variables.css */`);
1961
+ lines.push(` --color-chart-4: ${resolveLight("chart", "4")}; /* same name \u2192 overridden by variables.css */`);
1962
+ lines.push(` --color-chart-5: ${resolveLight("chart", "5")}; /* same name \u2192 overridden by variables.css */`);
1963
+ lines.push(``);
1964
+ lines.push(` /* Text */`);
1965
+ lines.push(` --color-text: ${resolveLight("text", "default")}; /* same name \u2192 overridden by variables.css */`);
1966
+ lines.push(` --color-text-muted: ${resolveLight("text", "muted")}; /* same name \u2192 overridden by variables.css */`);
1967
+ lines.push(` --color-text-subtle: ${resolveLight("text", "subtle")}; /* same name \u2192 overridden by variables.css */`);
1968
+ lines.push(` --color-text-link: ${resolveLight("text", "link")}; /* same name \u2192 overridden by variables.css */`);
1969
+ lines.push(` --color-text-primary: ${resolveLight("text", "primary")}; /* same name \u2192 overridden by variables.css */`);
1970
+ lines.push(` --color-text-info: ${resolveLight("text", "info")}; /* same name \u2192 overridden by variables.css */`);
1971
+ lines.push(` --color-text-success: ${resolveLight("text", "success")}; /* same name \u2192 overridden by variables.css */`);
1972
+ lines.push(` --color-text-error: ${resolveLight("text", "error")}; /* same name \u2192 overridden by variables.css */`);
1973
+ lines.push(` --color-text-warning: ${resolveLight("text", "warning")}; /* same name \u2192 overridden by variables.css */`);
1974
+ lines.push(``);
1975
+ const colorData = p.color;
1976
+ lines.push(` /* =============================================`);
1977
+ lines.push(` Primitive Color Palettes`);
1978
+ lines.push(` Same names as variables.css \u2014 overridden at runtime.`);
1979
+ lines.push(` ============================================= */`);
1980
+ lines.push(``);
1981
+ lines.push(` --color-white: ${colorData.white.value};`);
1982
+ lines.push(` --color-black: ${colorData.black.value};`);
1983
+ lines.push(``);
1984
+ const v4Palettes = ["gray", "primary", "secondary", "blue", "green", "yellow", "red", "violet", "rose"];
1985
+ for (const palName of v4Palettes) {
1986
+ const palette = colorData[palName];
1987
+ if (!palette) continue;
1988
+ lines.push(` /* ${palName} */`);
1989
+ for (const shade of orderedKeys(palette, { type: "known", order: KNOWN_ORDERS.shadeOrder }, `primitive.color.${palName}`)) {
1990
+ const token = palette[shade];
1991
+ if (token?.value) {
1992
+ lines.push(` --color-${palName}-${shade}: ${token.value};`);
1993
+ }
1994
+ }
1995
+ lines.push(``);
1996
+ }
1997
+ lines.push(` /* =============================================`);
1998
+ lines.push(` Typography \u2014 var() references (different namespace \u2192 auto-sync)`);
1999
+ lines.push(` @theme uses --text-*, variables.css uses --font-size-*`);
2000
+ lines.push(` ============================================= */`);
2001
+ for (const name of orderedKeys(p.fontSize, { type: "known", order: KNOWN_ORDERS.fontSize }, "primitive.fontSize")) {
2002
+ lines.push(` --text-${name}: var(--font-size-${name});`);
2003
+ lines.push(` --text-${name}--line-height: var(--line-height-${name});`);
2004
+ }
2005
+ lines.push(``);
2006
+ const fontFamilyData = p.fontFamily;
2007
+ if (fontFamilyData) {
2008
+ lines.push(` /* =============================================`);
2009
+ lines.push(` Font Family \u2014 var() references to variables.css`);
2010
+ lines.push(` ============================================= */`);
2011
+ for (const name of Object.keys(fontFamilyData).filter((k) => !k.startsWith("$"))) {
2012
+ lines.push(` --font-${name}: var(--font-family-${name});`);
2013
+ }
2014
+ lines.push(``);
2015
+ }
2016
+ lines.push(` /* =============================================`);
2017
+ lines.push(` Border Radius`);
2018
+ lines.push(` Same names as variables.css \u2014 values here just register utilities.`);
2019
+ lines.push(` Runtime values come from variables.css (unlayered > theme layer).`);
2020
+ lines.push(` ============================================= */`);
2021
+ const radiusData = p.borderRadius;
2022
+ for (const name of orderedKeys(radiusData, { type: "known", order: KNOWN_ORDERS.borderRadius }, "primitive.borderRadius")) {
2023
+ const token = radiusData[name];
2024
+ if (!token) continue;
2025
+ if (name === "base") {
2026
+ lines.push(` --radius-DEFAULT: ${radiusData["base"].value}px;`);
2027
+ } else {
2028
+ lines.push(` --radius-${name}: ${formatValue(String(token.value), "borderRadius")};`);
2029
+ }
2030
+ }
2031
+ lines.push(``);
2032
+ lines.push(` /* =============================================`);
2033
+ lines.push(` Shadows`);
2034
+ lines.push(` Same names as variables.css \u2014 overridden at runtime.`);
2035
+ lines.push(` Dark mode shadows also handled by variables.css .dark block.`);
2036
+ lines.push(` ============================================= */`);
2037
+ const shadowData = p.shadow;
2038
+ for (const name of orderedKeys(shadowData, { type: "known", order: KNOWN_ORDERS.shadow }, "primitive.shadow")) {
2039
+ const token = shadowData[name];
2040
+ if (!token) continue;
2041
+ const ext = token.$extensions;
2042
+ lines.push(` --shadow-${name}: ${formatShadow(token.value, ext)};`);
2043
+ }
2044
+ lines.push(``);
2045
+ const v4CompSizeData = tokens.semantic?.componentSize;
2046
+ if (v4CompSizeData) {
2047
+ lines.push(` /* =============================================`);
2048
+ lines.push(` Component Sizes`);
2049
+ lines.push(` Switch, Slider thumb/track dimensions.`);
2050
+ lines.push(` ============================================= */`);
2051
+ for (const comp of Object.keys(v4CompSizeData)) {
2052
+ const compData = v4CompSizeData[comp];
2053
+ for (const part of Object.keys(compData)) {
2054
+ const partData = compData[part];
2055
+ for (const size of Object.keys(partData)) {
2056
+ const token = partData[size];
2057
+ const val = token.value;
2058
+ if (typeof val === "object" && val !== null) {
2059
+ const obj = val;
2060
+ if (obj.width) lines.push(` --component-${comp}-${part}-${size}-width: ${obj.width}px;`);
2061
+ if (obj.height) lines.push(` --component-${comp}-${part}-${size}-height: ${obj.height}px;`);
2062
+ } else {
2063
+ lines.push(` --component-${comp}-${part}-${size}: ${val}px;`);
2064
+ }
2065
+ }
2066
+ }
2067
+ }
2068
+ lines.push(``);
2069
+ }
2070
+ const v4OpacityData = p.opacity;
2071
+ if (v4OpacityData) {
2072
+ lines.push(` /* =============================================`);
2073
+ lines.push(` Opacity`);
2074
+ lines.push(` Same names as variables.css \u2014 overridden at runtime.`);
2075
+ lines.push(` ============================================= */`);
2076
+ for (const name of orderedKeys(v4OpacityData, { type: "numeric" }, "v4.opacity")) {
2077
+ const token = v4OpacityData[name];
2078
+ if (!token) continue;
2079
+ const pct = Math.round(Number(token.value) * 100);
2080
+ lines.push(` --opacity-${name}: ${pct}%;`);
2081
+ }
2082
+ lines.push(``);
2083
+ }
2084
+ const v4EasingData = p.easing;
2085
+ if (v4EasingData) {
2086
+ lines.push(` /* =============================================`);
2087
+ lines.push(` Easing \u2014 var() references to variables.css`);
2088
+ lines.push(` ============================================= */`);
2089
+ for (const name of orderedKeys(v4EasingData, { type: "known", order: KNOWN_ORDERS.easing }, "v4.easing")) {
2090
+ const token = v4EasingData[name];
2091
+ if (!token) continue;
2092
+ const cssVarSuffix = camelToKebab(name);
2093
+ let v4Suffix;
2094
+ if (name === "ease") {
2095
+ v4Suffix = "DEFAULT";
2096
+ } else if (name === "linear") {
2097
+ v4Suffix = "linear";
2098
+ } else {
2099
+ v4Suffix = camelToKebab(name.replace(/^ease/, ""));
2100
+ }
2101
+ lines.push(` --ease-${v4Suffix}: var(--easing-${cssVarSuffix});`);
2102
+ }
2103
+ lines.push(``);
2104
+ }
2105
+ lines.push(` /* =============================================`);
2106
+ lines.push(` Animation`);
2107
+ lines.push(` ============================================= */`);
2108
+ lines.push(` --animate-spin: spin 1s linear infinite;`);
2109
+ lines.push(`}`);
2110
+ lines.push(``);
2111
+ lines.push(`/*`);
2112
+ lines.push(` * NOTE: No .dark block needed here.`);
2113
+ lines.push(` *`);
2114
+ lines.push(` * Dark mode is handled entirely by variables.css .dark { } block.`);
2115
+ lines.push(` * - var() referenced variables: source changes in .dark \u2192 auto-cascade`);
2116
+ lines.push(` * - Same-name variables: variables.css (unlayered) overrides @theme (theme layer)`);
2117
+ lines.push(` *`);
2118
+ lines.push(` * CSS cascade: unlayered > @layer theme`);
2119
+ lines.push(` * So variables.css always wins over @theme for same-name variables.`);
2120
+ lines.push(` */`);
2121
+ lines.push(``);
2122
+ lines.push(`/* =============================================`);
2123
+ lines.push(` Custom Utilities`);
2124
+ lines.push(` ============================================= */`);
2125
+ lines.push(``);
2126
+ lines.push(`/* Transition duration \u2014 var() references to variables.css */`);
2127
+ for (const name of orderedKeys(p.duration, { type: "known", order: KNOWN_ORDERS.duration }, "primitive.duration")) {
2128
+ lines.push(`@utility duration-${name} {`);
2129
+ lines.push(` transition-duration: var(--duration-${name});`);
2130
+ lines.push(`}`);
2131
+ }
2132
+ lines.push(``);
2133
+ lines.push(`/* Scale \u2014 var() references to variables.css */`);
2134
+ const v4ScaleData = p.scale;
2135
+ for (const [name, token] of Object.entries(v4ScaleData)) {
2136
+ if (name.startsWith("$")) continue;
2137
+ lines.push(`@utility scale-${name} {`);
2138
+ lines.push(` scale: var(--scale-${name});`);
2139
+ lines.push(`}`);
2140
+ }
2141
+ lines.push(``);
2142
+ lines.push(`/* Icon size utilities (5-step system) \u2014 var() references */`);
2143
+ for (const name of orderedKeys(p.iconSize, { type: "known", order: KNOWN_ORDERS.iconSize }, "primitive.iconSize")) {
2144
+ lines.push(`@utility icon-${name} {`);
2145
+ lines.push(` width: var(--icon-size-${name});`);
2146
+ lines.push(` height: var(--icon-size-${name});`);
2147
+ lines.push(`}`);
2148
+ }
2149
+ lines.push(``);
2150
+ lines.push(`/* Focus ring utility (v3/v4 compatible) */`);
2151
+ lines.push(`@utility focus-ring {`);
2152
+ lines.push(` outline: 2px solid transparent;`);
2153
+ lines.push(` outline-offset: 2px;`);
2154
+ lines.push(` box-shadow: 0 0 0 2px var(--color-focus-ring);`);
2155
+ lines.push(`}`);
2156
+ lines.push(``);
2157
+ lines.push(`/* Z-index utilities (named semantic values) \u2014 var() references */`);
2158
+ const zNamedOnly = ["sticky", "dropdown", "overlay", "modal", "popover", "tooltip", "toast"];
2159
+ const zUtilities = orderedKeys(p.zIndex, { type: "known", order: KNOWN_ORDERS.zIndex }).filter((k) => zNamedOnly.includes(k) || isNaN(Number(k)));
2160
+ for (const name of zUtilities) {
2161
+ lines.push(`@utility z-${name} {`);
2162
+ lines.push(` z-index: var(--z-index-${name});`);
2163
+ lines.push(`}`);
2164
+ }
2165
+ lines.push(``);
2166
+ lines.push(`/* Scale reset for grouped buttons (override active:scale-pressed) */`);
2167
+ lines.push(`@utility scale-none {`);
2168
+ lines.push(` scale: 1 !important;`);
2169
+ lines.push(` transform: none !important;`);
2170
+ lines.push(`}`);
2171
+ lines.push(``);
2172
+ const v4Anim = readAnimationTokens(tokens);
2173
+ if (v4Anim) {
2174
+ lines.push(`/* =============================================`);
2175
+ lines.push(` Animations`);
2176
+ lines.push(` Generated from semantic.animation in figma-tokens.json`);
2177
+ lines.push(` Token name = keyframe name = class name (1:1)`);
2178
+ lines.push(` ============================================= */`);
2179
+ lines.push(``);
2180
+ for (const a of v4Anim) {
2181
+ lines.push(generateAnimationCss(a, "v4"));
2182
+ lines.push(``);
2183
+ }
2184
+ }
2185
+ return lines.join("\n");
2186
+ }
2187
+ function generateCssBundle() {
2188
+ const lines = [];
2189
+ lines.push(`/* Auto-generated by sync-tokens \u2014 DO NOT EDIT */`);
2190
+ lines.push(`/* All-in-one CSS bundle: variables + light/dark themes */`);
2191
+ lines.push(``);
2192
+ lines.push(`@import './variables.css';`);
2193
+ lines.push(`@import './themes/light.css';`);
2194
+ lines.push(`@import './themes/dark.css';`);
2195
+ lines.push(``);
2196
+ return lines.join("\n");
2197
+ }
2198
+ function generateV4Bundle() {
2199
+ const lines = [];
2200
+ lines.push(`/* Auto-generated by sync-tokens \u2014 DO NOT EDIT */`);
2201
+ lines.push(`/* All-in-one Tailwind v4 bundle: variables + themes + v4 mapping */`);
2202
+ lines.push(``);
2203
+ lines.push(`@import '../css/variables.css';`);
2204
+ lines.push(`@import '../css/themes/light.css';`);
2205
+ lines.push(`@import '../css/themes/dark.css';`);
2206
+ lines.push(`@import './v4-theme.css';`);
2207
+ lines.push(``);
2208
+ return lines.join("\n");
2209
+ }
2210
+ function parseExistingVars(cssContent) {
2211
+ const vars = /* @__PURE__ */ new Map();
2212
+ const rootMatch = cssContent.match(/:root\s*\{([\s\S]*?)\n\}/);
2213
+ if (!rootMatch) return vars;
2214
+ const rootBlock = rootMatch[1];
2215
+ const varRegex = /--([\w-]+)\s*:\s*([^;]+);/g;
2216
+ let match;
2217
+ while ((match = varRegex.exec(rootBlock)) !== null) {
2218
+ vars.set(`--${match[1]}`, match[2].trim());
2219
+ }
2220
+ return vars;
2221
+ }
2222
+ function detectBreakingChanges(oldVars, newVars) {
2223
+ const removed = /* @__PURE__ */ new Map();
2224
+ const added = /* @__PURE__ */ new Map();
2225
+ const renamed = [];
2226
+ const changed = [];
2227
+ for (const [name, value] of oldVars) {
2228
+ if (!newVars.has(name)) {
2229
+ removed.set(name, value);
2230
+ } else if (newVars.get(name) !== value) {
2231
+ changed.push({ name, oldValue: value, newValue: newVars.get(name) });
2232
+ }
2233
+ }
2234
+ for (const [name, value] of newVars) {
2235
+ if (!oldVars.has(name)) {
2236
+ added.set(name, value);
2237
+ }
2238
+ }
2239
+ for (const [oldName, oldValue] of removed) {
2240
+ for (const [newName, newValue] of added) {
2241
+ if (oldValue === newValue) {
2242
+ renamed.push({ oldName, newName, value: oldValue });
2243
+ }
2244
+ }
2245
+ }
2246
+ return { removed, added, renamed, changed };
2247
+ }
2248
+ function formatBreakingChanges(changes) {
2249
+ const lines = [];
2250
+ lines.push(`
2251
+ \u26A0\uFE0F Breaking Changes Detected:
2252
+ `);
2253
+ if (changes.removed.size > 0) {
2254
+ lines.push(` REMOVED (${changes.removed.size}):`);
2255
+ for (const [name, value] of changes.removed) {
2256
+ lines.push(` - ${name}: ${value}`);
2257
+ }
2258
+ lines.push(``);
2259
+ }
2260
+ if (changes.renamed.length > 0) {
2261
+ lines.push(` POSSIBLE RENAME (${changes.renamed.length}):`);
2262
+ for (const r of changes.renamed) {
2263
+ lines.push(` ${r.oldName} \u2192 ${r.newName} (same value: ${r.value})`);
2264
+ }
2265
+ lines.push(``);
2266
+ }
2267
+ if (changes.added.size > 0) {
2268
+ lines.push(` ADDED (${changes.added.size}):`);
2269
+ for (const [name, value] of changes.added) {
2270
+ lines.push(` + ${name}: ${value}`);
2271
+ }
2272
+ lines.push(``);
2273
+ }
2274
+ lines.push(` Affected files: variables.css, v3-preset.js, v4-theme.css, globals.css
2275
+ `);
2276
+ return lines.join("\n");
2277
+ }
2278
+ function formatDiff(changes) {
2279
+ const total = changes.removed.size + changes.added.size + changes.renamed.length + changes.changed.length;
2280
+ if (total === 0) return "";
2281
+ const lines = [];
2282
+ lines.push(`
2283
+ \u{1F4CA} Token Diff Summary (${total} changes):
2284
+ `);
2285
+ if (changes.renamed.length > 0) {
2286
+ lines.push(` RENAMED (${changes.renamed.length}):`);
2287
+ for (const r of changes.renamed) {
2288
+ lines.push(` ~ ${r.oldName} \u2192 ${r.newName}`);
2289
+ }
2290
+ lines.push(``);
2291
+ }
2292
+ if (changes.changed.length > 0) {
2293
+ lines.push(` VALUE CHANGED (${changes.changed.length}):`);
2294
+ for (const c of changes.changed) {
2295
+ lines.push(` ~ ${c.name}: ${c.oldValue} \u2192 ${c.newValue}`);
2296
+ }
2297
+ lines.push(``);
2298
+ }
2299
+ if (changes.added.size > 0) {
2300
+ lines.push(` ADDED (${changes.added.size}):`);
2301
+ for (const [name, value] of changes.added) {
2302
+ lines.push(` + ${name}: ${value}`);
2303
+ }
2304
+ lines.push(``);
2305
+ }
2306
+ if (changes.removed.size > 0) {
2307
+ lines.push(` REMOVED (${changes.removed.size}):`);
2308
+ for (const [name, value] of changes.removed) {
2309
+ lines.push(` - ${name}: ${value}`);
2310
+ }
2311
+ lines.push(``);
2312
+ }
2313
+ return lines.join("\n");
2314
+ }
2315
+ function generateDeprecatedAliases(changes) {
2316
+ if (changes.renamed.length === 0) return "";
2317
+ const lines = [];
2318
+ lines.push(`/**`);
2319
+ lines.push(` * Deprecated CSS variable aliases`);
2320
+ lines.push(` * Auto-generated by sync-tokens \u2014 do not edit manually`);
2321
+ lines.push(` *`);
2322
+ lines.push(` * These aliases provide backwards compatibility for renamed tokens.`);
2323
+ lines.push(` * They will be removed in a future major version.`);
2324
+ lines.push(` */`);
2325
+ lines.push(`:root {`);
2326
+ for (const r of changes.renamed) {
2327
+ lines.push(` /* @deprecated \u2014 use ${r.newName} instead */`);
2328
+ lines.push(` ${r.oldName}: var(${r.newName});`);
2329
+ }
2330
+ lines.push(`}`);
2331
+ lines.push(``);
2332
+ return lines.join("\n");
2333
+ }
2334
+ async function promptUser(message) {
2335
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2336
+ return new Promise((resolve3) => {
2337
+ rl.question(`${message} Continue? (y/n) `, (answer) => {
2338
+ rl.close();
2339
+ resolve3(answer.toLowerCase() === "y");
2340
+ });
2341
+ });
2342
+ }
2343
+
2344
+ // scripts/cli-sync.ts
2345
+ function parseArgs(argv) {
2346
+ const args = argv.slice(2);
2347
+ const parsed = {
2348
+ input: "./figma-tokens.json",
2349
+ output: "./",
2350
+ force: false,
2351
+ dryRun: false,
2352
+ help: false
2353
+ };
2354
+ for (let i = 0; i < args.length; i++) {
2355
+ switch (args[i]) {
2356
+ case "--input":
2357
+ parsed.input = args[++i] ?? parsed.input;
2358
+ break;
2359
+ case "--output":
2360
+ parsed.output = args[++i] ?? parsed.output;
2361
+ break;
2362
+ case "--force":
2363
+ parsed.force = true;
2364
+ break;
2365
+ case "--dry-run":
2366
+ parsed.dryRun = true;
2367
+ break;
2368
+ case "--help":
2369
+ case "-h":
2370
+ parsed.help = true;
2371
+ break;
2372
+ }
2373
+ }
2374
+ return parsed;
2375
+ }
2376
+ function showHelp() {
2377
+ console.log(`
2378
+ @7onic-ui/tokens \u2014 Design Token Sync CLI
2379
+
2380
+ Usage:
2381
+ npx sync-tokens [options]
2382
+
2383
+ Options:
2384
+ --input <path> Path to figma-tokens.json (default: ./figma-tokens.json)
2385
+ --output <dir> Output directory (default: ./)
2386
+ --force Skip confirmation prompts
2387
+ --dry-run Preview changes without writing files
2388
+ --help, -h Show this help message
2389
+
2390
+ Generated files:
2391
+ css/variables.css All CSS custom properties
2392
+ css/themes/light.css Light theme semantic colors
2393
+ css/themes/dark.css Dark theme semantic colors
2394
+ css/all.css All-in-one CSS bundle
2395
+ tailwind/v3-preset.js Tailwind CSS v3 preset
2396
+ tailwind/v4-theme.css Tailwind CSS v4 theme
2397
+ tailwind/v4.css All-in-one Tailwind v4 bundle
2398
+ js/index.js CommonJS token exports
2399
+ js/index.mjs ESM token exports
2400
+ types/index.d.ts TypeScript type definitions
2401
+ json/tokens.json Flat resolved JSON
2402
+
2403
+ Example:
2404
+ npx sync-tokens --input ./design/figma-tokens.json --output ./tokens/
2405
+ `);
2406
+ }
2407
+ function writeOutputFile(outputDir, relativePath, content) {
2408
+ const fullPath = path2.resolve(outputDir, relativePath);
2409
+ const dir = path2.dirname(fullPath);
2410
+ if (!fs2.existsSync(dir)) {
2411
+ fs2.mkdirSync(dir, { recursive: true });
2412
+ }
2413
+ fs2.writeFileSync(fullPath, content);
2414
+ }
2415
+ async function cliMain() {
2416
+ const opts = parseArgs(process.argv);
2417
+ if (opts.help) {
2418
+ showHelp();
2419
+ return;
2420
+ }
2421
+ const inputPath = path2.resolve(opts.input);
2422
+ const outputDir = path2.resolve(opts.output);
2423
+ if (!fs2.existsSync(inputPath)) {
2424
+ console.error(`\u274C figma-tokens.json not found: ${inputPath}`);
2425
+ console.error(" Use --input <path> to specify the location.");
2426
+ process.exit(1);
2427
+ }
2428
+ console.log("\u{1F504} sync-tokens: Reading figma-tokens.json...");
2429
+ console.log(` Input: ${inputPath}`);
2430
+ console.log(` Output: ${outputDir}`);
2431
+ console.log("");
2432
+ const tokens = readJsonFile(inputPath);
2433
+ console.log("\u{1F50D} Validating tokens...");
2434
+ const tokenWarnings = validateTokens(tokens);
2435
+ const hasErrors = tokenWarnings.some((w) => w.level === "error");
2436
+ if (hasErrors) {
2437
+ printTokenWarnings(tokenWarnings);
2438
+ console.error("\n\u274C Token validation failed with errors. Fix the issues above before syncing.");
2439
+ process.exit(1);
2440
+ }
2441
+ let deprecatedCss = "";
2442
+ const existingVarsCssPath = path2.join(outputDir, "css/variables.css");
2443
+ const existingCss = fs2.existsSync(existingVarsCssPath) ? fs2.readFileSync(existingVarsCssPath, "utf-8") : "";
2444
+ if (existingCss) {
2445
+ const oldVars = parseExistingVars(existingCss);
2446
+ const newCss = generateVariablesCss(tokens);
2447
+ const newVars = parseExistingVars(newCss);
2448
+ const changes = detectBreakingChanges(oldVars, newVars);
2449
+ const hasBreaking = changes.removed.size > 0 || changes.renamed.length > 0;
2450
+ const hasAnyChange = hasBreaking || changes.added.size > 0 || changes.changed.length > 0;
2451
+ if (hasAnyChange) {
2452
+ console.log(formatDiff(changes));
2453
+ }
2454
+ if (hasBreaking) {
2455
+ console.log(formatBreakingChanges(changes));
2456
+ if (!opts.force) {
2457
+ const proceed = await promptUser("");
2458
+ if (!proceed) {
2459
+ console.log("\u274C Aborted.");
2460
+ process.exit(1);
2461
+ }
2462
+ }
2463
+ }
2464
+ deprecatedCss = generateDeprecatedAliases(changes);
2465
+ if (deprecatedCss) {
2466
+ console.log(`\u{1F4DD} Generating deprecated aliases (${changes.renamed.length} renames)...`);
2467
+ }
2468
+ }
2469
+ console.log("\u{1F4DD} Generating files...");
2470
+ const variablesCss = generateVariablesCss(tokens);
2471
+ const themeLight = generateThemeLight(tokens);
2472
+ const themeDark = generateThemeDark(tokens);
2473
+ const v3Preset = generateV3Preset(tokens);
2474
+ const v4Theme = generateV4Theme(tokens);
2475
+ const jsTokens = generateJsTokens(tokens);
2476
+ const typeDefs = generateTypeDefinitions(tokens);
2477
+ const normalizedJson = generateNormalizedJson(tokens);
2478
+ const cssBundle = generateCssBundle();
2479
+ const v4Bundle = generateV4Bundle();
2480
+ if (opts.dryRun) {
2481
+ console.log("\n--- DRY RUN: No files written ---");
2482
+ printTokenWarnings(tokenWarnings);
2483
+ console.log("\nFiles that would be generated:");
2484
+ console.log(" \u{1F4C4} css/variables.css");
2485
+ console.log(" \u{1F4C4} css/themes/light.css");
2486
+ console.log(" \u{1F4C4} css/themes/dark.css");
2487
+ console.log(" \u{1F4C4} tailwind/v3-preset.js");
2488
+ console.log(" \u{1F4C4} tailwind/v4-theme.css");
2489
+ console.log(" \u{1F4C4} js/index.js");
2490
+ console.log(" \u{1F4C4} js/index.mjs");
2491
+ console.log(" \u{1F4C4} types/index.d.ts");
2492
+ console.log(" \u{1F4C4} json/tokens.json");
2493
+ console.log(" \u{1F4C4} css/all.css (bundle)");
2494
+ console.log(" \u{1F4C4} tailwind/v4.css (bundle)");
2495
+ console.log("\n\u2705 Dry run complete.");
2496
+ return;
2497
+ }
2498
+ writeOutputFile(outputDir, "css/variables.css", variablesCss);
2499
+ writeOutputFile(outputDir, "css/themes/light.css", themeLight);
2500
+ writeOutputFile(outputDir, "css/themes/dark.css", themeDark);
2501
+ writeOutputFile(outputDir, "tailwind/v3-preset.js", v3Preset);
2502
+ writeOutputFile(outputDir, "tailwind/v4-theme.css", v4Theme);
2503
+ writeOutputFile(outputDir, "js/index.js", jsTokens.cjs);
2504
+ writeOutputFile(outputDir, "js/index.mjs", jsTokens.esm);
2505
+ writeOutputFile(outputDir, "types/index.d.ts", typeDefs);
2506
+ writeOutputFile(outputDir, "json/tokens.json", normalizedJson);
2507
+ writeOutputFile(outputDir, "css/all.css", cssBundle);
2508
+ writeOutputFile(outputDir, "tailwind/v4.css", v4Bundle);
2509
+ if (deprecatedCss) {
2510
+ writeOutputFile(outputDir, "css/deprecated.css", deprecatedCss);
2511
+ }
2512
+ printTokenWarnings(tokenWarnings);
2513
+ console.log("");
2514
+ console.log("\u2705 sync-tokens complete:");
2515
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "css/variables.css"))}`);
2516
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "css/themes/light.css"))}`);
2517
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "css/themes/dark.css"))}`);
2518
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "tailwind/v3-preset.js"))}`);
2519
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "tailwind/v4-theme.css"))}`);
2520
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "js/index.js"))}`);
2521
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "js/index.mjs"))}`);
2522
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "types/index.d.ts"))}`);
2523
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "json/tokens.json"))}`);
2524
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "css/all.css"))} (bundle)`);
2525
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "tailwind/v4.css"))} (bundle)`);
2526
+ if (deprecatedCss) {
2527
+ console.log(` \u{1F4C4} ${path2.relative(".", path2.join(outputDir, "css/deprecated.css"))} (backwards compat)`);
2528
+ }
2529
+ }
2530
+ cliMain().catch((err) => {
2531
+ console.error("\u274C sync-tokens failed:", err.message);
2532
+ process.exit(1);
2533
+ });