@qw-ui/tokens 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -1,1182 +1,2232 @@
1
- import { z } from 'zod';
2
- import { rgb, parse, formatHex, oklch, clampChroma } from 'culori';
3
-
4
- // src/seed/index.json
5
- var seed_default = {
6
- qw: {
7
- seed: {
8
- color: {
9
- brand: { $value: { colorSpace: "srgb", components: [0, 0.0667, 1], alpha: 1 }, $type: "color" },
10
- black: { $value: { colorSpace: "srgb", components: [0.1333, 0.1333, 0.1333], alpha: 1 }, $type: "color" },
11
- white: { $value: { colorSpace: "srgb", components: [1, 1, 1], alpha: 1 }, $type: "color" },
12
- gray: { $value: { colorSpace: "srgb", components: [0.6, 0.6, 0.6], alpha: 1 }, $type: "color" },
13
- red: { $value: { colorSpace: "srgb", components: [1, 0.251, 0.251], alpha: 1 }, $type: "color" },
14
- orange: { $value: { colorSpace: "srgb", components: [1, 0.4196, 0.2471], alpha: 1 }, $type: "color" },
15
- yellow: { $value: { colorSpace: "srgb", components: [1, 0.8353, 0.4588], alpha: 1 }, $type: "color" },
16
- green: { $value: { colorSpace: "srgb", components: [0.1216, 0.6, 0.2471], alpha: 1 }, $type: "color" },
17
- cyan: { $value: { colorSpace: "srgb", components: [0.1608, 0.7451, 0.8], alpha: 1 }, $type: "color" },
18
- blue: { $value: { colorSpace: "srgb", components: [0.051, 0.3255, 1], alpha: 1 }, $type: "color" },
19
- purple: { $value: { colorSpace: "srgb", components: [0.2627, 0.1725, 0.8588], alpha: 1 }, $type: "color" },
20
- brown: { $value: { colorSpace: "srgb", components: [0.7059, 0.3686, 0.0235], alpha: 1 }, $type: "color" }
21
- },
22
- size: {
23
- spacing: {
24
- step: { $value: { value: 4, unit: "px" }}
25
- },
26
- borderWidth: {
27
- hairline: { $value: { value: 1, unit: "px" }}
28
- },
29
- radius: {
30
- base: { $value: { value: 8, unit: "px" }},
31
- full: { $value: { value: 9999, unit: "px" }}
32
- },
33
- controlSize: {
34
- base: { $value: { value: 40, unit: "px" }}
35
- },
36
- iconSize: {
37
- base: { $value: { value: 20, unit: "px" }}
38
- },
39
- opacity: {
40
- disabled: { $value: 0.5},
41
- alphaStep: { $value: 0.04}
42
- },
43
- zIndex: {
44
- step: { $value: 10}
45
- }
46
- },
47
- typography: {
48
- fontFamily: {
49
- text: { $value: ["system-ui", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto", "Helvetica Neue", "Arial", "sans-serif"]},
50
- number: { $value: ["PlusJakartaSans", "system-ui", "Segoe UI", "Roboto", "Helvetica Neue", "Arial", "sans-serif"]},
51
- code: { $value: ["ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas", "Liberation Mono", "monospace"]},
52
- textMacLatin: { $value: ["-apple-system", "BlinkMacSystemFont", "SF Pro Text", "system-ui", "Helvetica Neue", "Helvetica", "Arial", "sans-serif"]},
53
- textMacZh: { $value: ["PingFang SC", "-apple-system", "BlinkMacSystemFont", "Hiragino Sans GB", "Hiragino Sans", "SF Pro Text", "system-ui", "Noto Sans SC", "Helvetica Neue", "Helvetica", "Arial", "sans-serif"]},
54
- textMacJa: { $value: ["Hiragino Sans", "Hiragino Kaku Gothic ProN", "-apple-system", "BlinkMacSystemFont", "PingFang SC", "SF Pro Text", "system-ui", "Noto Sans JP", "Helvetica Neue", "Helvetica", "Arial", "sans-serif"]},
55
- textWindowsLatin: { $value: ["Segoe UI", "system-ui", "Roboto", "Noto Sans", "Helvetica Neue", "Arial", "sans-serif"]},
56
- textWindowsZh: { $value: ["Microsoft YaHei", "Segoe UI", "system-ui", "Roboto", "Noto Sans SC", "Helvetica Neue", "Arial", "sans-serif"]},
57
- textWindowsJa: { $value: ["Meiryo", "Yu Gothic UI", "Segoe UI", "system-ui", "Roboto", "Noto Sans JP", "Helvetica Neue", "Arial", "sans-serif"]}
58
- },
59
- fontWeight: {
60
- regular: { $value: 400},
61
- medium: { $value: 500},
62
- semibold: { $value: 600}
63
- },
64
- fontSize: {
65
- base: { $value: { value: 14, unit: "px" }}
66
- },
67
- lineHeight: {
68
- base: { $value: 1.5}
69
- },
70
- letterSpacing: {
71
- base: { $value: { value: 0, unit: "px" }}
72
- }
73
- },
74
- motion: {
75
- duration: {
76
- base: { $value: { value: 200, unit: "ms" }}
77
- },
78
- easing: {
79
- standard: { $value: [0.16, 1, 0.3, 1]}
80
- }
81
- },
82
- effect: {
83
- shadow: {
84
- base: {
85
- $value: {
86
- color: { colorSpace: "srgb", components: [0, 0, 0], alpha: 0.08 },
87
- offsetX: { value: 0, unit: "px" },
88
- offsetY: { value: 2, unit: "px" },
89
- blur: { value: 4, unit: "px" },
90
- spread: { value: 0, unit: "px" }
91
- }}
92
- }
93
- }
94
- }
95
- }
1
+ import { z } from "zod";
2
+ import { clampChroma, formatHex, oklch, parse, rgb } from "culori";
3
+ //#region src/seed/index.json
4
+ var qw = { "seed": {
5
+ "color": {
6
+ "brand": {
7
+ "$value": {
8
+ "colorSpace": "srgb",
9
+ "components": [
10
+ 0,
11
+ .0667,
12
+ 1
13
+ ],
14
+ "alpha": 1
15
+ },
16
+ "$type": "color"
17
+ },
18
+ "black": {
19
+ "$value": {
20
+ "colorSpace": "srgb",
21
+ "components": [
22
+ .1333,
23
+ .1333,
24
+ .1333
25
+ ],
26
+ "alpha": 1
27
+ },
28
+ "$type": "color"
29
+ },
30
+ "white": {
31
+ "$value": {
32
+ "colorSpace": "srgb",
33
+ "components": [
34
+ 1,
35
+ 1,
36
+ 1
37
+ ],
38
+ "alpha": 1
39
+ },
40
+ "$type": "color"
41
+ },
42
+ "gray": {
43
+ "$value": {
44
+ "colorSpace": "srgb",
45
+ "components": [
46
+ .6,
47
+ .6,
48
+ .6
49
+ ],
50
+ "alpha": 1
51
+ },
52
+ "$type": "color"
53
+ },
54
+ "red": {
55
+ "$value": {
56
+ "colorSpace": "srgb",
57
+ "components": [
58
+ 1,
59
+ .251,
60
+ .251
61
+ ],
62
+ "alpha": 1
63
+ },
64
+ "$type": "color"
65
+ },
66
+ "orange": {
67
+ "$value": {
68
+ "colorSpace": "srgb",
69
+ "components": [
70
+ 1,
71
+ .4196,
72
+ .2471
73
+ ],
74
+ "alpha": 1
75
+ },
76
+ "$type": "color"
77
+ },
78
+ "yellow": {
79
+ "$value": {
80
+ "colorSpace": "srgb",
81
+ "components": [
82
+ 1,
83
+ .8353,
84
+ .4588
85
+ ],
86
+ "alpha": 1
87
+ },
88
+ "$type": "color"
89
+ },
90
+ "green": {
91
+ "$value": {
92
+ "colorSpace": "srgb",
93
+ "components": [
94
+ .1216,
95
+ .6,
96
+ .2471
97
+ ],
98
+ "alpha": 1
99
+ },
100
+ "$type": "color"
101
+ },
102
+ "cyan": {
103
+ "$value": {
104
+ "colorSpace": "srgb",
105
+ "components": [
106
+ .1608,
107
+ .7451,
108
+ .8
109
+ ],
110
+ "alpha": 1
111
+ },
112
+ "$type": "color"
113
+ },
114
+ "blue": {
115
+ "$value": {
116
+ "colorSpace": "srgb",
117
+ "components": [
118
+ .051,
119
+ .3255,
120
+ 1
121
+ ],
122
+ "alpha": 1
123
+ },
124
+ "$type": "color"
125
+ },
126
+ "purple": {
127
+ "$value": {
128
+ "colorSpace": "srgb",
129
+ "components": [
130
+ .2627,
131
+ .1725,
132
+ .8588
133
+ ],
134
+ "alpha": 1
135
+ },
136
+ "$type": "color"
137
+ },
138
+ "brown": {
139
+ "$value": {
140
+ "colorSpace": "srgb",
141
+ "components": [
142
+ .7059,
143
+ .3686,
144
+ .0235
145
+ ],
146
+ "alpha": 1
147
+ },
148
+ "$type": "color"
149
+ }
150
+ },
151
+ "size": {
152
+ "spacing": { "step": {
153
+ "$value": {
154
+ "value": 4,
155
+ "unit": "px"
156
+ },
157
+ "$type": "dimension"
158
+ } },
159
+ "borderWidth": { "hairline": {
160
+ "$value": {
161
+ "value": 1,
162
+ "unit": "px"
163
+ },
164
+ "$type": "dimension"
165
+ } },
166
+ "radius": {
167
+ "base": {
168
+ "$value": {
169
+ "value": 8,
170
+ "unit": "px"
171
+ },
172
+ "$type": "dimension"
173
+ },
174
+ "full": {
175
+ "$value": {
176
+ "value": 9999,
177
+ "unit": "px"
178
+ },
179
+ "$type": "dimension"
180
+ }
181
+ },
182
+ "controlSize": { "base": {
183
+ "$value": {
184
+ "value": 40,
185
+ "unit": "px"
186
+ },
187
+ "$type": "dimension"
188
+ } },
189
+ "iconSize": { "base": {
190
+ "$value": {
191
+ "value": 20,
192
+ "unit": "px"
193
+ },
194
+ "$type": "dimension"
195
+ } },
196
+ "opacity": {
197
+ "disabled": {
198
+ "$value": .5,
199
+ "$type": "number"
200
+ },
201
+ "alphaStep": {
202
+ "$value": .04,
203
+ "$type": "number"
204
+ }
205
+ },
206
+ "zIndex": { "step": {
207
+ "$value": 10,
208
+ "$type": "number"
209
+ } }
210
+ },
211
+ "typography": {
212
+ "fontFamily": {
213
+ "text": {
214
+ "$value": [
215
+ "system-ui",
216
+ "-apple-system",
217
+ "BlinkMacSystemFont",
218
+ "Segoe UI",
219
+ "Roboto",
220
+ "Helvetica Neue",
221
+ "Arial",
222
+ "sans-serif"
223
+ ],
224
+ "$type": "fontFamily"
225
+ },
226
+ "number": {
227
+ "$value": [
228
+ "PlusJakartaSans",
229
+ "system-ui",
230
+ "Segoe UI",
231
+ "Roboto",
232
+ "Helvetica Neue",
233
+ "Arial",
234
+ "sans-serif"
235
+ ],
236
+ "$type": "fontFamily"
237
+ },
238
+ "code": {
239
+ "$value": [
240
+ "ui-monospace",
241
+ "SFMono-Regular",
242
+ "SF Mono",
243
+ "Menlo",
244
+ "Consolas",
245
+ "Liberation Mono",
246
+ "monospace"
247
+ ],
248
+ "$type": "fontFamily"
249
+ },
250
+ "textMacLatin": {
251
+ "$value": [
252
+ "-apple-system",
253
+ "BlinkMacSystemFont",
254
+ "SF Pro Text",
255
+ "system-ui",
256
+ "Helvetica Neue",
257
+ "Helvetica",
258
+ "Arial",
259
+ "sans-serif"
260
+ ],
261
+ "$type": "fontFamily"
262
+ },
263
+ "textMacZh": {
264
+ "$value": [
265
+ "PingFang SC",
266
+ "-apple-system",
267
+ "BlinkMacSystemFont",
268
+ "Hiragino Sans GB",
269
+ "Hiragino Sans",
270
+ "SF Pro Text",
271
+ "system-ui",
272
+ "Noto Sans SC",
273
+ "Helvetica Neue",
274
+ "Helvetica",
275
+ "Arial",
276
+ "sans-serif"
277
+ ],
278
+ "$type": "fontFamily"
279
+ },
280
+ "textMacJa": {
281
+ "$value": [
282
+ "Hiragino Sans",
283
+ "Hiragino Kaku Gothic ProN",
284
+ "-apple-system",
285
+ "BlinkMacSystemFont",
286
+ "PingFang SC",
287
+ "SF Pro Text",
288
+ "system-ui",
289
+ "Noto Sans JP",
290
+ "Helvetica Neue",
291
+ "Helvetica",
292
+ "Arial",
293
+ "sans-serif"
294
+ ],
295
+ "$type": "fontFamily"
296
+ },
297
+ "textWindowsLatin": {
298
+ "$value": [
299
+ "Segoe UI",
300
+ "system-ui",
301
+ "Roboto",
302
+ "Noto Sans",
303
+ "Helvetica Neue",
304
+ "Arial",
305
+ "sans-serif"
306
+ ],
307
+ "$type": "fontFamily"
308
+ },
309
+ "textWindowsZh": {
310
+ "$value": [
311
+ "Microsoft YaHei",
312
+ "Segoe UI",
313
+ "system-ui",
314
+ "Roboto",
315
+ "Noto Sans SC",
316
+ "Helvetica Neue",
317
+ "Arial",
318
+ "sans-serif"
319
+ ],
320
+ "$type": "fontFamily"
321
+ },
322
+ "textWindowsJa": {
323
+ "$value": [
324
+ "Meiryo",
325
+ "Yu Gothic UI",
326
+ "Segoe UI",
327
+ "system-ui",
328
+ "Roboto",
329
+ "Noto Sans JP",
330
+ "Helvetica Neue",
331
+ "Arial",
332
+ "sans-serif"
333
+ ],
334
+ "$type": "fontFamily"
335
+ }
336
+ },
337
+ "fontWeight": {
338
+ "regular": {
339
+ "$value": 400,
340
+ "$type": "fontWeight"
341
+ },
342
+ "medium": {
343
+ "$value": 500,
344
+ "$type": "fontWeight"
345
+ },
346
+ "semibold": {
347
+ "$value": 600,
348
+ "$type": "fontWeight"
349
+ }
350
+ },
351
+ "fontSize": { "base": {
352
+ "$value": {
353
+ "value": 14,
354
+ "unit": "px"
355
+ },
356
+ "$type": "dimension"
357
+ } },
358
+ "lineHeight": { "base": {
359
+ "$value": 1.5,
360
+ "$type": "number"
361
+ } },
362
+ "letterSpacing": { "base": {
363
+ "$value": {
364
+ "value": 0,
365
+ "unit": "px"
366
+ },
367
+ "$type": "dimension"
368
+ } }
369
+ },
370
+ "motion": {
371
+ "duration": { "base": {
372
+ "$value": {
373
+ "value": 200,
374
+ "unit": "ms"
375
+ },
376
+ "$type": "duration"
377
+ } },
378
+ "easing": { "standard": {
379
+ "$value": [
380
+ .16,
381
+ 1,
382
+ .3,
383
+ 1
384
+ ],
385
+ "$type": "cubicBezier"
386
+ } }
387
+ },
388
+ "effect": { "shadow": { "base": {
389
+ "$value": {
390
+ "color": {
391
+ "colorSpace": "srgb",
392
+ "components": [
393
+ 0,
394
+ 0,
395
+ 0
396
+ ],
397
+ "alpha": .08
398
+ },
399
+ "offsetX": {
400
+ "value": 0,
401
+ "unit": "px"
402
+ },
403
+ "offsetY": {
404
+ "value": 2,
405
+ "unit": "px"
406
+ },
407
+ "blur": {
408
+ "value": 4,
409
+ "unit": "px"
410
+ },
411
+ "spread": {
412
+ "value": 0,
413
+ "unit": "px"
414
+ }
415
+ },
416
+ "$type": "shadow"
417
+ } } }
418
+ } };
419
+ //#endregion
420
+ //#region src/color-architecture.ts
421
+ const FIGMA_COLOR_COLLECTION = "Color";
422
+ const FIGMA_COLOR_PREFIX = "qw";
423
+ const T1_COLOR_KEYS = [
424
+ "brand",
425
+ "black",
426
+ "white",
427
+ "gray",
428
+ "red",
429
+ "orange",
430
+ "yellow",
431
+ "green",
432
+ "cyan",
433
+ "blue",
434
+ "purple",
435
+ "brown"
436
+ ];
437
+ const FIGMA_T1_SEED_HEX = {
438
+ brand: "#0011FF",
439
+ black: "#222222",
440
+ white: "#FFFFFF",
441
+ gray: "#999999",
442
+ red: "#FF4040",
443
+ orange: "#FF6B3F",
444
+ yellow: "#FFD575",
445
+ green: "#1F993F",
446
+ cyan: "#29BECC",
447
+ blue: "#0D53FF",
448
+ purple: "#432CDB",
449
+ brown: "#B45E06"
96
450
  };
97
-
98
- // src/color-architecture.ts
99
- var FIGMA_COLOR_COLLECTION = "Color";
100
- var FIGMA_COLOR_PREFIX = "qw";
101
- var T1_COLOR_KEYS = [
102
- "brand",
103
- "black",
104
- "white",
105
- "gray",
106
- "red",
107
- "orange",
108
- "yellow",
109
- "green",
110
- "cyan",
111
- "blue",
112
- "purple",
113
- "brown"
451
+ const T2_ALPHA_STEPS = [
452
+ 5,
453
+ 10,
454
+ 20,
455
+ 30,
456
+ 40,
457
+ 50,
458
+ 60,
459
+ 70,
460
+ 80,
461
+ 90,
462
+ 100
114
463
  ];
115
- var FIGMA_T1_SEED_HEX = {
116
- brand: "#0011FF",
117
- black: "#222222",
118
- white: "#FFFFFF",
119
- gray: "#999999",
120
- red: "#FF4040",
121
- orange: "#FF6B3F",
122
- yellow: "#FFD575",
123
- green: "#1F993F",
124
- cyan: "#29BECC",
125
- blue: "#0D53FF",
126
- purple: "#432CDB",
127
- brown: "#B45E06"
464
+ const T2_ALPHA_KEYS = T2_ALPHA_STEPS.map((step) => `a${step}`);
465
+ const T2_OKLCH_STEPS = [
466
+ 50,
467
+ 100,
468
+ 200,
469
+ 300,
470
+ 400,
471
+ 500,
472
+ 600,
473
+ 700,
474
+ 800,
475
+ 900
476
+ ];
477
+ const FIGMA_T2_SOLID_SCALE = {
478
+ brand: {
479
+ 50: "#F2F6FF",
480
+ 100: "#E6ECFF",
481
+ 200: "#C4D8FF",
482
+ 300: "#9DBDFF",
483
+ 400: "#6C9AFF",
484
+ 500: "#426EFF",
485
+ 600: "#1B57FF",
486
+ 700: "#002CE8",
487
+ 800: "#001A99",
488
+ 900: "#000540"
489
+ },
490
+ black: {
491
+ 50: "#F6F6F6",
492
+ 100: "#EBEBEB",
493
+ 200: "#D7D7D7",
494
+ 300: "#BEBEBE",
495
+ 400: "#9E9E9E",
496
+ 500: "#868686",
497
+ 600: "#6F6F6F",
498
+ 700: "#555555",
499
+ 800: "#353535",
500
+ 900: "#121212"
501
+ },
502
+ white: {
503
+ 50: "#F6F6F6",
504
+ 100: "#EBEBEB",
505
+ 200: "#D7D7D7",
506
+ 300: "#BEBEBE",
507
+ 400: "#9E9E9E",
508
+ 500: "#868686",
509
+ 600: "#6F6F6F",
510
+ 700: "#555555",
511
+ 800: "#353535",
512
+ 900: "#121212"
513
+ },
514
+ gray: {
515
+ 50: "#F6F6F6",
516
+ 100: "#EEEEEE",
517
+ 200: "#E0E0E0",
518
+ 300: "#CCCCCC",
519
+ 400: "#A2A9B6",
520
+ 500: "#888E99",
521
+ 600: "#666666",
522
+ 700: "#3A3A3A",
523
+ 800: "#2D2D2D",
524
+ 900: "#202020"
525
+ },
526
+ red: {
527
+ 50: "#FFF3F1",
528
+ 100: "#FFE7E4",
529
+ 200: "#FFC8C2",
530
+ 300: "#FFA097",
531
+ 400: "#FF5745",
532
+ 500: "#FF4D4D",
533
+ 600: "#CF081E",
534
+ 700: "#A30013",
535
+ 800: "#6A0009",
536
+ 900: "#2A0001"
537
+ },
538
+ orange: {
539
+ 50: "#FFF3EF",
540
+ 100: "#FFE5DD",
541
+ 200: "#FFC9B9",
542
+ 300: "#FFA287",
543
+ 400: "#F76F47",
544
+ 500: "#FF764D",
545
+ 600: "#C03800",
546
+ 700: "#962A00",
547
+ 800: "#611800",
548
+ 900: "#260500"
549
+ },
550
+ yellow: {
551
+ 50: "#FFF5DF",
552
+ 100: "#FFE9B9",
553
+ 200: "#F4D38B",
554
+ 300: "#DCB96B",
555
+ 400: "#BE9940",
556
+ 500: "#A78013",
557
+ 600: "#8B6900",
558
+ 700: "#6C5100",
559
+ 800: "#453200",
560
+ 900: "#191000"
561
+ },
562
+ green: {
563
+ 50: "#E2FFE4",
564
+ 100: "#BCFFC3",
565
+ 200: "#9BEFA5",
566
+ 300: "#7BD688",
567
+ 400: "#52B764",
568
+ 500: "#2C9F47",
569
+ 600: "#068631",
570
+ 700: "#006823",
571
+ 800: "#004213",
572
+ 900: "#001704"
573
+ },
574
+ cyan: {
575
+ 50: "#E0FCFF",
576
+ 100: "#BAF8FF",
577
+ 200: "#91EAF2",
578
+ 300: "#65D0DC",
579
+ 400: "#2FB2BE",
580
+ 500: "#0098A4",
581
+ 600: "#007E88",
582
+ 700: "#006169",
583
+ 800: "#003D43",
584
+ 900: "#001518"
585
+ },
586
+ blue: {
587
+ 50: "#EBF5FF",
588
+ 100: "#E7F1FF",
589
+ 200: "#D6EBFF",
590
+ 300: "#9CBEFF",
591
+ 400: "#3086FF",
592
+ 500: "#1677FF",
593
+ 600: "#266FFF",
594
+ 700: "#003DB8",
595
+ 800: "#001D66",
596
+ 900: "#000A3A"
597
+ },
598
+ purple: {
599
+ 50: "#F3F5FF",
600
+ 100: "#E6EAFF",
601
+ 200: "#CDD5FF",
602
+ 300: "#A699FF",
603
+ 400: "#8692FF",
604
+ 500: "#6B71FF",
605
+ 600: "#5550F2",
606
+ 700: "#4135C9",
607
+ 800: "#280B96",
608
+ 900: "#0C0041"
609
+ },
610
+ brown: {
611
+ 50: "#FFF3EC",
612
+ 100: "#FFE6D5",
613
+ 200: "#FFCBA8",
614
+ 300: "#F8A971",
615
+ 400: "#DA8749",
616
+ 500: "#C36D24",
617
+ 600: "#A75600",
618
+ 700: "#824100",
619
+ 800: "#542800",
620
+ 900: "#200B00"
621
+ }
128
622
  };
129
- var T2_ALPHA_STEPS = [5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
130
- var T2_ALPHA_KEYS = T2_ALPHA_STEPS.map((step) => `a${step}`);
131
- var T2_OKLCH_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
132
- var FIGMA_T2_SOLID_SCALE = {
133
- brand: {
134
- 50: "#F2F6FF",
135
- 100: "#E6ECFF",
136
- 200: "#C4D8FF",
137
- 300: "#9DBDFF",
138
- 400: "#6C9AFF",
139
- 500: "#426EFF",
140
- 600: "#1B57FF",
141
- 700: "#002CE8",
142
- 800: "#001A99",
143
- 900: "#000540"
144
- },
145
- black: {
146
- 50: "#F6F6F6",
147
- 100: "#EBEBEB",
148
- 200: "#D7D7D7",
149
- 300: "#BEBEBE",
150
- 400: "#9E9E9E",
151
- 500: "#868686",
152
- 600: "#6F6F6F",
153
- 700: "#555555",
154
- 800: "#353535",
155
- 900: "#121212"
156
- },
157
- white: {
158
- 50: "#F6F6F6",
159
- 100: "#EBEBEB",
160
- 200: "#D7D7D7",
161
- 300: "#BEBEBE",
162
- 400: "#9E9E9E",
163
- 500: "#868686",
164
- 600: "#6F6F6F",
165
- 700: "#555555",
166
- 800: "#353535",
167
- 900: "#121212"
168
- },
169
- gray: {
170
- 50: "#F6F6F6",
171
- 100: "#EEEEEE",
172
- 200: "#E0E0E0",
173
- 300: "#CCCCCC",
174
- 400: "#A2A9B6",
175
- 500: "#888E99",
176
- 600: "#666666",
177
- 700: "#3A3A3A",
178
- 800: "#2D2D2D",
179
- 900: "#202020"
180
- },
181
- red: {
182
- 50: "#FFF3F1",
183
- 100: "#FFE7E4",
184
- 200: "#FFC8C2",
185
- 300: "#FFA097",
186
- 400: "#FF5745",
187
- 500: "#FF4D4D",
188
- 600: "#CF081E",
189
- 700: "#A30013",
190
- 800: "#6A0009",
191
- 900: "#2A0001"
192
- },
193
- orange: {
194
- 50: "#FFF3EF",
195
- 100: "#FFE5DD",
196
- 200: "#FFC9B9",
197
- 300: "#FFA287",
198
- 400: "#F76F47",
199
- 500: "#FF764D",
200
- 600: "#C03800",
201
- 700: "#962A00",
202
- 800: "#611800",
203
- 900: "#260500"
204
- },
205
- yellow: {
206
- 50: "#FFF5DF",
207
- 100: "#FFE9B9",
208
- 200: "#F4D38B",
209
- 300: "#DCB96B",
210
- 400: "#BE9940",
211
- 500: "#A78013",
212
- 600: "#8B6900",
213
- 700: "#6C5100",
214
- 800: "#453200",
215
- 900: "#191000"
216
- },
217
- green: {
218
- 50: "#E2FFE4",
219
- 100: "#BCFFC3",
220
- 200: "#9BEFA5",
221
- 300: "#7BD688",
222
- 400: "#52B764",
223
- 500: "#2C9F47",
224
- 600: "#068631",
225
- 700: "#006823",
226
- 800: "#004213",
227
- 900: "#001704"
228
- },
229
- cyan: {
230
- 50: "#E0FCFF",
231
- 100: "#BAF8FF",
232
- 200: "#91EAF2",
233
- 300: "#65D0DC",
234
- 400: "#2FB2BE",
235
- 500: "#0098A4",
236
- 600: "#007E88",
237
- 700: "#006169",
238
- 800: "#003D43",
239
- 900: "#001518"
240
- },
241
- blue: {
242
- 50: "#EBF5FF",
243
- 100: "#E7F1FF",
244
- 200: "#D6EBFF",
245
- 300: "#9CBEFF",
246
- 400: "#3086FF",
247
- 500: "#1677FF",
248
- 600: "#266FFF",
249
- 700: "#003DB8",
250
- 800: "#001D66",
251
- 900: "#000A3A"
252
- },
253
- purple: {
254
- 50: "#F3F5FF",
255
- 100: "#E6EAFF",
256
- 200: "#CDD5FF",
257
- 300: "#A699FF",
258
- 400: "#8692FF",
259
- 500: "#6B71FF",
260
- 600: "#5550F2",
261
- 700: "#4135C9",
262
- 800: "#280B96",
263
- 900: "#0C0041"
264
- },
265
- brown: {
266
- 50: "#FFF3EC",
267
- 100: "#FFE6D5",
268
- 200: "#FFCBA8",
269
- 300: "#F8A971",
270
- 400: "#DA8749",
271
- 500: "#C36D24",
272
- 600: "#A75600",
273
- 700: "#824100",
274
- 800: "#542800",
275
- 900: "#200B00"
276
- }
623
+ const FIGMA_T3_COLOR_ROLES = {
624
+ "surface.base": {
625
+ light: "color.white.base",
626
+ dark: "color.black.base"
627
+ },
628
+ "surface.subtle": {
629
+ light: "color.gray.50",
630
+ dark: "color.gray.900"
631
+ },
632
+ "surface.elevated": {
633
+ light: "color.white.base",
634
+ dark: "color.gray.800"
635
+ },
636
+ "surface.header": {
637
+ light: "color.gray.100",
638
+ dark: "color.gray.700"
639
+ },
640
+ "surface.brand-subtle": {
641
+ light: "color.blue.50",
642
+ dark: "color.blue.800"
643
+ },
644
+ "content.primary": {
645
+ light: "color.black.base",
646
+ dark: "color.white.base"
647
+ },
648
+ "content.secondary": {
649
+ light: "color.gray.600",
650
+ dark: "color.gray.base"
651
+ },
652
+ "content.tertiary": {
653
+ light: "color.gray.base",
654
+ dark: "color.gray.600"
655
+ },
656
+ "content.placeholder": {
657
+ light: "color.gray.300",
658
+ dark: "color.white.a30"
659
+ },
660
+ "content.intro": {
661
+ light: "color.gray.400",
662
+ dark: "color.gray.500"
663
+ },
664
+ "border.default": {
665
+ light: "color.gray.100",
666
+ dark: "color.gray.700"
667
+ },
668
+ "border.strong": {
669
+ light: "color.gray.300",
670
+ dark: "color.gray.600"
671
+ },
672
+ "border.focus": {
673
+ light: "color.brand.base",
674
+ dark: "color.brand.500"
675
+ },
676
+ "fill.subtle": {
677
+ light: "color.gray.50",
678
+ dark: "color.white.a20"
679
+ },
680
+ "fill.hover": {
681
+ light: "color.gray.100",
682
+ dark: "color.gray.800"
683
+ },
684
+ "fill.active": {
685
+ light: "color.blue.50",
686
+ dark: "color.blue.800"
687
+ },
688
+ "fill.disabled": {
689
+ light: "color.gray.50",
690
+ dark: "color.gray.900"
691
+ },
692
+ "action.brand": {
693
+ light: "color.brand.base",
694
+ dark: "color.brand.500"
695
+ },
696
+ "action.product": {
697
+ light: "color.blue.base",
698
+ dark: "color.blue.600"
699
+ },
700
+ "action.selected": {
701
+ light: "color.blue.a20",
702
+ dark: "color.blue.a40"
703
+ },
704
+ "action.inverse": {
705
+ light: "color.white.base",
706
+ dark: "color.black.base"
707
+ },
708
+ "status.success": {
709
+ light: "color.green.base",
710
+ dark: "color.green.base"
711
+ },
712
+ "status.warning": {
713
+ light: "color.yellow.base",
714
+ dark: "color.yellow.base"
715
+ },
716
+ "status.danger": {
717
+ light: "color.red.400",
718
+ dark: "color.red.400"
719
+ },
720
+ "status.info": {
721
+ light: "color.blue.base",
722
+ dark: "color.blue.600"
723
+ },
724
+ "overlay.scrim": {
725
+ light: "color.black.a50",
726
+ dark: "color.black.a70"
727
+ },
728
+ "overlay.guide": {
729
+ light: "color.black.a70",
730
+ dark: "color.black.a70"
731
+ }
277
732
  };
278
- var FIGMA_T3_COLOR_ROLES = {
279
- "surface.base": { light: "color.white.base", dark: "color.black.base" },
280
- "surface.subtle": { light: "color.gray.50", dark: "color.gray.900" },
281
- "surface.elevated": { light: "color.white.base", dark: "color.gray.800" },
282
- "surface.header": { light: "color.gray.100", dark: "color.gray.700" },
283
- "surface.brand-subtle": { light: "color.blue.50", dark: "color.blue.800" },
284
- "content.primary": { light: "color.black.base", dark: "color.white.base" },
285
- "content.secondary": { light: "color.gray.600", dark: "color.gray.base" },
286
- "content.tertiary": { light: "color.gray.base", dark: "color.gray.600" },
287
- "content.placeholder": { light: "color.gray.300", dark: "color.white.a30" },
288
- "content.intro": { light: "color.gray.400", dark: "color.gray.500" },
289
- "border.default": { light: "color.gray.100", dark: "color.gray.700" },
290
- "border.strong": { light: "color.gray.300", dark: "color.gray.600" },
291
- "border.focus": { light: "color.brand.base", dark: "color.brand.500" },
292
- "fill.subtle": { light: "color.gray.50", dark: "color.white.a20" },
293
- "fill.hover": { light: "color.gray.100", dark: "color.gray.800" },
294
- "fill.active": { light: "color.blue.50", dark: "color.blue.800" },
295
- "fill.disabled": { light: "color.gray.50", dark: "color.gray.900" },
296
- "action.brand": { light: "color.brand.base", dark: "color.brand.500" },
297
- "action.product": { light: "color.blue.base", dark: "color.blue.600" },
298
- "action.selected": { light: "color.blue.a20", dark: "color.blue.a40" },
299
- "action.inverse": { light: "color.white.base", dark: "color.black.base" },
300
- "status.success": { light: "color.green.base", dark: "color.green.base" },
301
- "status.warning": { light: "color.yellow.base", dark: "color.yellow.base" },
302
- "status.danger": { light: "color.red.400", dark: "color.red.400" },
303
- "status.info": { light: "color.blue.base", dark: "color.blue.600" },
304
- "overlay.scrim": { light: "color.black.a50", dark: "color.black.a70" },
305
- "overlay.guide": { light: "color.black.a70", dark: "color.black.a70" }
733
+ //#endregion
734
+ //#region src/seed/index.ts
735
+ /**
736
+ * 把设计交付的 DTCG seed (嵌套 $value,五大类目) 拍平成 derive() 可消费的
737
+ * Seed 形状。DTCG 是设计/Figma 的事实来源;这里是工程读取它的唯一入口。
738
+ * 数值与 packages/tokens/design/seed.tokens.json 逐条一致(parity 测试保证不漂移)。
739
+ */
740
+ const s = qw.seed;
741
+ const px = (d) => `${d.value}${d.unit}`;
742
+ const color = (v) => v;
743
+ const defaultSeed = {
744
+ color: Object.fromEntries(T1_COLOR_KEYS.map((key) => [key, color(s.color[key].$value)])),
745
+ size: {
746
+ spacing: { step: px(s.size.spacing.step.$value) },
747
+ borderWidth: { hairline: px(s.size.borderWidth.hairline.$value) },
748
+ radius: {
749
+ base: px(s.size.radius.base.$value),
750
+ full: px(s.size.radius.full.$value)
751
+ },
752
+ controlSize: { base: px(s.size.controlSize.base.$value) },
753
+ iconSize: { base: px(s.size.iconSize.base.$value) },
754
+ opacity: {
755
+ disabled: s.size.opacity.disabled.$value,
756
+ alphaStep: s.size.opacity.alphaStep.$value
757
+ },
758
+ zIndex: { step: s.size.zIndex.step.$value }
759
+ },
760
+ typography: {
761
+ fontFamily: {
762
+ text: s.typography.fontFamily.text.$value,
763
+ number: s.typography.fontFamily.number.$value,
764
+ code: s.typography.fontFamily.code.$value,
765
+ textMacLatin: s.typography.fontFamily.textMacLatin.$value,
766
+ textMacZh: s.typography.fontFamily.textMacZh.$value,
767
+ textMacJa: s.typography.fontFamily.textMacJa.$value,
768
+ textWindowsLatin: s.typography.fontFamily.textWindowsLatin.$value,
769
+ textWindowsZh: s.typography.fontFamily.textWindowsZh.$value,
770
+ textWindowsJa: s.typography.fontFamily.textWindowsJa.$value
771
+ },
772
+ fontWeight: {
773
+ regular: s.typography.fontWeight.regular.$value,
774
+ medium: s.typography.fontWeight.medium.$value,
775
+ semibold: s.typography.fontWeight.semibold.$value
776
+ },
777
+ fontSize: { base: px(s.typography.fontSize.base.$value) },
778
+ lineHeight: { base: s.typography.lineHeight.base.$value },
779
+ letterSpacing: { base: px(s.typography.letterSpacing.base.$value) }
780
+ },
781
+ motion: {
782
+ duration: { base: px(s.motion.duration.base.$value) },
783
+ easing: { standard: s.motion.easing.standard.$value }
784
+ },
785
+ effect: { shadow: { base: {
786
+ color: color(s.effect.shadow.base.$value.color),
787
+ offsetX: px(s.effect.shadow.base.$value.offsetX),
788
+ offsetY: px(s.effect.shadow.base.$value.offsetY),
789
+ blur: px(s.effect.shadow.base.$value.blur),
790
+ spread: px(s.effect.shadow.base.$value.spread)
791
+ } } }
306
792
  };
307
-
308
- // src/seed/index.ts
309
- var s = seed_default.qw.seed;
310
- var px = (d) => `${d.value}${d.unit}`;
311
- var color = (v) => v;
312
- var defaultSeed = {
313
- color: Object.fromEntries(T1_COLOR_KEYS.map((key) => [key, color(s.color[key].$value)])),
314
- size: {
315
- spacing: {
316
- step: px(s.size.spacing.step.$value)
317
- },
318
- borderWidth: {
319
- hairline: px(s.size.borderWidth.hairline.$value)
320
- },
321
- radius: {
322
- base: px(s.size.radius.base.$value),
323
- full: px(s.size.radius.full.$value)
324
- },
325
- controlSize: {
326
- base: px(s.size.controlSize.base.$value)
327
- },
328
- iconSize: {
329
- base: px(s.size.iconSize.base.$value)
330
- },
331
- opacity: {
332
- disabled: s.size.opacity.disabled.$value,
333
- alphaStep: s.size.opacity.alphaStep.$value
334
- },
335
- zIndex: {
336
- step: s.size.zIndex.step.$value
337
- }
338
- },
339
- typography: {
340
- fontFamily: {
341
- text: s.typography.fontFamily.text.$value,
342
- number: s.typography.fontFamily.number.$value,
343
- code: s.typography.fontFamily.code.$value,
344
- textMacLatin: s.typography.fontFamily.textMacLatin.$value,
345
- textMacZh: s.typography.fontFamily.textMacZh.$value,
346
- textMacJa: s.typography.fontFamily.textMacJa.$value,
347
- textWindowsLatin: s.typography.fontFamily.textWindowsLatin.$value,
348
- textWindowsZh: s.typography.fontFamily.textWindowsZh.$value,
349
- textWindowsJa: s.typography.fontFamily.textWindowsJa.$value
350
- },
351
- fontWeight: {
352
- regular: s.typography.fontWeight.regular.$value,
353
- medium: s.typography.fontWeight.medium.$value,
354
- semibold: s.typography.fontWeight.semibold.$value
355
- },
356
- fontSize: {
357
- base: px(s.typography.fontSize.base.$value)
358
- },
359
- lineHeight: {
360
- base: s.typography.lineHeight.base.$value
361
- },
362
- letterSpacing: {
363
- base: px(s.typography.letterSpacing.base.$value)
364
- }
365
- },
366
- motion: {
367
- duration: {
368
- base: px(s.motion.duration.base.$value)
369
- },
370
- easing: {
371
- standard: s.motion.easing.standard.$value
372
- }
373
- },
374
- effect: {
375
- shadow: {
376
- base: {
377
- color: color(s.effect.shadow.base.$value.color),
378
- offsetX: px(s.effect.shadow.base.$value.offsetX),
379
- offsetY: px(s.effect.shadow.base.$value.offsetY),
380
- blur: px(s.effect.shadow.base.$value.blur),
381
- spread: px(s.effect.shadow.base.$value.spread)
382
- }
383
- }
384
- }
385
- };
386
- var seedColor = z.object({
387
- colorSpace: z.enum(["srgb", "oklch"]),
388
- components: z.tuple([z.number(), z.number(), z.number()]),
389
- alpha: z.number().min(0).max(1)
793
+ //#endregion
794
+ //#region src/schema.ts
795
+ /**
796
+ * seedColor — DTCG Color module 结构化颜色(不再接受十六进制字符串)。
797
+ * - srgb:components 各分量 0–1(写 0–255 会被拒绝,这是常见错误)。
798
+ * - oklch:l 0–1、c ≥0、h 为角度;用于"设计师直接以感知空间拍板"的输入。
799
+ */
800
+ const seedColor = z.object({
801
+ colorSpace: z.enum(["srgb", "oklch"]),
802
+ components: z.tuple([
803
+ z.number(),
804
+ z.number(),
805
+ z.number()
806
+ ]),
807
+ alpha: z.number().min(0).max(1)
390
808
  }).superRefine((c, ctx) => {
391
- if (c.colorSpace === "srgb") {
392
- if (c.components.some((v) => v < 0 || v > 1)) {
393
- ctx.addIssue({
394
- code: "custom",
395
- message: "srgb components \u5FC5\u987B\u662F 0\u20131\uFF08\u4E0D\u662F 0\u2013255\uFF09\uFF0C\u4F8B\u5982 #0011ff \u2192 [0, 0.0667, 1]"
396
- });
397
- }
398
- } else {
399
- const [l, chroma] = c.components;
400
- if (l < 0 || l > 1 || chroma < 0) {
401
- ctx.addIssue({ code: "custom", message: "oklch \u8981\u6C42 l \u2208 [0,1] \u4E14 c \u2265 0" });
402
- }
403
- }
809
+ if (c.colorSpace === "srgb") {
810
+ if (c.components.some((v) => v < 0 || v > 1)) ctx.addIssue({
811
+ code: "custom",
812
+ message: "srgb components 必须是 0–1(不是 0–255),例如 #0011ff → [0, 0.0667, 1]"
813
+ });
814
+ } else {
815
+ const [l, chroma] = c.components;
816
+ if (l < 0 || l > 1 || chroma < 0) ctx.addIssue({
817
+ code: "custom",
818
+ message: "oklch 要求 l ∈ [0,1] 且 c ≥ 0"
819
+ });
820
+ }
404
821
  });
405
- var cubicBezier = z.tuple([z.number(), z.number(), z.number(), z.number()]);
406
- var fontStack = z.array(z.string()).min(1);
407
- var colorSeedShape = Object.fromEntries(T1_COLOR_KEYS.map((key) => [key, seedColor]));
408
- var SeedSchema = z.object({
409
- color: z.object(colorSeedShape),
410
- size: z.object({
411
- spacing: z.object({
412
- step: z.string()
413
- }),
414
- borderWidth: z.object({
415
- hairline: z.string()
416
- }),
417
- radius: z.object({
418
- /** 控件基准圆角;Reference 派生 sm=×0.5 / md=×1 / lg=×1.75。 */
419
- base: z.string(),
420
- /** 药丸/圆形裁切基准(独立设计决策,不从 base 派生)。 */
421
- full: z.string()
422
- }),
423
- controlSize: z.object({
424
- /** md 控件高度锚点;sm/lg = base ∓ spacing.step × 2。 */
425
- base: z.string()
426
- }),
427
- iconSize: z.object({
428
- /** md 图标尺寸锚点;sm/lg = base ∓ spacing.step。 */
429
- base: z.string()
430
- }),
431
- opacity: z.object({
432
- disabled: z.number().min(0).max(1),
433
- alphaStep: z.number().min(0).max(1)
434
- }),
435
- zIndex: z.object({
436
- step: z.number().int().positive()
437
- })
438
- }),
439
- typography: z.object({
440
- fontFamily: z.object({
441
- /** 通用正文栈(跨平台缺省)。 */
442
- text: fontStack,
443
- /** 数字/数据展示栈。 */
444
- number: fontStack,
445
- /** 代码等宽栈。 */
446
- code: fontStack,
447
- /** 平台/语言细分变体,供上层按 UA/lang 重映射 text 角色。 */
448
- textMacLatin: fontStack,
449
- textMacZh: fontStack,
450
- textMacJa: fontStack,
451
- textWindowsLatin: fontStack,
452
- textWindowsZh: fontStack,
453
- textWindowsJa: fontStack
454
- }),
455
- fontWeight: z.object({
456
- regular: z.number(),
457
- medium: z.number(),
458
- semibold: z.number()
459
- }),
460
- fontSize: z.object({
461
- base: z.string()
462
- }),
463
- lineHeight: z.object({
464
- base: z.number().positive()
465
- }),
466
- letterSpacing: z.object({
467
- base: z.string()
468
- })
469
- }),
470
- motion: z.object({
471
- duration: z.object({
472
- base: z.string()
473
- }),
474
- easing: z.object({
475
- standard: cubicBezier
476
- })
477
- }),
478
- effect: z.object({
479
- shadow: z.object({
480
- base: z.object({
481
- color: seedColor,
482
- offsetX: z.string(),
483
- offsetY: z.string(),
484
- blur: z.string(),
485
- spread: z.string()
486
- })
487
- })
488
- })
822
+ const cubicBezier = z.tuple([
823
+ z.number(),
824
+ z.number(),
825
+ z.number(),
826
+ z.number()
827
+ ]);
828
+ const fontStack = z.array(z.string()).min(1);
829
+ const colorSeedShape = Object.fromEntries(T1_COLOR_KEYS.map((key) => [key, seedColor]));
830
+ /**
831
+ * SeedSchema — T1 Seed 的唯一事实源校验(RFC 0001 §2.1,v3 修订)。
832
+ *
833
+ * 五大类目 color/size/typography/motion/effect,字段结构对齐
834
+ * packages/tokens/design/seed.tokens.json,全部字段必填——不允许省略后静默
835
+ * 回退默认值,锚点缺口必须在 Seed 层显式补齐,Reference 层不得用硬编码兜底。
836
+ *
837
+ * v3 变化:
838
+ * - 颜色全部结构化(DTCG Color module,支持 srgb/oklch)。
839
+ * - radius/controlSize/iconSize 只留 base 锚点(full 是独立的裁切基准决策),
840
+ * sm/lg 由 Reference 层按既定公式派生。
841
+ * - lineHeight 从 size 移入 typography;新增 letterSpacing.base 锚点。
842
+ * - fontFamily 换 text/number/code 三族 + 平台/语言变体(textMacXx、textWindowsXx)。
843
+ */
844
+ const SeedSchema = z.object({
845
+ color: z.object(colorSeedShape),
846
+ size: z.object({
847
+ spacing: z.object({ step: z.string() }),
848
+ borderWidth: z.object({ hairline: z.string() }),
849
+ radius: z.object({
850
+ /** 控件基准圆角;Reference 派生 sm=×0.5 / md=×1 / lg=×1.75。 */
851
+ base: z.string(),
852
+ /** 药丸/圆形裁切基准(独立设计决策,不从 base 派生)。 */
853
+ full: z.string()
854
+ }),
855
+ controlSize: z.object({
856
+ /** md 控件高度锚点;sm/lg = base ∓ spacing.step × 2。 */
857
+ base: z.string() }),
858
+ iconSize: z.object({
859
+ /** md 图标尺寸锚点;sm/lg = base ∓ spacing.step。 */
860
+ base: z.string() }),
861
+ opacity: z.object({
862
+ disabled: z.number().min(0).max(1),
863
+ alphaStep: z.number().min(0).max(1)
864
+ }),
865
+ zIndex: z.object({ step: z.number().int().positive() })
866
+ }),
867
+ typography: z.object({
868
+ fontFamily: z.object({
869
+ /** 通用正文栈(跨平台缺省)。 */
870
+ text: fontStack,
871
+ /** 数字/数据展示栈。 */
872
+ number: fontStack,
873
+ /** 代码等宽栈。 */
874
+ code: fontStack,
875
+ /** 平台/语言细分变体,供上层按 UA/lang 重映射 text 角色。 */
876
+ textMacLatin: fontStack,
877
+ textMacZh: fontStack,
878
+ textMacJa: fontStack,
879
+ textWindowsLatin: fontStack,
880
+ textWindowsZh: fontStack,
881
+ textWindowsJa: fontStack
882
+ }),
883
+ fontWeight: z.object({
884
+ regular: z.number(),
885
+ medium: z.number(),
886
+ semibold: z.number()
887
+ }),
888
+ fontSize: z.object({ base: z.string() }),
889
+ lineHeight: z.object({ base: z.number().positive() }),
890
+ letterSpacing: z.object({ base: z.string() })
891
+ }),
892
+ motion: z.object({
893
+ duration: z.object({ base: z.string() }),
894
+ easing: z.object({ standard: cubicBezier })
895
+ }),
896
+ effect: z.object({ shadow: z.object({ base: z.object({
897
+ color: seedColor,
898
+ offsetX: z.string(),
899
+ offsetY: z.string(),
900
+ blur: z.string(),
901
+ spread: z.string()
902
+ }) }) })
489
903
  });
904
+ /**
905
+ * validateSeed — ConfigProvider 在 dev 模式调用;生产环境可裁掉,不带 zod 体积。
906
+ * 解析失败抛错,错误信息直接定位到字段。
907
+ */
490
908
  function validateSeed(seed) {
491
- const r = SeedSchema.safeParse(seed);
492
- if (!r.success) {
493
- console.error("[qw] invalid seed:", r.error.format());
494
- throw new Error("invalid seed");
495
- }
496
- return r.data;
909
+ const r = SeedSchema.safeParse(seed);
910
+ if (!r.success) {
911
+ console.error("[qw] invalid seed:", r.error.format());
912
+ throw new Error("invalid seed");
913
+ }
914
+ return r.data;
497
915
  }
916
+ //#endregion
917
+ //#region src/color.ts
918
+ /** SeedColorValue → culori 可消费的颜色对象。 */
498
919
  function toCulori(c) {
499
- if (c.colorSpace === "srgb") {
500
- return { mode: "rgb", r: c.components[0], g: c.components[1], b: c.components[2], alpha: c.alpha };
501
- }
502
- return { mode: "oklch", l: c.components[0], c: c.components[1], h: c.components[2], alpha: c.alpha };
920
+ if (c.colorSpace === "srgb") return {
921
+ mode: "rgb",
922
+ r: c.components[0],
923
+ g: c.components[1],
924
+ b: c.components[2],
925
+ alpha: c.alpha
926
+ };
927
+ return {
928
+ mode: "oklch",
929
+ l: c.components[0],
930
+ c: c.components[1],
931
+ h: c.components[2],
932
+ alpha: c.alpha
933
+ };
503
934
  }
504
- var round6 = (n) => Math.round(n * 1e6) / 1e6;
935
+ const round6 = (n) => Math.round(n * 1e6) / 1e6;
936
+ /** 十六进制(#rgb / #rrggbb)→ 结构化 srgb SeedColorValue。编辑器色板输入的桥。 */
505
937
  function hexToSeedColor(hex) {
506
- const parsed = rgb(parse(hex));
507
- if (!parsed) throw new Error(`[qw] \u65E0\u6CD5\u89E3\u6790\u989C\u8272: ${hex}`);
508
- return {
509
- colorSpace: "srgb",
510
- components: [round6(parsed.r), round6(parsed.g), round6(parsed.b)],
511
- alpha: parsed.alpha ?? 1
512
- };
938
+ const parsed = rgb(parse(hex));
939
+ if (!parsed) throw new Error(`[qw] 无法解析颜色: ${hex}`);
940
+ return {
941
+ colorSpace: "srgb",
942
+ components: [
943
+ round6(parsed.r),
944
+ round6(parsed.g),
945
+ round6(parsed.b)
946
+ ],
947
+ alpha: parsed.alpha ?? 1
948
+ };
513
949
  }
950
+ /** 结构化颜色 → #rrggbb(丢 alpha;oklch 会先转 sRGB)。展示/对比度计算用。 */
514
951
  function seedColorToHex(c) {
515
- const converted = rgb(toCulori(c));
516
- if (!converted) throw new Error(`[qw] \u65E0\u6CD5\u8F6C\u6362\u989C\u8272: ${JSON.stringify(c)}`);
517
- return formatHex(converted) ?? "#000000";
952
+ const converted = rgb(toCulori(c));
953
+ if (!converted) throw new Error(`[qw] 无法转换颜色: ${JSON.stringify(c)}`);
954
+ return formatHex(converted) ?? "#000000";
518
955
  }
956
+ /**
957
+ * 结构化颜色 → CSS 颜色字符串。srgb 且不透明输出 hex;srgb 半透明输出
958
+ * `rgb(r g b / a)`;oklch 保持原生 `oklch(l c h / a)`(relative color 派生保真)。
959
+ */
519
960
  function seedColorToCss(c) {
520
- if (c.colorSpace === "oklch") {
521
- const [l, ch, h] = c.components;
522
- return c.alpha >= 1 ? `oklch(${l} ${ch} ${h})` : `oklch(${l} ${ch} ${h} / ${c.alpha})`;
523
- }
524
- if (c.alpha >= 1) return seedColorToHex(c);
525
- const [r, g, b] = c.components.map((v) => Math.round(v * 255));
526
- return `rgb(${r} ${g} ${b} / ${c.alpha})`;
961
+ if (c.colorSpace === "oklch") {
962
+ const [l, ch, h] = c.components;
963
+ return c.alpha >= 1 ? `oklch(${l} ${ch} ${h})` : `oklch(${l} ${ch} ${h} / ${c.alpha})`;
964
+ }
965
+ if (c.alpha >= 1) return seedColorToHex(c);
966
+ const [r, g, b] = c.components.map((v) => Math.round(v * 255));
967
+ return `rgb(${r} ${g} ${b} / ${c.alpha})`;
527
968
  }
528
- var STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
529
- var L_TARGETS = {
530
- 50: 0.972,
531
- 100: 0.94,
532
- 200: 0.88,
533
- 300: 0.8,
534
- 400: 0.7,
535
- 500: 0.62,
536
- 600: 0.54,
537
- 700: 0.45,
538
- 800: 0.33,
539
- 900: 0.18
969
+ //#endregion
970
+ //#region src/derive/oklch.ts
971
+ /** 10 档 token: 50 / 100 / 200 / ... / 900 */
972
+ const STEPS = [
973
+ 50,
974
+ 100,
975
+ 200,
976
+ 300,
977
+ 400,
978
+ 500,
979
+ 600,
980
+ 700,
981
+ 800,
982
+ 900
983
+ ];
984
+ /**
985
+ * OKLCH L 目标曲线 — 感知均匀的亮度阶梯 (0..1)。
986
+ * 50 最亮 → 900 最暗;端点 0.972 / 0.18 避免纯白纯黑导致对比"塌"。
987
+ * (替换原 HSV:HSV 在感知上不均匀,深档发灰/浅档发白,WCAG 易不过。)
988
+ *
989
+ * 导出供 live-css 生成器复用:CSS relative color 语法
990
+ * `oklch(from var(--seed) L calc(c * f) h)` 的 L/f 常量与这里同源。
991
+ */
992
+ const L_TARGETS = {
993
+ 50: .972,
994
+ 100: .94,
995
+ 200: .88,
996
+ 300: .8,
997
+ 400: .7,
998
+ 500: .62,
999
+ 600: .54,
1000
+ 700: .45,
1001
+ 800: .33,
1002
+ 900: .18
540
1003
  };
1004
+ /**
1005
+ * chroma 端点衰减 — 在 L 的亮/暗两端轻微衰减 chroma,避免 out-of-gamut;
1006
+ * 中段 (L≈0.6) 保留全 chroma。
1007
+ */
541
1008
  function chromaFactor(l) {
542
- const d = Math.abs(l - 0.6) / 0.6;
543
- return 1 - 0.45 * Math.min(1, d);
1009
+ const d = Math.abs(l - .6) / .6;
1010
+ return 1 - .45 * Math.min(1, d);
544
1011
  }
545
- function ramp(seedColor2) {
546
- const input = typeof seedColor2 === "string" ? parse(seedColor2) : toCulori(seedColor2);
547
- const base = oklch(input);
548
- if (!base) throw new Error(`[qw] \u65E0\u6CD5\u89E3\u6790\u989C\u8272: ${JSON.stringify(seedColor2)}`);
549
- const baseC = base.c ?? 0;
550
- const baseH = base.h ?? 0;
551
- const out = {};
552
- for (const step of STEPS) {
553
- const l = L_TARGETS[step];
554
- const c = baseC * chromaFactor(l);
555
- const inGamut = clampChroma({ mode: "oklch", l, c, h: baseH }, "oklch");
556
- out[step] = formatHex(inGamut) ?? formatHex(base) ?? "#000000";
557
- }
558
- return out;
1012
+ /**
1013
+ * 给一个 seed 颜色(结构化 SeedColorValue 或 CSS 颜色字符串),算 10 阶 ramp。
1014
+ * L 走目标曲线,hue 固定,chroma 端点衰减后再 clampChroma 收进 sRGB 色域。
1015
+ */
1016
+ function ramp(seedColor) {
1017
+ const base = oklch(typeof seedColor === "string" ? parse(seedColor) : toCulori(seedColor));
1018
+ if (!base) throw new Error(`[qw] 无法解析颜色: ${JSON.stringify(seedColor)}`);
1019
+ const baseC = base.c ?? 0;
1020
+ const baseH = base.h ?? 0;
1021
+ const out = {};
1022
+ for (const step of STEPS) {
1023
+ const l = L_TARGETS[step];
1024
+ out[step] = formatHex(clampChroma({
1025
+ mode: "oklch",
1026
+ l,
1027
+ c: baseC * chromaFactor(l),
1028
+ h: baseH
1029
+ }, "oklch")) ?? formatHex(base) ?? "#000000";
1030
+ }
1031
+ return out;
559
1032
  }
1033
+ /** 相对亮度 (WCAG) — 用于对偶/校验时的对比度判断。 */
560
1034
  function relativeLuminance(hex) {
561
- const srgb = parse(hex);
562
- if (!srgb) return 0;
563
- const lin = (v) => v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
564
- return 0.2126 * lin(srgb.r) + 0.7152 * lin(srgb.g) + 0.0722 * lin(srgb.b);
1035
+ const srgb = parse(hex);
1036
+ if (!srgb) return 0;
1037
+ const lin = (v) => v <= .03928 ? v / 12.92 : Math.pow((v + .055) / 1.055, 2.4);
1038
+ return .2126 * lin(srgb.r) + .7152 * lin(srgb.g) + .0722 * lin(srgb.b);
565
1039
  }
1040
+ /** WCAG 对比度 (1..21)。 */
566
1041
  function contrastRatio(fg, bg) {
567
- const l1 = relativeLuminance(fg);
568
- const l2 = relativeLuminance(bg);
569
- const [hi, lo] = l1 > l2 ? [l1, l2] : [l2, l1];
570
- return (hi + 0.05) / (lo + 0.05);
1042
+ const l1 = relativeLuminance(fg);
1043
+ const l2 = relativeLuminance(bg);
1044
+ const [hi, lo] = l1 > l2 ? [l1, l2] : [l2, l1];
1045
+ return (hi + .05) / (lo + .05);
571
1046
  }
572
-
573
- // src/derive/naming.ts
1047
+ //#endregion
1048
+ //#region src/derive/naming.ts
574
1049
  function kebab(segment) {
575
- return segment.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1050
+ return segment.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
576
1051
  }
577
1052
  function toVar(layer, path) {
578
- return `--qw-${layer}-${path.map(kebab).join("-")}`;
1053
+ return `--qw-${layer}-${path.map(kebab).join("-")}`;
579
1054
  }
580
-
581
- // src/derive/reference.ts
1055
+ //#endregion
1056
+ //#region src/derive/reference.ts
1057
+ /** 按点分路径读 Seed 字段("size.spacing.step" → seed.size.spacing.step)。 */
582
1058
  function seedValueAt(seed, dotted) {
583
- let node = seed;
584
- for (const seg of dotted.split(".")) {
585
- if (node == null || typeof node !== "object") {
586
- throw new Error(`[qw] Seed \u8DEF\u5F84\u4E0D\u5B58\u5728: ${dotted}`);
587
- }
588
- node = node[seg];
589
- }
590
- if (node === void 0) throw new Error(`[qw] Seed \u8DEF\u5F84\u4E0D\u5B58\u5728: ${dotted}`);
591
- return node;
1059
+ let node = seed;
1060
+ for (const seg of dotted.split(".")) {
1061
+ if (node == null || typeof node !== "object") throw new Error(`[qw] Seed 路径不存在: ${dotted}`);
1062
+ node = node[seg];
1063
+ }
1064
+ if (node === void 0) throw new Error(`[qw] Seed 路径不存在: ${dotted}`);
1065
+ return node;
592
1066
  }
593
- function parsePx(s2) {
594
- const m = /^(-?\d+(?:\.\d+)?)(.*)$/.exec(s2.trim());
595
- if (!m) throw new Error(`[qw] \u65E0\u6CD5\u89E3\u6790\u5C3A\u5BF8: ${s2}`);
596
- return { value: Number(m[1]), unit: m[2] ?? "" };
1067
+ function parsePx(s) {
1068
+ const m = /^(-?\d+(?:\.\d+)?)(.*)$/.exec(s.trim());
1069
+ if (!m) throw new Error(`[qw] 无法解析尺寸: ${s}`);
1070
+ return {
1071
+ value: Number(m[1]),
1072
+ unit: m[2] ?? ""
1073
+ };
597
1074
  }
598
- var round2 = (n) => Math.round(n * 100) / 100;
1075
+ const round2 = (n) => Math.round(n * 100) / 100;
599
1076
  function rgba(components, alpha) {
600
- return `rgba(${components[0]}, ${components[1]}, ${components[2]}, ${round2(alpha)})`;
1077
+ return `rgba(${components[0]}, ${components[1]}, ${components[2]}, ${round2(alpha)})`;
601
1078
  }
602
1079
  function hexRgb(hex) {
603
- const m = /^#?([0-9a-f]{6})$/i.exec(hex);
604
- if (!m) throw new Error(`[qw] \u65E0\u6CD5\u89E3\u6790 hex \u989C\u8272: ${hex}`);
605
- const n = Number.parseInt(m[1], 16);
606
- return [n >> 16 & 255, n >> 8 & 255, n & 255];
1080
+ const m = /^#?([0-9a-f]{6})$/i.exec(hex);
1081
+ if (!m) throw new Error(`[qw] 无法解析 hex 颜色: ${hex}`);
1082
+ const n = Number.parseInt(m[1], 16);
1083
+ return [
1084
+ n >> 16 & 255,
1085
+ n >> 8 & 255,
1086
+ n & 255
1087
+ ];
607
1088
  }
608
- function seedAlphaCss(seedColor2, alpha) {
609
- if (alpha >= 1) return seedColorToCss({ ...seedColor2, alpha: 1 });
610
- return rgba(hexRgb(seedColorToHex(seedColor2)), alpha);
1089
+ function seedAlphaCss(seedColor, alpha) {
1090
+ if (alpha >= 1) return seedColorToCss({
1091
+ ...seedColor,
1092
+ alpha: 1
1093
+ });
1094
+ return rgba(hexRgb(seedColorToHex(seedColor)), alpha);
611
1095
  }
612
1096
  function formatFontFamily(list) {
613
- return list.map((f) => /[\s./]/.test(f) ? `'${f}'` : f).join(", ");
1097
+ return list.map((f) => /[\s./]/.test(f) ? `'${f}'` : f).join(", ");
614
1098
  }
615
1099
  function formatCubicBezier([a, b, c, d]) {
616
- return `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
1100
+ return `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
617
1101
  }
618
- var SHADOW_LEVELS = {
619
- sm: { offset: 0.5, blur: 0.5, alphaMult: 2 },
620
- md: { offset: 2, blur: 3, alphaMult: 3 },
621
- lg: { offset: 4, blur: 6, alphaMult: 4 }
1102
+ /** 阴影档位系数:形状(offset/blur/spread 缩放)+ 颜色(alphaStep 倍数)。 */
1103
+ const SHADOW_LEVELS = {
1104
+ sm: {
1105
+ offset: .5,
1106
+ blur: .5,
1107
+ alphaMult: 2
1108
+ },
1109
+ md: {
1110
+ offset: 2,
1111
+ blur: 3,
1112
+ alphaMult: 3
1113
+ },
1114
+ lg: {
1115
+ offset: 4,
1116
+ blur: 6,
1117
+ alphaMult: 4
1118
+ }
622
1119
  };
623
1120
  function shadowShapeOf(seed, level) {
624
- const { offset, blur } = SHADOW_LEVELS[level];
625
- const base = seed.effect.shadow.base;
626
- const scale = (s2, f) => {
627
- const { value, unit } = parsePx(s2);
628
- return `${round2(value * f)}${unit}`;
629
- };
630
- return {
631
- offsetX: scale(base.offsetX, offset),
632
- offsetY: scale(base.offsetY, offset),
633
- blur: scale(base.blur, blur),
634
- spread: scale(base.spread, offset)
635
- };
1121
+ const { offset, blur } = SHADOW_LEVELS[level];
1122
+ const base = seed.effect.shadow.base;
1123
+ const scale = (s, f) => {
1124
+ const { value, unit } = parsePx(s);
1125
+ return `${round2(value * f)}${unit}`;
1126
+ };
1127
+ return {
1128
+ offsetX: scale(base.offsetX, offset),
1129
+ offsetY: scale(base.offsetY, offset),
1130
+ blur: scale(base.blur, blur),
1131
+ spread: scale(base.spread, offset)
1132
+ };
636
1133
  }
1134
+ /** 求值:一条 Derivation + Seed → 最终 CSS 值字符串。 */
637
1135
  function evaluateDerivation(d, seed) {
638
- switch (d.kind) {
639
- case "alias": {
640
- const v = seedValueAt(seed, d.seedRef);
641
- if (v != null && typeof v === "object" && "colorSpace" in v) {
642
- return seedColorToCss(v);
643
- }
644
- return typeof v === "number" ? String(v) : v;
645
- }
646
- case "scale": {
647
- const { value, unit } = parsePx(seedValueAt(seed, d.seedRef));
648
- const scaled = value * d.factor;
649
- return `${d.round === "int" ? Math.round(scaled) : round2(scaled)}${unit}`;
650
- }
651
- case "scaleNumber":
652
- return String(round2(seedValueAt(seed, d.seedRef) * d.factor));
653
- case "offsetNumber":
654
- return String(round2(seedValueAt(seed, d.seedRef) + d.delta));
655
- case "offsetSteps": {
656
- const base = parsePx(seedValueAt(seed, d.seedRef));
657
- const step = parsePx(seedValueAt(seed, d.stepRef));
658
- return `${round2(base.value + step.value * d.n)}${base.unit}`;
659
- }
660
- case "oklchRamp":
661
- return ramp(seed.color[d.seedRef.split(".")[1]])[d.step];
662
- case "figmaOklchScale":
663
- return d.value;
664
- case "seedAlpha":
665
- return seedAlphaCss(seedValueAt(seed, d.seedRef), d.alpha);
666
- case "alphaScale": {
667
- const step = seedValueAt(seed, d.stepRef);
668
- const channel = d.channel === "black" ? [0, 0, 0] : [255, 255, 255];
669
- return rgba(channel, step * d.multiplier);
670
- }
671
- case "fontStack":
672
- return formatFontFamily(seedValueAt(seed, d.seedRef));
673
- case "cubicBezier":
674
- return formatCubicBezier(seedValueAt(seed, d.seedRef));
675
- case "literal":
676
- return d.value;
677
- case "shadowLayer": {
678
- const { offsetX, offsetY, blur, spread } = shadowShapeOf(seed, d.level);
679
- const color2 = rgba([0, 0, 0], seed.size.opacity.alphaStep * SHADOW_LEVELS[d.level].alphaMult);
680
- return `${offsetX} ${offsetY} ${blur} ${spread} ${color2}`;
681
- }
682
- }
1136
+ switch (d.kind) {
1137
+ case "alias": {
1138
+ const v = seedValueAt(seed, d.seedRef);
1139
+ if (v != null && typeof v === "object" && "colorSpace" in v) return seedColorToCss(v);
1140
+ return typeof v === "number" ? String(v) : v;
1141
+ }
1142
+ case "scale": {
1143
+ const { value, unit } = parsePx(seedValueAt(seed, d.seedRef));
1144
+ const scaled = value * d.factor;
1145
+ return `${d.round === "int" ? Math.round(scaled) : round2(scaled)}${unit}`;
1146
+ }
1147
+ case "scaleNumber": return String(round2(seedValueAt(seed, d.seedRef) * d.factor));
1148
+ case "offsetNumber": return String(round2(seedValueAt(seed, d.seedRef) + d.delta));
1149
+ case "offsetSteps": {
1150
+ const base = parsePx(seedValueAt(seed, d.seedRef));
1151
+ const step = parsePx(seedValueAt(seed, d.stepRef));
1152
+ return `${round2(base.value + step.value * d.n)}${base.unit}`;
1153
+ }
1154
+ case "oklchRamp": return ramp(seed.color[d.seedRef.split(".")[1]])[d.step];
1155
+ case "figmaOklchScale": return d.value;
1156
+ case "seedAlpha": return seedAlphaCss(seedValueAt(seed, d.seedRef), d.alpha);
1157
+ case "alphaScale": {
1158
+ const step = seedValueAt(seed, d.stepRef);
1159
+ return rgba(d.channel === "black" ? [
1160
+ 0,
1161
+ 0,
1162
+ 0
1163
+ ] : [
1164
+ 255,
1165
+ 255,
1166
+ 255
1167
+ ], step * d.multiplier);
1168
+ }
1169
+ case "fontStack": return formatFontFamily(seedValueAt(seed, d.seedRef));
1170
+ case "cubicBezier": return formatCubicBezier(seedValueAt(seed, d.seedRef));
1171
+ case "literal": return d.value;
1172
+ case "shadowLayer": {
1173
+ const { offsetX, offsetY, blur, spread } = shadowShapeOf(seed, d.level);
1174
+ return `${offsetX} ${offsetY} ${blur} ${spread} ${rgba([
1175
+ 0,
1176
+ 0,
1177
+ 0
1178
+ ], seed.size.opacity.alphaStep * SHADOW_LEVELS[d.level].alphaMult)}`;
1179
+ }
1180
+ }
683
1181
  }
1182
+ /** Seed 依赖推导:AST 里出现的全部 Seed 点分路径。 */
684
1183
  function seedRefsOf(d) {
685
- switch (d.kind) {
686
- case "alias":
687
- case "scale":
688
- case "scaleNumber":
689
- case "offsetNumber":
690
- case "oklchRamp":
691
- case "figmaOklchScale":
692
- case "seedAlpha":
693
- case "fontStack":
694
- case "cubicBezier":
695
- return [d.seedRef];
696
- case "offsetSteps":
697
- return [d.seedRef, d.stepRef];
698
- case "alphaScale":
699
- return [d.stepRef];
700
- case "literal":
701
- return [];
702
- case "shadowLayer":
703
- return ["effect.shadow.base", "size.opacity.alphaStep"];
704
- }
1184
+ switch (d.kind) {
1185
+ case "alias":
1186
+ case "scale":
1187
+ case "scaleNumber":
1188
+ case "offsetNumber":
1189
+ case "oklchRamp":
1190
+ case "figmaOklchScale":
1191
+ case "seedAlpha":
1192
+ case "fontStack":
1193
+ case "cubicBezier": return [d.seedRef];
1194
+ case "offsetSteps": return [d.seedRef, d.stepRef];
1195
+ case "alphaScale": return [d.stepRef];
1196
+ case "literal": return [];
1197
+ case "shadowLayer": return ["effect.shadow.base", "size.opacity.alphaStep"];
1198
+ }
705
1199
  }
1200
+ /** 人读派生公式(Token Studio 展示用)。 */
706
1201
  function formatDerivation(d) {
707
- const sign = (n) => n >= 0 ? "+" : "\u2212";
708
- switch (d.kind) {
709
- case "alias":
710
- case "fontStack":
711
- case "cubicBezier":
712
- return `= seed.${d.seedRef}`;
713
- case "scale":
714
- return `= seed.${d.seedRef} \xD7 ${d.factor}`;
715
- case "scaleNumber":
716
- return `= seed.${d.seedRef} \xD7 ${d.factor}`;
717
- case "offsetNumber":
718
- return `= seed.${d.seedRef} ${sign(d.delta)} ${Math.abs(d.delta)}`;
719
- case "offsetSteps":
720
- return `= seed.${d.seedRef} ${sign(d.n)} seed.${d.stepRef} \xD7 ${Math.abs(d.n)}`;
721
- case "oklchRamp":
722
- return `= oklch(seed.${d.seedRef}) @ L ${L_TARGETS[d.step]} \xB7 C \xD7 ${round2(chromaFactor(L_TARGETS[d.step]))}`;
723
- case "figmaOklchScale":
724
- return `= figma Color/qw/t2/${d.seedRef.split(".")[1]}/${d.step}\uFF08OKLCH \u8BBE\u8BA1\u6821\u51C6\uFF09`;
725
- case "seedAlpha":
726
- return `= seed.${d.seedRef} / ${round2(d.alpha * 100)}%`;
727
- case "alphaScale":
728
- return `= ${d.channel === "black" ? "\u9ED1" : "\u767D"} / seed.${d.stepRef} \xD7 ${d.multiplier}`;
729
- case "literal":
730
- return `= ${d.value}\uFF08\u8272\u5F69\u7A7A\u95F4\u7AEF\u70B9\uFF0C\u767D\u540D\u5355\u5B57\u9762\u91CF\uFF09`;
731
- case "shadowLayer": {
732
- const f = SHADOW_LEVELS[d.level];
733
- return `= seed.effect.shadow.base \u5F62\u72B6 \xD7 {offset ${f.offset}, blur ${f.blur}} \xB7 \u8272 = \u9ED1 / alphaStep \xD7 ${f.alphaMult}`;
734
- }
735
- }
1202
+ const sign = (n) => n >= 0 ? "+" : "−";
1203
+ switch (d.kind) {
1204
+ case "alias":
1205
+ case "fontStack":
1206
+ case "cubicBezier": return `= seed.${d.seedRef}`;
1207
+ case "scale": return `= seed.${d.seedRef} × ${d.factor}`;
1208
+ case "scaleNumber": return `= seed.${d.seedRef} × ${d.factor}`;
1209
+ case "offsetNumber": return `= seed.${d.seedRef} ${sign(d.delta)} ${Math.abs(d.delta)}`;
1210
+ case "offsetSteps": return `= seed.${d.seedRef} ${sign(d.n)} seed.${d.stepRef} × ${Math.abs(d.n)}`;
1211
+ case "oklchRamp": return `= oklch(seed.${d.seedRef}) @ L ${L_TARGETS[d.step]} · C × ${round2(chromaFactor(L_TARGETS[d.step]))}`;
1212
+ case "figmaOklchScale": return `= figma Color/qw/t2/${d.seedRef.split(".")[1]}/${d.step}(OKLCH 设计校准)`;
1213
+ case "seedAlpha": return `= seed.${d.seedRef} / ${round2(d.alpha * 100)}%`;
1214
+ case "alphaScale": return `= ${d.channel === "black" ? "黑" : "白"} / seed.${d.stepRef} × ${d.multiplier}`;
1215
+ case "literal": return `= ${d.value}(色彩空间端点,白名单字面量)`;
1216
+ case "shadowLayer": {
1217
+ const f = SHADOW_LEVELS[d.level];
1218
+ return `= seed.effect.shadow.base 形状 × {offset ${f.offset}, blur ${f.blur}} · 色 = 黑 / alphaStep × ${f.alphaMult}`;
1219
+ }
1220
+ }
736
1221
  }
737
1222
  function ref(path, derivation) {
738
- return { path, cssVar: toVar("ref", path), seedRefs: seedRefsOf(derivation), derivation };
1223
+ return {
1224
+ path,
1225
+ cssVar: toVar("ref", path),
1226
+ seedRefs: seedRefsOf(derivation),
1227
+ derivation
1228
+ };
739
1229
  }
740
- var RAMP_COLORS = T1_COLOR_KEYS;
1230
+ const RAMP_COLORS = T1_COLOR_KEYS;
1231
+ /** 1. 12 个 Seed ×(A5-A100 Alpha 通道 + 50-900 Figma OKLCH 校准色阶 + base)。 */
741
1232
  function buildColorRampRecipes() {
742
- const out = [];
743
- for (const name of RAMP_COLORS) {
744
- const seedRef = `color.${name}`;
745
- for (const alpha of T2_ALPHA_STEPS) {
746
- out.push(ref(["color", name, `a${alpha}`], { kind: "seedAlpha", seedRef, alpha: alpha / 100 }));
747
- }
748
- for (const step of T2_OKLCH_STEPS) {
749
- out.push(
750
- ref(["color", name, String(step)], {
751
- kind: "figmaOklchScale",
752
- seedRef,
753
- step,
754
- value: FIGMA_T2_SOLID_SCALE[name][step]
755
- })
756
- );
757
- }
758
- out.push(ref(["color", name, "base"], { kind: "alias", seedRef }));
759
- }
760
- return out;
1233
+ const out = [];
1234
+ for (const name of RAMP_COLORS) {
1235
+ const seedRef = `color.${name}`;
1236
+ for (const alpha of T2_ALPHA_STEPS) out.push(ref([
1237
+ "color",
1238
+ name,
1239
+ `a${alpha}`
1240
+ ], {
1241
+ kind: "seedAlpha",
1242
+ seedRef,
1243
+ alpha: alpha / 100
1244
+ }));
1245
+ for (const step of T2_OKLCH_STEPS) out.push(ref([
1246
+ "color",
1247
+ name,
1248
+ String(step)
1249
+ ], {
1250
+ kind: "figmaOklchScale",
1251
+ seedRef,
1252
+ step,
1253
+ value: FIGMA_T2_SOLID_SCALE[name][step]
1254
+ }));
1255
+ out.push(ref([
1256
+ "color",
1257
+ name,
1258
+ "base"
1259
+ ], {
1260
+ kind: "alias",
1261
+ seedRef
1262
+ }));
1263
+ }
1264
+ return out;
761
1265
  }
1266
+ /** 4. 间距刻度 = spacing.step × N (N=1..8)。 */
762
1267
  function buildSpacingRecipes() {
763
- const out = [];
764
- for (let n = 1; n <= 8; n++) {
765
- out.push(ref(["size", "spacing", String(n)], { kind: "scale", seedRef: "size.spacing.step", factor: n }));
766
- }
767
- return out;
1268
+ const out = [];
1269
+ for (let n = 1; n <= 8; n++) out.push(ref([
1270
+ "size",
1271
+ "spacing",
1272
+ String(n)
1273
+ ], {
1274
+ kind: "scale",
1275
+ seedRef: "size.spacing.step",
1276
+ factor: n
1277
+ }));
1278
+ return out;
768
1279
  }
769
- var RADIUS_FACTORS = { sm: 0.5, lg: 1.75 };
1280
+ /** 圆角档位系数(sm/lg 从 base 派生;默认 base=8px → 4/8/14)。 */
1281
+ const RADIUS_FACTORS = {
1282
+ sm: .5,
1283
+ md: 1,
1284
+ lg: 1.75
1285
+ };
1286
+ /** 5. 圆角刻度:sm/md/lg 由 radius.base 按系数派生,full 直通独立裁切基准。 */
770
1287
  function buildRadiusRecipes() {
771
- return [
772
- ref(["size", "radius", "sm"], { kind: "scale", seedRef: "size.radius.base", factor: RADIUS_FACTORS.sm }),
773
- ref(["size", "radius", "md"], { kind: "alias", seedRef: "size.radius.base" }),
774
- ref(["size", "radius", "lg"], { kind: "scale", seedRef: "size.radius.base", factor: RADIUS_FACTORS.lg }),
775
- ref(["size", "radius", "full"], { kind: "alias", seedRef: "size.radius.full" })
776
- ];
1288
+ return [
1289
+ ref([
1290
+ "size",
1291
+ "radius",
1292
+ "sm"
1293
+ ], {
1294
+ kind: "scale",
1295
+ seedRef: "size.radius.base",
1296
+ factor: RADIUS_FACTORS.sm
1297
+ }),
1298
+ ref([
1299
+ "size",
1300
+ "radius",
1301
+ "md"
1302
+ ], {
1303
+ kind: "alias",
1304
+ seedRef: "size.radius.base"
1305
+ }),
1306
+ ref([
1307
+ "size",
1308
+ "radius",
1309
+ "lg"
1310
+ ], {
1311
+ kind: "scale",
1312
+ seedRef: "size.radius.base",
1313
+ factor: RADIUS_FACTORS.lg
1314
+ }),
1315
+ ref([
1316
+ "size",
1317
+ "radius",
1318
+ "full"
1319
+ ], {
1320
+ kind: "alias",
1321
+ seedRef: "size.radius.full"
1322
+ })
1323
+ ];
777
1324
  }
1325
+ /** 6. 控件/图标尺寸:md = base 直通,sm/lg = base ∓ spacing.step × n(控件 ×2、图标 ×1)。 */
778
1326
  function buildControlIconSizeRecipes() {
779
- const step = "size.spacing.step";
780
- return [
781
- ref(["size", "controlSize", "sm"], { kind: "offsetSteps", seedRef: "size.controlSize.base", stepRef: step, n: -2 }),
782
- ref(["size", "controlSize", "md"], { kind: "alias", seedRef: "size.controlSize.base" }),
783
- ref(["size", "controlSize", "lg"], { kind: "offsetSteps", seedRef: "size.controlSize.base", stepRef: step, n: 2 }),
784
- ref(["size", "iconSize", "sm"], { kind: "offsetSteps", seedRef: "size.iconSize.base", stepRef: step, n: -1 }),
785
- ref(["size", "iconSize", "md"], { kind: "alias", seedRef: "size.iconSize.base" }),
786
- ref(["size", "iconSize", "lg"], { kind: "offsetSteps", seedRef: "size.iconSize.base", stepRef: step, n: 1 })
787
- ];
1327
+ const step = "size.spacing.step";
1328
+ return [
1329
+ ref([
1330
+ "size",
1331
+ "controlSize",
1332
+ "sm"
1333
+ ], {
1334
+ kind: "offsetSteps",
1335
+ seedRef: "size.controlSize.base",
1336
+ stepRef: step,
1337
+ n: -2
1338
+ }),
1339
+ ref([
1340
+ "size",
1341
+ "controlSize",
1342
+ "md"
1343
+ ], {
1344
+ kind: "alias",
1345
+ seedRef: "size.controlSize.base"
1346
+ }),
1347
+ ref([
1348
+ "size",
1349
+ "controlSize",
1350
+ "lg"
1351
+ ], {
1352
+ kind: "offsetSteps",
1353
+ seedRef: "size.controlSize.base",
1354
+ stepRef: step,
1355
+ n: 2
1356
+ }),
1357
+ ref([
1358
+ "size",
1359
+ "iconSize",
1360
+ "sm"
1361
+ ], {
1362
+ kind: "offsetSteps",
1363
+ seedRef: "size.iconSize.base",
1364
+ stepRef: step,
1365
+ n: -1
1366
+ }),
1367
+ ref([
1368
+ "size",
1369
+ "iconSize",
1370
+ "md"
1371
+ ], {
1372
+ kind: "alias",
1373
+ seedRef: "size.iconSize.base"
1374
+ }),
1375
+ ref([
1376
+ "size",
1377
+ "iconSize",
1378
+ "lg"
1379
+ ], {
1380
+ kind: "offsetSteps",
1381
+ seedRef: "size.iconSize.base",
1382
+ stepRef: step,
1383
+ n: 1
1384
+ })
1385
+ ];
788
1386
  }
1387
+ /** 7. 描边宽度 —— 直通引用。 */
789
1388
  function buildBorderWidthRecipes() {
790
- return [ref(["size", "borderWidth", "hairline"], { kind: "alias", seedRef: "size.borderWidth.hairline" })];
1389
+ return [ref([
1390
+ "size",
1391
+ "borderWidth",
1392
+ "hairline"
1393
+ ], {
1394
+ kind: "alias",
1395
+ seedRef: "size.borderWidth.hairline"
1396
+ })];
791
1397
  }
1398
+ /** 8. 行高(typography 类目):normal 直通 Seed,tight/relaxed = base ∓ 0.25。 */
792
1399
  function buildLineHeightRecipes() {
793
- return [
794
- ref(["typography", "lineHeight", "tight"], { kind: "offsetNumber", seedRef: "typography.lineHeight.base", delta: -0.25 }),
795
- ref(["typography", "lineHeight", "normal"], { kind: "alias", seedRef: "typography.lineHeight.base" }),
796
- ref(["typography", "lineHeight", "relaxed"], { kind: "offsetNumber", seedRef: "typography.lineHeight.base", delta: 0.25 })
797
- ];
1400
+ return [
1401
+ ref([
1402
+ "typography",
1403
+ "lineHeight",
1404
+ "tight"
1405
+ ], {
1406
+ kind: "offsetNumber",
1407
+ seedRef: "typography.lineHeight.base",
1408
+ delta: -.25
1409
+ }),
1410
+ ref([
1411
+ "typography",
1412
+ "lineHeight",
1413
+ "normal"
1414
+ ], {
1415
+ kind: "alias",
1416
+ seedRef: "typography.lineHeight.base"
1417
+ }),
1418
+ ref([
1419
+ "typography",
1420
+ "lineHeight",
1421
+ "relaxed"
1422
+ ], {
1423
+ kind: "offsetNumber",
1424
+ seedRef: "typography.lineHeight.base",
1425
+ delta: .25
1426
+ })
1427
+ ];
798
1428
  }
1429
+ /** 9. 禁用态不透明度 —— 直通引用。 */
799
1430
  function buildOpacityRecipes() {
800
- return [ref(["size", "opacity", "disabled"], { kind: "alias", seedRef: "size.opacity.disabled" })];
1431
+ return [ref([
1432
+ "size",
1433
+ "opacity",
1434
+ "disabled"
1435
+ ], {
1436
+ kind: "alias",
1437
+ seedRef: "size.opacity.disabled"
1438
+ })];
801
1439
  }
1440
+ /** 10. 层级刻度 = zIndex.step × N (N=1..4);角色分配交给 Semantic 层。 */
802
1441
  function buildZIndexRecipes() {
803
- return [1, 2, 3, 4].map(
804
- (n) => ref(["size", "zIndex", String(n)], { kind: "scaleNumber", seedRef: "size.zIndex.step", factor: n })
805
- );
1442
+ return [
1443
+ 1,
1444
+ 2,
1445
+ 3,
1446
+ 4
1447
+ ].map((n) => ref([
1448
+ "size",
1449
+ "zIndex",
1450
+ String(n)
1451
+ ], {
1452
+ kind: "scaleNumber",
1453
+ seedRef: "size.zIndex.step",
1454
+ factor: n
1455
+ }));
806
1456
  }
807
- var FONT_FAMILY_KEYS = [
808
- "text",
809
- "number",
810
- "code",
811
- "textMacLatin",
812
- "textMacZh",
813
- "textMacJa",
814
- "textWindowsLatin",
815
- "textWindowsZh",
816
- "textWindowsJa"
1457
+ const FONT_FAMILY_KEYS = [
1458
+ "text",
1459
+ "number",
1460
+ "code",
1461
+ "textMacLatin",
1462
+ "textMacZh",
1463
+ "textMacJa",
1464
+ "textWindowsLatin",
1465
+ "textWindowsZh",
1466
+ "textWindowsJa"
817
1467
  ];
818
- var FONT_SIZE_FACTORS = { sm: 0.857, lg: 1.143, xl: 1.429, "2xl": 1.714 };
1468
+ /** 字号档位倍数(沿用既定视觉:14px 基准 → 12/16/20/24)。 */
1469
+ const FONT_SIZE_FACTORS = {
1470
+ sm: .857,
1471
+ lg: 1.143,
1472
+ xl: 1.429,
1473
+ "2xl": 1.714
1474
+ };
1475
+ /** 11. 排版原子:字体族(三族 + 平台/语言变体)/字重直通,字号按既定倍数派生。 */
819
1476
  function buildTypographyRecipes() {
820
- const out = [];
821
- for (const key of FONT_FAMILY_KEYS) {
822
- out.push(ref(["typography", "fontFamily", key], { kind: "fontStack", seedRef: `typography.fontFamily.${key}` }));
823
- }
824
- for (const key of ["regular", "medium", "semibold"]) {
825
- out.push(ref(["typography", "fontWeight", key], { kind: "alias", seedRef: `typography.fontWeight.${key}` }));
826
- }
827
- out.push(ref(["typography", "fontSize", "base"], { kind: "alias", seedRef: "typography.fontSize.base" }));
828
- for (const [key, factor] of Object.entries(FONT_SIZE_FACTORS)) {
829
- out.push(
830
- ref(["typography", "fontSize", key], { kind: "scale", seedRef: "typography.fontSize.base", factor, round: "int" })
831
- );
832
- }
833
- out.push(ref(["typography", "letterSpacing", "base"], { kind: "alias", seedRef: "typography.letterSpacing.base" }));
834
- return out;
1477
+ const out = [];
1478
+ for (const key of FONT_FAMILY_KEYS) out.push(ref([
1479
+ "typography",
1480
+ "fontFamily",
1481
+ key
1482
+ ], {
1483
+ kind: "fontStack",
1484
+ seedRef: `typography.fontFamily.${key}`
1485
+ }));
1486
+ for (const key of [
1487
+ "regular",
1488
+ "medium",
1489
+ "semibold"
1490
+ ]) out.push(ref([
1491
+ "typography",
1492
+ "fontWeight",
1493
+ key
1494
+ ], {
1495
+ kind: "alias",
1496
+ seedRef: `typography.fontWeight.${key}`
1497
+ }));
1498
+ out.push(ref([
1499
+ "typography",
1500
+ "fontSize",
1501
+ "base"
1502
+ ], {
1503
+ kind: "alias",
1504
+ seedRef: "typography.fontSize.base"
1505
+ }));
1506
+ for (const [key, factor] of Object.entries(FONT_SIZE_FACTORS)) out.push(ref([
1507
+ "typography",
1508
+ "fontSize",
1509
+ key
1510
+ ], {
1511
+ kind: "scale",
1512
+ seedRef: "typography.fontSize.base",
1513
+ factor,
1514
+ round: "int"
1515
+ }));
1516
+ out.push(ref([
1517
+ "typography",
1518
+ "letterSpacing",
1519
+ "base"
1520
+ ], {
1521
+ kind: "alias",
1522
+ seedRef: "typography.letterSpacing.base"
1523
+ }));
1524
+ return out;
835
1525
  }
836
- var DURATION_FACTORS = { fast: 0.5, slow: 1.6 };
1526
+ /** 动效时长档位倍数。 */
1527
+ const DURATION_FACTORS = {
1528
+ fast: .5,
1529
+ slow: 1.6
1530
+ };
1531
+ /** 12. 动效:duration 按倍数派生,easing 直通。 */
837
1532
  function buildMotionRecipes() {
838
- return [
839
- ref(["motion", "duration", "fast"], { kind: "scale", seedRef: "motion.duration.base", factor: DURATION_FACTORS.fast }),
840
- ref(["motion", "duration", "base"], { kind: "alias", seedRef: "motion.duration.base" }),
841
- ref(["motion", "duration", "slow"], { kind: "scale", seedRef: "motion.duration.base", factor: DURATION_FACTORS.slow }),
842
- ref(["motion", "easing", "standard"], { kind: "cubicBezier", seedRef: "motion.easing.standard" })
843
- ];
1533
+ return [
1534
+ ref([
1535
+ "motion",
1536
+ "duration",
1537
+ "fast"
1538
+ ], {
1539
+ kind: "scale",
1540
+ seedRef: "motion.duration.base",
1541
+ factor: DURATION_FACTORS.fast
1542
+ }),
1543
+ ref([
1544
+ "motion",
1545
+ "duration",
1546
+ "base"
1547
+ ], {
1548
+ kind: "alias",
1549
+ seedRef: "motion.duration.base"
1550
+ }),
1551
+ ref([
1552
+ "motion",
1553
+ "duration",
1554
+ "slow"
1555
+ ], {
1556
+ kind: "scale",
1557
+ seedRef: "motion.duration.base",
1558
+ factor: DURATION_FACTORS.slow
1559
+ }),
1560
+ ref([
1561
+ "motion",
1562
+ "easing",
1563
+ "standard"
1564
+ ], {
1565
+ kind: "cubicBezier",
1566
+ seedRef: "motion.easing.standard"
1567
+ })
1568
+ ];
844
1569
  }
1570
+ /**
1571
+ * 13. 阴影:形状由 effect.shadow.base 按各档位比例缩放,颜色沿用
1572
+ * alphaStep 结构锚点。它不是 Figma Color 变量体系里的 T2 颜色 token。
1573
+ */
845
1574
  function buildShadowRecipes() {
846
- return ["sm", "md", "lg"].map((level) => ref(["effect", "shadow", level], { kind: "shadowLayer", level }));
1575
+ return [
1576
+ "sm",
1577
+ "md",
1578
+ "lg"
1579
+ ].map((level) => ref([
1580
+ "effect",
1581
+ "shadow",
1582
+ level
1583
+ ], {
1584
+ kind: "shadowLayer",
1585
+ level
1586
+ }));
847
1587
  }
848
1588
  function allReferenceRecipes() {
849
- return [
850
- ...buildColorRampRecipes(),
851
- ...buildSpacingRecipes(),
852
- ...buildRadiusRecipes(),
853
- ...buildControlIconSizeRecipes(),
854
- ...buildBorderWidthRecipes(),
855
- ...buildLineHeightRecipes(),
856
- ...buildOpacityRecipes(),
857
- ...buildZIndexRecipes(),
858
- ...buildTypographyRecipes(),
859
- ...buildMotionRecipes(),
860
- ...buildShadowRecipes()
861
- ];
1589
+ return [
1590
+ ...buildColorRampRecipes(),
1591
+ ...buildSpacingRecipes(),
1592
+ ...buildRadiusRecipes(),
1593
+ ...buildControlIconSizeRecipes(),
1594
+ ...buildBorderWidthRecipes(),
1595
+ ...buildLineHeightRecipes(),
1596
+ ...buildOpacityRecipes(),
1597
+ ...buildZIndexRecipes(),
1598
+ ...buildTypographyRecipes(),
1599
+ ...buildMotionRecipes(),
1600
+ ...buildShadowRecipes()
1601
+ ];
862
1602
  }
1603
+ /** T2 顶层入口:喂一个 Seed,产出全量 Reference recipes + 拍平后的 CSS 变量映射 + 阴影形状(供 Semantic 层消费)。 */
863
1604
  function buildReference(seed) {
864
- const recipes = allReferenceRecipes();
865
- const tokens = {};
866
- for (const recipe of recipes) tokens[recipe.cssVar] = evaluateDerivation(recipe.derivation, seed);
867
- return {
868
- recipes,
869
- tokens,
870
- shapes: {
871
- shadow: {
872
- sm: shadowShapeOf(seed, "sm"),
873
- md: shadowShapeOf(seed, "md"),
874
- lg: shadowShapeOf(seed, "lg")
875
- }
876
- }
877
- };
1605
+ const recipes = allReferenceRecipes();
1606
+ const tokens = {};
1607
+ for (const recipe of recipes) tokens[recipe.cssVar] = evaluateDerivation(recipe.derivation, seed);
1608
+ return {
1609
+ recipes,
1610
+ tokens,
1611
+ shapes: { shadow: {
1612
+ sm: shadowShapeOf(seed, "sm"),
1613
+ md: shadowShapeOf(seed, "md"),
1614
+ lg: shadowShapeOf(seed, "lg")
1615
+ } }
1616
+ };
878
1617
  }
879
-
880
- // src/derive/semantic.ts
1618
+ //#endregion
1619
+ //#region src/derive/semantic.ts
881
1620
  function pick(tokens, dottedRefPath) {
882
- const v = tokens[toVar("ref", dottedRefPath.split("."))];
883
- if (v === void 0) throw new Error(`[qw] Semantic \u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684 Reference token: ${dottedRefPath}`);
884
- return v;
1621
+ const v = tokens[toVar("ref", dottedRefPath.split("."))];
1622
+ if (v === void 0) throw new Error(`[qw] Semantic 引用了不存在的 Reference token: ${dottedRefPath}`);
1623
+ return v;
885
1624
  }
886
1625
  function semVar(path) {
887
- return toVar("sem", path);
1626
+ return toVar("sem", path);
888
1627
  }
889
- var RADIUS_ROLES = {
890
- control: "size.radius.md",
891
- card: "size.radius.lg",
892
- pill: "size.radius.full",
893
- full: "size.radius.full"
1628
+ const RADIUS_ROLES = {
1629
+ control: "size.radius.md",
1630
+ card: "size.radius.lg",
1631
+ pill: "size.radius.full",
1632
+ full: "size.radius.full"
894
1633
  };
895
- var CONTROL_HEIGHT_ROLES = {
896
- sm: "size.controlSize.sm",
897
- md: "size.controlSize.md",
898
- lg: "size.controlSize.lg"
1634
+ const CONTROL_HEIGHT_ROLES = {
1635
+ sm: "size.controlSize.sm",
1636
+ md: "size.controlSize.md",
1637
+ lg: "size.controlSize.lg"
899
1638
  };
900
- var ICON_SIZE_ROLES = {
901
- sm: "size.iconSize.sm",
902
- md: "size.iconSize.md",
903
- lg: "size.iconSize.lg"
1639
+ const ICON_SIZE_ROLES = {
1640
+ sm: "size.iconSize.sm",
1641
+ md: "size.iconSize.md",
1642
+ lg: "size.iconSize.lg"
904
1643
  };
905
- var INSET_STACK_ROLES = {
906
- sm: "size.spacing.2",
907
- md: "size.spacing.4",
908
- lg: "size.spacing.6"
1644
+ const INSET_STACK_ROLES = {
1645
+ sm: "size.spacing.2",
1646
+ md: "size.spacing.4",
1647
+ lg: "size.spacing.6"
909
1648
  };
910
- var Z_INDEX_ROLES = {
911
- dropdown: "size.zIndex.1",
912
- sticky: "size.zIndex.2",
913
- overlay: "size.zIndex.3",
914
- toast: "size.zIndex.4"
1649
+ const Z_INDEX_ROLES = {
1650
+ dropdown: "size.zIndex.1",
1651
+ sticky: "size.zIndex.2",
1652
+ overlay: "size.zIndex.3",
1653
+ toast: "size.zIndex.4"
915
1654
  };
916
- var MOTION_DURATION_ROLES = {
917
- fast: "motion.duration.fast",
918
- base: "motion.duration.base",
919
- slow: "motion.duration.slow"
1655
+ const MOTION_DURATION_ROLES = {
1656
+ fast: "motion.duration.fast",
1657
+ base: "motion.duration.base",
1658
+ slow: "motion.duration.slow"
920
1659
  };
921
- var FONT_FAMILY_ROLES = {
922
- text: "typography.fontFamily.text",
923
- number: "typography.fontFamily.number",
924
- code: "typography.fontFamily.code"
1660
+ /** 字体族角色:text(正文缺省)/ number(数字数据)/ code(等宽)。 */
1661
+ const FONT_FAMILY_ROLES = {
1662
+ text: "typography.fontFamily.text",
1663
+ number: "typography.fontFamily.number",
1664
+ code: "typography.fontFamily.code"
925
1665
  };
926
- var T_LETTER = "typography.letterSpacing.base";
927
- var TYPOGRAPHY_ROLES = {
928
- body: {
929
- fontFamilyRef: "typography.fontFamily.text",
930
- fontSizeRef: "typography.fontSize.base",
931
- fontWeightRef: "typography.fontWeight.regular",
932
- lineHeightRef: "typography.lineHeight.normal",
933
- letterSpacingRef: T_LETTER
934
- },
935
- bodyStrong: {
936
- fontFamilyRef: "typography.fontFamily.text",
937
- fontSizeRef: "typography.fontSize.base",
938
- fontWeightRef: "typography.fontWeight.medium",
939
- lineHeightRef: "typography.lineHeight.normal",
940
- letterSpacingRef: T_LETTER
941
- },
942
- headingSm: {
943
- fontFamilyRef: "typography.fontFamily.text",
944
- fontSizeRef: "typography.fontSize.lg",
945
- fontWeightRef: "typography.fontWeight.semibold",
946
- lineHeightRef: "typography.lineHeight.tight",
947
- letterSpacingRef: T_LETTER
948
- },
949
- headingMd: {
950
- fontFamilyRef: "typography.fontFamily.text",
951
- fontSizeRef: "typography.fontSize.xl",
952
- fontWeightRef: "typography.fontWeight.semibold",
953
- lineHeightRef: "typography.lineHeight.tight",
954
- letterSpacingRef: T_LETTER
955
- },
956
- headingLg: {
957
- fontFamilyRef: "typography.fontFamily.text",
958
- fontSizeRef: "typography.fontSize.2xl",
959
- fontWeightRef: "typography.fontWeight.semibold",
960
- lineHeightRef: "typography.lineHeight.tight",
961
- letterSpacingRef: T_LETTER
962
- },
963
- caption: {
964
- fontFamilyRef: "typography.fontFamily.text",
965
- fontSizeRef: "typography.fontSize.sm",
966
- fontWeightRef: "typography.fontWeight.regular",
967
- lineHeightRef: "typography.lineHeight.normal",
968
- letterSpacingRef: T_LETTER
969
- },
970
- code: {
971
- fontFamilyRef: "typography.fontFamily.code",
972
- fontSizeRef: "typography.fontSize.sm",
973
- fontWeightRef: "typography.fontWeight.regular",
974
- lineHeightRef: "typography.lineHeight.normal",
975
- letterSpacingRef: T_LETTER
976
- }
1666
+ const T_LETTER = "typography.letterSpacing.base";
1667
+ /** typography.* 复合角色:每个字段各自引用 Reference(无任何裸字面量)。 */
1668
+ const TYPOGRAPHY_ROLES = {
1669
+ body: {
1670
+ fontFamilyRef: "typography.fontFamily.text",
1671
+ fontSizeRef: "typography.fontSize.base",
1672
+ fontWeightRef: "typography.fontWeight.regular",
1673
+ lineHeightRef: "typography.lineHeight.normal",
1674
+ letterSpacingRef: T_LETTER
1675
+ },
1676
+ bodyStrong: {
1677
+ fontFamilyRef: "typography.fontFamily.text",
1678
+ fontSizeRef: "typography.fontSize.base",
1679
+ fontWeightRef: "typography.fontWeight.medium",
1680
+ lineHeightRef: "typography.lineHeight.normal",
1681
+ letterSpacingRef: T_LETTER
1682
+ },
1683
+ headingSm: {
1684
+ fontFamilyRef: "typography.fontFamily.text",
1685
+ fontSizeRef: "typography.fontSize.lg",
1686
+ fontWeightRef: "typography.fontWeight.semibold",
1687
+ lineHeightRef: "typography.lineHeight.tight",
1688
+ letterSpacingRef: T_LETTER
1689
+ },
1690
+ headingMd: {
1691
+ fontFamilyRef: "typography.fontFamily.text",
1692
+ fontSizeRef: "typography.fontSize.xl",
1693
+ fontWeightRef: "typography.fontWeight.semibold",
1694
+ lineHeightRef: "typography.lineHeight.tight",
1695
+ letterSpacingRef: T_LETTER
1696
+ },
1697
+ headingLg: {
1698
+ fontFamilyRef: "typography.fontFamily.text",
1699
+ fontSizeRef: "typography.fontSize.2xl",
1700
+ fontWeightRef: "typography.fontWeight.semibold",
1701
+ lineHeightRef: "typography.lineHeight.tight",
1702
+ letterSpacingRef: T_LETTER
1703
+ },
1704
+ caption: {
1705
+ fontFamilyRef: "typography.fontFamily.text",
1706
+ fontSizeRef: "typography.fontSize.sm",
1707
+ fontWeightRef: "typography.fontWeight.regular",
1708
+ lineHeightRef: "typography.lineHeight.normal",
1709
+ letterSpacingRef: T_LETTER
1710
+ },
1711
+ code: {
1712
+ fontFamilyRef: "typography.fontFamily.code",
1713
+ fontSizeRef: "typography.fontSize.sm",
1714
+ fontWeightRef: "typography.fontWeight.regular",
1715
+ lineHeightRef: "typography.lineHeight.normal",
1716
+ letterSpacingRef: T_LETTER
1717
+ }
977
1718
  };
978
- var COLOR_ROLES = FIGMA_T3_COLOR_ROLES;
979
- var SHADOW_COMPOSITE_ROLES = {};
1719
+ const COLOR_ROLES = {
1720
+ ...FIGMA_T3_COLOR_ROLES,
1721
+ shadow: {
1722
+ light: "color.black.a10",
1723
+ dark: "color.black.a50"
1724
+ }
1725
+ };
1726
+ /** 复合阴影角色:形状取 Reference effect.shadow.<level>,颜色取 color.shadow。 */
1727
+ const SHADOW_COMPOSITE_ROLES = { dialog: "lg" };
1728
+ /**
1729
+ * 全量 Semantic 角色的结构化血缘表——"角色 → 引用的 Reference 路径(含 light/dark 分支)"。
1730
+ * 与 buildSemanticStructural/buildSemanticColor 的产出一一对应(顺序一致)。
1731
+ */
980
1732
  function semanticRoleEntries() {
981
- const out = [];
982
- const structural = (path, refPath) => out.push({ kind: "structural", path: path.join("."), cssVar: semVar(path), refPath });
983
- for (const [role, refPath] of Object.entries(RADIUS_ROLES)) structural(["size", "radius", role], refPath);
984
- for (const [role, refPath] of Object.entries(CONTROL_HEIGHT_ROLES)) structural(["size", "controlHeight", role], refPath);
985
- for (const [role, refPath] of Object.entries(ICON_SIZE_ROLES)) structural(["size", "iconSize", role], refPath);
986
- for (const [role, refPath] of Object.entries(INSET_STACK_ROLES)) {
987
- structural(["size", "inset", role], refPath);
988
- structural(["size", "stack", role], refPath);
989
- }
990
- structural(["size", "opacity", "disabled"], "size.opacity.disabled");
991
- for (const [role, refPath] of Object.entries(Z_INDEX_ROLES)) structural(["size", "zIndex", role], refPath);
992
- for (const [role, refPath] of Object.entries(FONT_FAMILY_ROLES)) structural(["typography", "fontFamily", role], refPath);
993
- for (const [role, spec] of Object.entries(TYPOGRAPHY_ROLES)) {
994
- structural(["typography", role, "fontFamily"], spec.fontFamilyRef);
995
- structural(["typography", role, "fontSize"], spec.fontSizeRef);
996
- structural(["typography", role, "fontWeight"], spec.fontWeightRef);
997
- structural(["typography", role, "lineHeight"], spec.lineHeightRef);
998
- structural(["typography", role, "letterSpacing"], spec.letterSpacingRef);
999
- }
1000
- for (const [role, refPath] of Object.entries(MOTION_DURATION_ROLES)) structural(["motion", "duration", role], refPath);
1001
- structural(["motion", "easing", "standard"], "motion.easing.standard");
1002
- for (const [path, refs] of Object.entries(COLOR_ROLES)) {
1003
- const segs = ["color", ...path.split(".")];
1004
- out.push({ kind: "color", path: segs.join("."), cssVar: semVar(segs), refPath: refs });
1005
- }
1006
- for (const [role, level] of Object.entries(SHADOW_COMPOSITE_ROLES)) {
1007
- const segs = ["effect", "shadow", role];
1008
- out.push({
1009
- kind: "shadowComposite",
1010
- path: segs.join("."),
1011
- cssVar: semVar(segs),
1012
- shapeRef: `effect.shadow.${level}`,
1013
- colorRole: "color.shadow"
1014
- });
1015
- }
1016
- return out;
1733
+ const out = [];
1734
+ const structural = (path, refPath) => out.push({
1735
+ kind: "structural",
1736
+ path: path.join("."),
1737
+ cssVar: semVar(path),
1738
+ refPath
1739
+ });
1740
+ for (const [role, refPath] of Object.entries(RADIUS_ROLES)) structural([
1741
+ "size",
1742
+ "radius",
1743
+ role
1744
+ ], refPath);
1745
+ for (const [role, refPath] of Object.entries(CONTROL_HEIGHT_ROLES)) structural([
1746
+ "size",
1747
+ "controlHeight",
1748
+ role
1749
+ ], refPath);
1750
+ for (const [role, refPath] of Object.entries(ICON_SIZE_ROLES)) structural([
1751
+ "size",
1752
+ "iconSize",
1753
+ role
1754
+ ], refPath);
1755
+ for (const [role, refPath] of Object.entries(INSET_STACK_ROLES)) {
1756
+ structural([
1757
+ "size",
1758
+ "inset",
1759
+ role
1760
+ ], refPath);
1761
+ structural([
1762
+ "size",
1763
+ "stack",
1764
+ role
1765
+ ], refPath);
1766
+ }
1767
+ structural([
1768
+ "size",
1769
+ "opacity",
1770
+ "disabled"
1771
+ ], "size.opacity.disabled");
1772
+ for (const [role, refPath] of Object.entries(Z_INDEX_ROLES)) structural([
1773
+ "size",
1774
+ "zIndex",
1775
+ role
1776
+ ], refPath);
1777
+ for (const [role, refPath] of Object.entries(FONT_FAMILY_ROLES)) structural([
1778
+ "typography",
1779
+ "fontFamily",
1780
+ role
1781
+ ], refPath);
1782
+ for (const [role, spec] of Object.entries(TYPOGRAPHY_ROLES)) {
1783
+ structural([
1784
+ "typography",
1785
+ role,
1786
+ "fontFamily"
1787
+ ], spec.fontFamilyRef);
1788
+ structural([
1789
+ "typography",
1790
+ role,
1791
+ "fontSize"
1792
+ ], spec.fontSizeRef);
1793
+ structural([
1794
+ "typography",
1795
+ role,
1796
+ "fontWeight"
1797
+ ], spec.fontWeightRef);
1798
+ structural([
1799
+ "typography",
1800
+ role,
1801
+ "lineHeight"
1802
+ ], spec.lineHeightRef);
1803
+ structural([
1804
+ "typography",
1805
+ role,
1806
+ "letterSpacing"
1807
+ ], spec.letterSpacingRef);
1808
+ }
1809
+ for (const [role, refPath] of Object.entries(MOTION_DURATION_ROLES)) structural([
1810
+ "motion",
1811
+ "duration",
1812
+ role
1813
+ ], refPath);
1814
+ structural([
1815
+ "motion",
1816
+ "easing",
1817
+ "standard"
1818
+ ], "motion.easing.standard");
1819
+ for (const [path, refs] of Object.entries(COLOR_ROLES)) {
1820
+ const segs = ["color", ...path.split(".")];
1821
+ out.push({
1822
+ kind: "color",
1823
+ path: segs.join("."),
1824
+ cssVar: semVar(segs),
1825
+ refPath: refs
1826
+ });
1827
+ }
1828
+ for (const [role, level] of Object.entries(SHADOW_COMPOSITE_ROLES)) {
1829
+ const segs = [
1830
+ "effect",
1831
+ "shadow",
1832
+ role
1833
+ ];
1834
+ out.push({
1835
+ kind: "shadowComposite",
1836
+ path: segs.join("."),
1837
+ cssVar: semVar(segs),
1838
+ shapeRef: `effect.shadow.${level}`,
1839
+ colorRole: "color.shadow"
1840
+ });
1841
+ }
1842
+ return out;
1017
1843
  }
1844
+ /**
1845
+ * 结构类(mode 无关):尺寸角色 + 字体族角色 + 排版复合角色 + 动效时长/缓动。
1846
+ * `effect.shadow.*` 的颜色字段随 mode 变化,归入 `buildSemanticColor`。
1847
+ */
1018
1848
  function buildSemanticStructural(reference) {
1019
- const out = {};
1020
- const { tokens } = reference;
1021
- for (const entry of semanticRoleEntries()) {
1022
- if (entry.kind !== "structural") continue;
1023
- out[entry.cssVar] = pick(tokens, entry.refPath);
1024
- }
1025
- return out;
1849
+ const out = {};
1850
+ const { tokens } = reference;
1851
+ for (const entry of semanticRoleEntries()) {
1852
+ if (entry.kind !== "structural") continue;
1853
+ out[entry.cssVar] = pick(tokens, entry.refPath);
1854
+ }
1855
+ return out;
1026
1856
  }
1857
+ /**
1858
+ * 颜色类(light/dark 分别求值):color.* 全部角色。
1859
+ */
1027
1860
  function buildSemanticColor(reference, mode) {
1028
- const out = {};
1029
- const { tokens, shapes } = reference;
1030
- for (const entry of semanticRoleEntries()) {
1031
- if (entry.kind !== "color") continue;
1032
- out[entry.cssVar] = pick(tokens, entry.refPath[mode]);
1033
- }
1034
- const shadowColor = out[semVar(["color", "shadow"])];
1035
- for (const [role, level] of Object.entries(SHADOW_COMPOSITE_ROLES)) {
1036
- const shape = shapes.shadow[level];
1037
- out[semVar(["effect", "shadow", role])] = `${shape.offsetX} ${shape.offsetY} ${shape.blur} ${shape.spread} ${shadowColor}`;
1038
- }
1039
- return out;
1861
+ const out = {};
1862
+ const { tokens, shapes } = reference;
1863
+ for (const entry of semanticRoleEntries()) {
1864
+ if (entry.kind !== "color") continue;
1865
+ out[entry.cssVar] = pick(tokens, entry.refPath[mode]);
1866
+ }
1867
+ const shadowColor = out[semVar(["color", "shadow"])];
1868
+ for (const [role, level] of Object.entries(SHADOW_COMPOSITE_ROLES)) {
1869
+ const shape = shapes.shadow[level];
1870
+ out[semVar([
1871
+ "effect",
1872
+ "shadow",
1873
+ role
1874
+ ])] = `${shape.offsetX} ${shape.offsetY} ${shape.blur} ${shape.spread} ${shadowColor}`;
1875
+ }
1876
+ return out;
1040
1877
  }
1041
-
1042
- // src/derive/index.ts
1878
+ //#endregion
1879
+ //#region src/derive/index.ts
1880
+ /**
1881
+ * derive — 单源派生(RFC 0001)。喂一个 Seed,编排 T2 Reference → T3 Semantic,
1882
+ * 返回亮/暗两套全量 CSS 变量映射(含 --qw-ref-* 与 --qw-sem-*)。
1883
+ * 外部签名/返回值形状与改造前保持一致(FR-014),供 `@qw-ui/react`、`@qw-ui/vue` 的
1884
+ * ConfigProvider 直接消费,无需改动。
1885
+ *
1886
+ * 注意:`validateSeed` 不在此处调用(沿用现状契约),校验是调用方的责任。
1887
+ */
1043
1888
  function derive(seed) {
1044
- const reference = buildReference(seed);
1045
- const structural = buildSemanticStructural(reference);
1046
- const mk = (mode) => ({
1047
- ...reference.tokens,
1048
- ...structural,
1049
- ...buildSemanticColor(reference, mode)
1050
- });
1051
- return { light: mk("light"), dark: mk("dark") };
1889
+ const reference = buildReference(seed);
1890
+ const structural = buildSemanticStructural(reference);
1891
+ const mk = (mode) => ({
1892
+ ...reference.tokens,
1893
+ ...structural,
1894
+ ...buildSemanticColor(reference, mode)
1895
+ });
1896
+ return {
1897
+ light: mk("light"),
1898
+ dark: mk("dark")
1899
+ };
1052
1900
  }
1053
-
1054
- // src/derive/live-css.ts
1055
- var round4 = (n) => Math.round(n * 1e4) / 1e4;
1901
+ //#endregion
1902
+ //#region src/derive/live-css.ts
1903
+ /**
1904
+ * live CSS 生成器 —— dist/tokens.live.css / Token Studio 响应式级联的引擎。
1905
+ *
1906
+ * 与静态 tokens.css(解析后的字面值,SSR 稳定出口)不同,live CSS 把整棵派生
1907
+ * 树翻译成 CSS 原生表达式:
1908
+ * - T1 Seed 落为 `--qw-seed-*` 变量(唯一含具体数值的层);
1909
+ * - T2 Reference 用 relative color(oklch(from …))/ calc() 从 Seed 变量派生;
1910
+ * - T3 Semantic 全部是 `var(--qw-ref-*)` 别名;dark 模式只在暗色选择器下
1911
+ * 重映射 sem → 不同的 ref 档位。
1912
+ *
1913
+ * 于是改一个 `--qw-seed-*`(setProperty 即可),整棵 ref/sem 树由浏览器原生
1914
+ * 级联更新,零 JS 重算。依赖现代 CSS(relative color / round()),刻意不做
1915
+ * 老浏览器兼容——组件库消费方继续用静态 tokens.css。
1916
+ */
1917
+ const round4 = (n) => Math.round(n * 1e4) / 1e4;
1056
1918
  function seedVar(dotted) {
1057
- return toVar("seed", dotted.split("."));
1919
+ return toVar("seed", dotted.split("."));
1058
1920
  }
1921
+ /** Seed 拍平成 `--qw-seed-*` → CSS 值。live CSS 里唯一携带具体数值的层。 */
1059
1922
  function seedCssVars(seed) {
1060
- const out = {};
1061
- for (const [name, value] of Object.entries(seed.color)) {
1062
- out[seedVar(`color.${name}`)] = seedColorToCss(value);
1063
- }
1064
- out[seedVar("size.spacing.step")] = seed.size.spacing.step;
1065
- out[seedVar("size.borderWidth.hairline")] = seed.size.borderWidth.hairline;
1066
- out[seedVar("size.radius.base")] = seed.size.radius.base;
1067
- out[seedVar("size.radius.full")] = seed.size.radius.full;
1068
- out[seedVar("size.controlSize.base")] = seed.size.controlSize.base;
1069
- out[seedVar("size.iconSize.base")] = seed.size.iconSize.base;
1070
- out[seedVar("size.opacity.disabled")] = String(seed.size.opacity.disabled);
1071
- out[seedVar("size.opacity.alphaStep")] = String(seed.size.opacity.alphaStep);
1072
- out[seedVar("size.zIndex.step")] = String(seed.size.zIndex.step);
1073
- for (const key of FONT_FAMILY_KEYS) {
1074
- out[seedVar(`typography.fontFamily.${key}`)] = formatFontFamily(seed.typography.fontFamily[key]);
1075
- }
1076
- out[seedVar("typography.fontWeight.regular")] = String(seed.typography.fontWeight.regular);
1077
- out[seedVar("typography.fontWeight.medium")] = String(seed.typography.fontWeight.medium);
1078
- out[seedVar("typography.fontWeight.semibold")] = String(seed.typography.fontWeight.semibold);
1079
- out[seedVar("typography.fontSize.base")] = seed.typography.fontSize.base;
1080
- out[seedVar("typography.lineHeight.base")] = String(seed.typography.lineHeight.base);
1081
- out[seedVar("typography.letterSpacing.base")] = seed.typography.letterSpacing.base;
1082
- out[seedVar("motion.duration.base")] = seed.motion.duration.base;
1083
- const [a, b, c, d] = seed.motion.easing.standard;
1084
- out[seedVar("motion.easing.standard")] = `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
1085
- out[seedVar("effect.shadow.base.offsetX")] = seed.effect.shadow.base.offsetX;
1086
- out[seedVar("effect.shadow.base.offsetY")] = seed.effect.shadow.base.offsetY;
1087
- out[seedVar("effect.shadow.base.blur")] = seed.effect.shadow.base.blur;
1088
- out[seedVar("effect.shadow.base.spread")] = seed.effect.shadow.base.spread;
1089
- return out;
1923
+ const out = {};
1924
+ for (const [name, value] of Object.entries(seed.color)) out[seedVar(`color.${name}`)] = seedColorToCss(value);
1925
+ out[seedVar("size.spacing.step")] = seed.size.spacing.step;
1926
+ out[seedVar("size.borderWidth.hairline")] = seed.size.borderWidth.hairline;
1927
+ out[seedVar("size.radius.base")] = seed.size.radius.base;
1928
+ out[seedVar("size.radius.full")] = seed.size.radius.full;
1929
+ out[seedVar("size.controlSize.base")] = seed.size.controlSize.base;
1930
+ out[seedVar("size.iconSize.base")] = seed.size.iconSize.base;
1931
+ out[seedVar("size.opacity.disabled")] = String(seed.size.opacity.disabled);
1932
+ out[seedVar("size.opacity.alphaStep")] = String(seed.size.opacity.alphaStep);
1933
+ out[seedVar("size.zIndex.step")] = String(seed.size.zIndex.step);
1934
+ for (const key of FONT_FAMILY_KEYS) out[seedVar(`typography.fontFamily.${key}`)] = formatFontFamily(seed.typography.fontFamily[key]);
1935
+ out[seedVar("typography.fontWeight.regular")] = String(seed.typography.fontWeight.regular);
1936
+ out[seedVar("typography.fontWeight.medium")] = String(seed.typography.fontWeight.medium);
1937
+ out[seedVar("typography.fontWeight.semibold")] = String(seed.typography.fontWeight.semibold);
1938
+ out[seedVar("typography.fontSize.base")] = seed.typography.fontSize.base;
1939
+ out[seedVar("typography.lineHeight.base")] = String(seed.typography.lineHeight.base);
1940
+ out[seedVar("typography.letterSpacing.base")] = seed.typography.letterSpacing.base;
1941
+ out[seedVar("motion.duration.base")] = seed.motion.duration.base;
1942
+ const [a, b, c, d] = seed.motion.easing.standard;
1943
+ out[seedVar("motion.easing.standard")] = `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
1944
+ out[seedVar("effect.shadow.base.offsetX")] = seed.effect.shadow.base.offsetX;
1945
+ out[seedVar("effect.shadow.base.offsetY")] = seed.effect.shadow.base.offsetY;
1946
+ out[seedVar("effect.shadow.base.blur")] = seed.effect.shadow.base.blur;
1947
+ out[seedVar("effect.shadow.base.spread")] = seed.effect.shadow.base.spread;
1948
+ return out;
1090
1949
  }
1091
1950
  function shadowShapeExprs(level) {
1092
- const { offset, blur } = SHADOW_LEVELS[level];
1093
- const scale = (field, f) => `calc(var(${seedVar(`effect.shadow.base.${field}`)}) * ${f})`;
1094
- return {
1095
- offsetX: scale("offsetX", offset),
1096
- offsetY: scale("offsetY", offset),
1097
- blur: scale("blur", blur),
1098
- spread: scale("spread", offset)
1099
- };
1951
+ const { offset, blur } = SHADOW_LEVELS[level];
1952
+ const scale = (field, f) => `calc(var(${seedVar(`effect.shadow.base.${field}`)}) * ${f})`;
1953
+ return {
1954
+ offsetX: scale("offsetX", offset),
1955
+ offsetY: scale("offsetY", offset),
1956
+ blur: scale("blur", blur),
1957
+ spread: scale("spread", offset)
1958
+ };
1100
1959
  }
1960
+ /** 一条 Derivation → 引用 `--qw-seed-*` 的 CSS 原生表达式。 */
1101
1961
  function derivationToCssExpr(d) {
1102
- switch (d.kind) {
1103
- case "alias":
1104
- case "fontStack":
1105
- case "cubicBezier":
1106
- return `var(${seedVar(d.seedRef)})`;
1107
- case "scale": {
1108
- const expr = `calc(var(${seedVar(d.seedRef)}) * ${d.factor})`;
1109
- return d.round === "int" ? `round(nearest, ${expr}, 1px)` : expr;
1110
- }
1111
- case "scaleNumber":
1112
- return `calc(var(${seedVar(d.seedRef)}) * ${d.factor})`;
1113
- case "offsetNumber":
1114
- return `calc(var(${seedVar(d.seedRef)}) ${d.delta >= 0 ? "+" : "-"} ${Math.abs(d.delta)})`;
1115
- case "offsetSteps":
1116
- return `calc(var(${seedVar(d.seedRef)}) ${d.n >= 0 ? "+" : "-"} var(${seedVar(d.stepRef)}) * ${Math.abs(d.n)})`;
1117
- case "oklchRamp": {
1118
- const l = L_TARGETS[d.step];
1119
- const f = round4(chromaFactor(l));
1120
- return `oklch(from var(${seedVar(d.seedRef)}) ${l} calc(c * ${f}) h)`;
1121
- }
1122
- case "figmaOklchScale":
1123
- return d.value;
1124
- case "seedAlpha":
1125
- return `rgb(from var(${seedVar(d.seedRef)}) r g b / ${round4(d.alpha)})`;
1126
- case "alphaScale": {
1127
- const channel = d.channel === "black" ? "0 0 0" : "255 255 255";
1128
- return `rgb(${channel} / calc(var(${seedVar(d.stepRef)}) * ${d.multiplier}))`;
1129
- }
1130
- case "literal":
1131
- return d.value;
1132
- case "shadowLayer": {
1133
- const s2 = shadowShapeExprs(d.level);
1134
- const alpha = `calc(var(${seedVar("size.opacity.alphaStep")}) * ${SHADOW_LEVELS[d.level].alphaMult})`;
1135
- return `${s2.offsetX} ${s2.offsetY} ${s2.blur} ${s2.spread} rgb(0 0 0 / ${alpha})`;
1136
- }
1137
- }
1962
+ switch (d.kind) {
1963
+ case "alias":
1964
+ case "fontStack":
1965
+ case "cubicBezier": return `var(${seedVar(d.seedRef)})`;
1966
+ case "scale": {
1967
+ const expr = `calc(var(${seedVar(d.seedRef)}) * ${d.factor})`;
1968
+ return d.round === "int" ? `round(nearest, ${expr}, 1px)` : expr;
1969
+ }
1970
+ case "scaleNumber": return `calc(var(${seedVar(d.seedRef)}) * ${d.factor})`;
1971
+ case "offsetNumber": return `calc(var(${seedVar(d.seedRef)}) ${d.delta >= 0 ? "+" : "-"} ${Math.abs(d.delta)})`;
1972
+ case "offsetSteps": return `calc(var(${seedVar(d.seedRef)}) ${d.n >= 0 ? "+" : "-"} var(${seedVar(d.stepRef)}) * ${Math.abs(d.n)})`;
1973
+ case "oklchRamp": {
1974
+ const l = L_TARGETS[d.step];
1975
+ const f = round4(chromaFactor(l));
1976
+ return `oklch(from var(${seedVar(d.seedRef)}) ${l} calc(c * ${f}) h)`;
1977
+ }
1978
+ case "figmaOklchScale": return d.value;
1979
+ case "seedAlpha": return `rgb(from var(${seedVar(d.seedRef)}) r g b / ${round4(d.alpha)})`;
1980
+ case "alphaScale": return `rgb(${d.channel === "black" ? "0 0 0" : "255 255 255"} / calc(var(${seedVar(d.stepRef)}) * ${d.multiplier}))`;
1981
+ case "literal": return d.value;
1982
+ case "shadowLayer": {
1983
+ const s = shadowShapeExprs(d.level);
1984
+ const alpha = `calc(var(${seedVar("size.opacity.alphaStep")}) * ${SHADOW_LEVELS[d.level].alphaMult})`;
1985
+ return `${s.offsetX} ${s.offsetY} ${s.blur} ${s.spread} rgb(0 0 0 / ${alpha})`;
1986
+ }
1987
+ }
1138
1988
  }
1139
1989
  function block(selector, decls) {
1140
- return `${selector} {
1141
- ${decls.map(([k, v]) => ` ${k}: ${v};`).join("\n")}
1142
- }
1143
- `;
1990
+ return `${selector} {\n${decls.map(([k, v]) => ` ${k}: ${v};`).join("\n")}\n}\n`;
1144
1991
  }
1992
+ /**
1993
+ * 生成整份 live CSS:
1994
+ * 1. seed 块(具体数值);2. ref 块(表达式派生);3. sem 块(light 别名);
1995
+ * 4. dark 块(仅重映射随 mode 变化的 sem 颜色别名)。
1996
+ */
1145
1997
  function buildLiveCss(seed, options = {}) {
1146
- const selector = options.selector ?? ":root";
1147
- const darkSelector = options.darkSelector ?? '[data-qw-mode="dark"]';
1148
- const seedDecls = Object.entries(seedCssVars(seed));
1149
- const refDecls = allReferenceRecipes().map((r) => [
1150
- r.cssVar,
1151
- derivationToCssExpr(r.derivation)
1152
- ]);
1153
- const semLight = [];
1154
- const semDark = [];
1155
- for (const entry of semanticRoleEntries()) {
1156
- if (entry.kind === "structural") {
1157
- semLight.push([entry.cssVar, `var(${toVar("ref", entry.refPath.split("."))})`]);
1158
- } else if (entry.kind === "color") {
1159
- semLight.push([entry.cssVar, `var(${toVar("ref", entry.refPath.light.split("."))})`]);
1160
- if (entry.refPath.dark !== entry.refPath.light) {
1161
- semDark.push([entry.cssVar, `var(${toVar("ref", entry.refPath.dark.split("."))})`]);
1162
- }
1163
- } else {
1164
- const level = entry.shapeRef.split(".").pop();
1165
- const s2 = shadowShapeExprs(level);
1166
- semLight.push([
1167
- entry.cssVar,
1168
- `${s2.offsetX} ${s2.offsetY} ${s2.blur} ${s2.spread} var(${toVar("sem", entry.colorRole.split("."))})`
1169
- ]);
1170
- }
1171
- }
1172
- return [
1173
- "/* @qw-ui/tokens live \u2014 Seed \u53D8\u91CF\u9A71\u52A8\u7684\u54CD\u5E94\u5F0F\u6D3E\u751F\u6811\uFF08relative color / calc\uFF09\uFF0C\u8BF7\u52FF\u624B\u6539\u3002",
1174
- " \u6539 --qw-seed-* \u5373\u7EA7\u8054\u6574\u68F5 ref/sem \u6811\uFF1B\u9700\u8981\u73B0\u4EE3\u6D4F\u89C8\u5668\uFF0C\u7EC4\u4EF6\u5E93\u6D88\u8D39\u65B9\u8BF7\u7528\u9759\u6001 tokens.css\u3002 */",
1175
- block(selector, [...seedDecls, ...refDecls, ...semLight]),
1176
- block(darkSelector, semDark)
1177
- ].join("\n");
1998
+ const selector = options.selector ?? ":root";
1999
+ const darkSelector = options.darkSelector ?? "[data-qw-mode=\"dark\"]";
2000
+ const seedDecls = Object.entries(seedCssVars(seed));
2001
+ const refDecls = allReferenceRecipes().map((r) => [r.cssVar, derivationToCssExpr(r.derivation)]);
2002
+ const semLight = [];
2003
+ const semDark = [];
2004
+ for (const entry of semanticRoleEntries()) if (entry.kind === "structural") semLight.push([entry.cssVar, `var(${toVar("ref", entry.refPath.split("."))})`]);
2005
+ else if (entry.kind === "color") {
2006
+ semLight.push([entry.cssVar, `var(${toVar("ref", entry.refPath.light.split("."))})`]);
2007
+ if (entry.refPath.dark !== entry.refPath.light) semDark.push([entry.cssVar, `var(${toVar("ref", entry.refPath.dark.split("."))})`]);
2008
+ } else {
2009
+ const s = shadowShapeExprs(entry.shapeRef.split(".").pop());
2010
+ semLight.push([entry.cssVar, `${s.offsetX} ${s.offsetY} ${s.blur} ${s.spread} var(${toVar("sem", entry.colorRole.split("."))})`]);
2011
+ }
2012
+ return [
2013
+ "/* @qw-ui/tokens live — Seed 变量驱动的响应式派生树(relative color / calc),请勿手改。",
2014
+ " 改 --qw-seed-* 即级联整棵 ref/sem 树;需要现代浏览器,组件库消费方请用静态 tokens.css。 */",
2015
+ block(selector, [
2016
+ ...seedDecls,
2017
+ ...refDecls,
2018
+ ...semLight
2019
+ ]),
2020
+ block(darkSelector, semDark)
2021
+ ].join("\n");
2022
+ }
2023
+ //#endregion
2024
+ //#region src/compat/schema.ts
2025
+ /** DTCG 兼容的颜色字面量:hex、hex/NN% 透明度后缀,或结构化 srgb。 */
2026
+ const legacyHexColor = z.string().regex(/^#[0-9A-Fa-f]{6}(\/\d{1,3}%)?$/, "期望 #RRGGBB 或 #RRGGBB/NN%");
2027
+ const structuredColor = z.object({
2028
+ colorSpace: z.literal("srgb"),
2029
+ components: z.tuple([
2030
+ z.number(),
2031
+ z.number(),
2032
+ z.number()
2033
+ ]),
2034
+ alpha: z.number().min(0).max(1)
2035
+ });
2036
+ const dimensionValue = z.object({
2037
+ value: z.number(),
2038
+ unit: z.enum([
2039
+ "px",
2040
+ "rem",
2041
+ "em",
2042
+ "%"
2043
+ ])
2044
+ });
2045
+ const modePair = z.object({
2046
+ light: z.union([
2047
+ legacyHexColor,
2048
+ structuredColor,
2049
+ z.string()
2050
+ ]),
2051
+ dark: z.union([
2052
+ legacyHexColor,
2053
+ structuredColor,
2054
+ z.string()
2055
+ ])
2056
+ });
2057
+ const compatStatusSchema = z.enum([
2058
+ "active",
2059
+ "aliased",
2060
+ "deprecated"
2061
+ ]);
2062
+ const compatEntrySchema = z.object({
2063
+ path: z.string().regex(/^[a-z][a-z0-9-]*\.[a-z][a-z0-9.-]*$/, "path 格式:<component>.<category>.<name>,如 button.color.bg-soft-brand"),
2064
+ type: z.enum([
2065
+ "color",
2066
+ "dimension",
2067
+ "typography",
2068
+ "motion"
2069
+ ]),
2070
+ value: z.union([
2071
+ modePair,
2072
+ dimensionValue,
2073
+ z.object({ aliasOf: z.string().min(1) })
2074
+ ]),
2075
+ source: z.object({
2076
+ figma: z.string().url(),
2077
+ nodeId: z.string().min(1),
2078
+ legacyName: z.string().nullable()
2079
+ }),
2080
+ introducedBy: z.string().min(1),
2081
+ reason: z.string().min(1),
2082
+ owner: z.string().min(1),
2083
+ targetSemanticRole: z.string().nullable(),
2084
+ status: compatStatusSchema,
2085
+ reviewAfter: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
2086
+ migrationIssue: z.string().nullable()
2087
+ }).superRefine((entry, ctx) => {
2088
+ if (entry.status === "aliased") {
2089
+ if (!("aliasOf" in entry.value)) ctx.addIssue({
2090
+ code: "custom",
2091
+ message: "status=aliased 时 value 必须为 { aliasOf }",
2092
+ path: ["value"]
2093
+ });
2094
+ if (!entry.targetSemanticRole) ctx.addIssue({
2095
+ code: "custom",
2096
+ message: "status=aliased 时 targetSemanticRole 必填",
2097
+ path: ["targetSemanticRole"]
2098
+ });
2099
+ }
2100
+ if (entry.status === "active" && "aliasOf" in entry.value) ctx.addIssue({
2101
+ code: "custom",
2102
+ message: "status=active 时不得使用 aliasOf",
2103
+ path: ["value"]
2104
+ });
2105
+ });
2106
+ const compatRegistrySchema = z.object({
2107
+ $description: z.string().optional(),
2108
+ version: z.literal(1),
2109
+ graceDays: z.number().int().positive().default(90),
2110
+ entries: z.array(compatEntrySchema).min(1)
2111
+ });
2112
+ /** path → CSS 变量名:`button.color.bg-soft-brand` → `--qw-compat-button-color-bg-soft-brand` */
2113
+ function compatPathToCssVar(path) {
2114
+ return `--qw-compat-${path.replace(/\./g, "-")}`;
2115
+ }
2116
+ /** path → component slug(第一段) */
2117
+ function compatComponentOf(path) {
2118
+ return path.split(".")[0];
2119
+ }
2120
+ /** path → helper suffix(component 之后的段,用 `-` 连接) */
2121
+ function compatSuffixOf(path) {
2122
+ return path.split(".").slice(1).join("-");
2123
+ }
2124
+ //#endregion
2125
+ //#region src/compat/color.ts
2126
+ /**
2127
+ * 将 legacy hex(含 /NN% 透明度后缀)或 DTCG srgb 结构转为 CSS 颜色字符串。
2128
+ */
2129
+ function compatColorToCss(input) {
2130
+ if (typeof input === "string") {
2131
+ const m = /^#([0-9A-Fa-f]{6})(?:\/(\d{1,3})%)?$/.exec(input);
2132
+ if (!m) throw new Error(`[qw-compat] 无法解析颜色: ${input}`);
2133
+ const n = Number.parseInt(m[1], 16);
2134
+ const r = n >> 16 & 255;
2135
+ const g = n >> 8 & 255;
2136
+ const b = n & 255;
2137
+ const alpha = m[2] != null ? Number(m[2]) / 100 : 1;
2138
+ if (alpha >= 1) return `#${m[1].toUpperCase()}`;
2139
+ return `rgba(${r}, ${g}, ${b}, ${roundAlpha(alpha)})`;
2140
+ }
2141
+ const [r, g, b] = input.components.map((c) => Math.round(c * 255));
2142
+ const alpha = input.alpha;
2143
+ if (alpha >= 1) return `#${[
2144
+ r,
2145
+ g,
2146
+ b
2147
+ ].map((x) => x.toString(16).padStart(2, "0")).join("")}`;
2148
+ return `rgba(${r}, ${g}, ${b}, ${roundAlpha(alpha)})`;
2149
+ }
2150
+ function roundAlpha(a) {
2151
+ return Math.round(a * 1e3) / 1e3;
2152
+ }
2153
+ //#endregion
2154
+ //#region src/compat/build.ts
2155
+ function dimensionToCss(value) {
2156
+ return `${value.value}${value.unit}`;
2157
+ }
2158
+ function resolveEntryValue(entry, mode, semCssVar) {
2159
+ const v = entry.value;
2160
+ if ("aliasOf" in v) return `var(${semCssVar(v.aliasOf)})`;
2161
+ if ("light" in v && "dark" in v) {
2162
+ const raw = v[mode];
2163
+ if (entry.type === "color") return compatColorToCss(raw);
2164
+ return String(raw);
2165
+ }
2166
+ if ("value" in v && "unit" in v) return dimensionToCss(v);
2167
+ throw new Error(`[qw-compat] 条目 ${entry.path} 的 value 形状无法解析`);
2168
+ }
2169
+ /** semantic role 点分路径 → --qw-sem-* CSS 变量 */
2170
+ function semRoleToCssVar(role) {
2171
+ const parts = role.split(".");
2172
+ return `--qw-sem-${parts[0]}-${parts.slice(1).join("-")}`;
2173
+ }
2174
+ function buildCompatFromRegistry(registry) {
2175
+ const light = {};
2176
+ const dark = {};
2177
+ const paths = {};
2178
+ const byComponent = {};
2179
+ for (const entry of registry.entries) {
2180
+ const cssVar = compatPathToCssVar(entry.path);
2181
+ paths[entry.path] = cssVar;
2182
+ const component = compatComponentOf(entry.path);
2183
+ const suffix = compatSuffixOf(entry.path);
2184
+ if (!byComponent[component]) byComponent[component] = [];
2185
+ byComponent[component].push(suffix);
2186
+ const resolve = (mode) => resolveEntryValue(entry, mode, semRoleToCssVar);
2187
+ if ("light" in entry.value && "dark" in entry.value) {
2188
+ light[cssVar] = resolve("light");
2189
+ const darkVal = resolve("dark");
2190
+ if (darkVal !== light[cssVar]) dark[cssVar] = darkVal;
2191
+ } else light[cssVar] = resolve("light");
2192
+ }
2193
+ return {
2194
+ light,
2195
+ dark,
2196
+ paths,
2197
+ byComponent,
2198
+ entries: registry.entries,
2199
+ graceDays: registry.graceDays
2200
+ };
2201
+ }
2202
+ function componentSuffixTypeName(component) {
2203
+ return `Compat${component.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("")}Suffix`;
2204
+ }
2205
+ function componentSuffixConstName(component) {
2206
+ return `compat${component.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("")}Suffixes`;
2207
+ }
2208
+ /** 生成 compat token-paths TypeScript 片段(并入 tokens.ts) */
2209
+ function formatCompatTypes(build) {
2210
+ const components = Object.keys(build.byComponent).sort();
2211
+ const lines = ["export const compatComponents = " + JSON.stringify(components) + " as const;", "export type CompatComponent = (typeof compatComponents)[number];"];
2212
+ for (const c of components) {
2213
+ const suffixes = [...build.byComponent[c]].sort();
2214
+ const constName = componentSuffixConstName(c);
2215
+ const typeName = componentSuffixTypeName(c);
2216
+ lines.push(`export const ${constName} = ${JSON.stringify(suffixes)} as const;`);
2217
+ lines.push(`export type ${typeName} = (typeof ${constName})[number];`);
2218
+ }
2219
+ if (components.length === 1) {
2220
+ const c = components[0];
2221
+ lines.push(`export type CompatSuffix<C extends CompatComponent> = C extends ${JSON.stringify(c)} ? ${componentSuffixTypeName(c)} : never;`);
2222
+ } else if (components.length > 1) {
2223
+ lines.push("export type CompatSuffix<C extends CompatComponent> =");
2224
+ for (const c of components) lines.push(` C extends ${JSON.stringify(c)} ? ${componentSuffixTypeName(c)} :`);
2225
+ lines.push(" never;");
2226
+ }
2227
+ return lines.join("\n");
1178
2228
  }
2229
+ //#endregion
2230
+ export { COLOR_ROLES, FIGMA_COLOR_COLLECTION, FIGMA_COLOR_PREFIX, FIGMA_T1_SEED_HEX, FIGMA_T2_SOLID_SCALE, FIGMA_T3_COLOR_ROLES, L_TARGETS, RAMP_COLORS, SHADOW_COMPOSITE_ROLES, STEPS, SeedSchema, T1_COLOR_KEYS, T2_ALPHA_KEYS, T2_ALPHA_STEPS, T2_OKLCH_STEPS, TYPOGRAPHY_ROLES, allReferenceRecipes, buildCompatFromRegistry, buildLiveCss, buildReference, buildSemanticColor, buildSemanticStructural, compatPathToCssVar, compatRegistrySchema, contrastRatio, defaultSeed, derivationToCssExpr, derive, evaluateDerivation, formatCompatTypes, formatDerivation, hexToSeedColor, ramp, seedColorToCss, seedColorToHex, seedCssVars, seedRefsOf, seedValueAt, semanticRoleEntries, toVar, validateSeed };
1179
2231
 
1180
- export { COLOR_ROLES, FIGMA_COLOR_COLLECTION, FIGMA_COLOR_PREFIX, FIGMA_T1_SEED_HEX, FIGMA_T2_SOLID_SCALE, FIGMA_T3_COLOR_ROLES, L_TARGETS, RAMP_COLORS, SHADOW_COMPOSITE_ROLES, STEPS, SeedSchema, T1_COLOR_KEYS, T2_ALPHA_KEYS, T2_ALPHA_STEPS, T2_OKLCH_STEPS, TYPOGRAPHY_ROLES, allReferenceRecipes, buildLiveCss, buildReference, buildSemanticColor, buildSemanticStructural, contrastRatio, defaultSeed, derivationToCssExpr, derive, evaluateDerivation, formatDerivation, hexToSeedColor, ramp, seedColorToCss, seedColorToHex, seedCssVars, seedRefsOf, seedValueAt, semanticRoleEntries, toVar, validateSeed };
1181
- //# sourceMappingURL=index.js.map
1182
2232
  //# sourceMappingURL=index.js.map