@kbach/react 0.6.29 → 0.6.31

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.
@@ -1,554 +1,5 @@
1
1
  'use client';
2
2
 
3
- // src/core/theme.ts
4
- var defaultColors = {
5
- transparent: "transparent",
6
- current: "currentColor",
7
- black: "#000000",
8
- white: "#ffffff",
9
- slate: {
10
- 1: "#f8fafc",
11
- 2: "#f1f5f9",
12
- 3: "#e2e8f0",
13
- 4: "#cbd5e1",
14
- 5: "#94a3b8",
15
- 6: "#64748b",
16
- 7: "#475569",
17
- 8: "#334155",
18
- 9: "#1e293b",
19
- 10: "#0f172a",
20
- 11: "#020617",
21
- 12: "#01020a"
22
- },
23
- gray: {
24
- 1: "#f9fafb",
25
- 2: "#f3f4f6",
26
- 3: "#e5e7eb",
27
- 4: "#d1d5db",
28
- 5: "#9ca3af",
29
- 6: "#6b7280",
30
- 7: "#4b5563",
31
- 8: "#374151",
32
- 9: "#1f2937",
33
- 10: "#111827",
34
- 11: "#030712",
35
- 12: "#020409"
36
- },
37
- zinc: {
38
- 1: "#fafafa",
39
- 2: "#f4f4f5",
40
- 3: "#e4e4e7",
41
- 4: "#d4d4d8",
42
- 5: "#a1a1aa",
43
- 6: "#71717a",
44
- 7: "#52525b",
45
- 8: "#3f3f46",
46
- 9: "#27272a",
47
- 10: "#18181b",
48
- 11: "#09090b",
49
- 12: "#050506"
50
- },
51
- neutral: {
52
- 1: "#fafafa",
53
- 2: "#f5f5f5",
54
- 3: "#e5e5e5",
55
- 4: "#d4d4d4",
56
- 5: "#a3a3a3",
57
- 6: "#737373",
58
- 7: "#525252",
59
- 8: "#404040",
60
- 9: "#262626",
61
- 10: "#171717",
62
- 11: "#0a0a0a",
63
- 12: "#050505"
64
- },
65
- stone: {
66
- 1: "#fafaf9",
67
- 2: "#f5f5f4",
68
- 3: "#e7e5e4",
69
- 4: "#d6d3d1",
70
- 5: "#a8a29e",
71
- 6: "#78716c",
72
- 7: "#57534e",
73
- 8: "#44403c",
74
- 9: "#292524",
75
- 10: "#1c1917",
76
- 11: "#0c0a09",
77
- 12: "#070605"
78
- },
79
- red: {
80
- 1: "#fef2f2",
81
- 2: "#fee2e2",
82
- 3: "#fecaca",
83
- 4: "#fca5a5",
84
- 5: "#f87171",
85
- 6: "#ef4444",
86
- 7: "#dc2626",
87
- 8: "#b91c1c",
88
- 9: "#991b1b",
89
- 10: "#7f1d1d",
90
- 11: "#450a0a",
91
- 12: "#280606"
92
- },
93
- orange: {
94
- 1: "#fff7ed",
95
- 2: "#ffedd5",
96
- 3: "#fed7aa",
97
- 4: "#fdba74",
98
- 5: "#fb923c",
99
- 6: "#f97316",
100
- 7: "#ea580c",
101
- 8: "#c2410c",
102
- 9: "#9a3412",
103
- 10: "#7c2d12",
104
- 11: "#431407",
105
- 12: "#270c04"
106
- },
107
- amber: {
108
- 1: "#fffbeb",
109
- 2: "#fef3c7",
110
- 3: "#fde68a",
111
- 4: "#fcd34d",
112
- 5: "#fbbf24",
113
- 6: "#f59e0b",
114
- 7: "#d97706",
115
- 8: "#b45309",
116
- 9: "#92400e",
117
- 10: "#78350f",
118
- 11: "#451a03",
119
- 12: "#291002"
120
- },
121
- yellow: {
122
- 1: "#fefce8",
123
- 2: "#fef9c3",
124
- 3: "#fef08a",
125
- 4: "#fde047",
126
- 5: "#facc15",
127
- 6: "#eab308",
128
- 7: "#ca8a04",
129
- 8: "#a16207",
130
- 9: "#854d0e",
131
- 10: "#713f12",
132
- 11: "#422006",
133
- 12: "#271304"
134
- },
135
- lime: {
136
- 1: "#f7fee7",
137
- 2: "#ecfccb",
138
- 3: "#d9f99d",
139
- 4: "#bef264",
140
- 5: "#a3e635",
141
- 6: "#84cc16",
142
- 7: "#65a30d",
143
- 8: "#4d7c0f",
144
- 9: "#3f6212",
145
- 10: "#365314",
146
- 11: "#1a2e05",
147
- 12: "#0f1b03"
148
- },
149
- green: {
150
- 1: "#f0fdf4",
151
- 2: "#dcfce7",
152
- 3: "#bbf7d0",
153
- 4: "#86efac",
154
- 5: "#4ade80",
155
- 6: "#22c55e",
156
- 7: "#16a34a",
157
- 8: "#15803d",
158
- 9: "#166534",
159
- 10: "#14532d",
160
- 11: "#052e16",
161
- 12: "#031b0d"
162
- },
163
- emerald: {
164
- 1: "#ecfdf5",
165
- 2: "#d1fae5",
166
- 3: "#a7f3d0",
167
- 4: "#6ee7b7",
168
- 5: "#34d399",
169
- 6: "#10b981",
170
- 7: "#059669",
171
- 8: "#047857",
172
- 9: "#065f46",
173
- 10: "#064e3b",
174
- 11: "#022c22",
175
- 12: "#011a14"
176
- },
177
- teal: {
178
- 1: "#f0fdfa",
179
- 2: "#ccfbf1",
180
- 3: "#99f6e4",
181
- 4: "#5eead4",
182
- 5: "#2dd4bf",
183
- 6: "#14b8a6",
184
- 7: "#0d9488",
185
- 8: "#0f766e",
186
- 9: "#115e59",
187
- 10: "#134e4a",
188
- 11: "#042f2e",
189
- 12: "#021c1b"
190
- },
191
- cyan: {
192
- 1: "#ecfeff",
193
- 2: "#cffafe",
194
- 3: "#a5f3fc",
195
- 4: "#67e8f9",
196
- 5: "#22d3ee",
197
- 6: "#06b6d4",
198
- 7: "#0891b2",
199
- 8: "#0e7490",
200
- 9: "#155e75",
201
- 10: "#164e63",
202
- 11: "#083344",
203
- 12: "#041e28"
204
- },
205
- sky: {
206
- 1: "#f0f9ff",
207
- 2: "#e0f2fe",
208
- 3: "#bae6fd",
209
- 4: "#7dd3fc",
210
- 5: "#38bdf8",
211
- 6: "#0ea5e9",
212
- 7: "#0284c7",
213
- 8: "#0369a1",
214
- 9: "#075985",
215
- 10: "#0c4a6e",
216
- 11: "#082f49",
217
- 12: "#041b2b"
218
- },
219
- blue: {
220
- 1: "#eff6ff",
221
- 2: "#dbeafe",
222
- 3: "#bfdbfe",
223
- 4: "#93c5fd",
224
- 5: "#60a5fa",
225
- 6: "#3b82f6",
226
- 7: "#2563eb",
227
- 8: "#1d4ed8",
228
- 9: "#1e40af",
229
- 10: "#1e3a8a",
230
- 11: "#172554",
231
- 12: "#0d1633"
232
- },
233
- indigo: {
234
- 1: "#eef2ff",
235
- 2: "#e0e7ff",
236
- 3: "#c7d2fe",
237
- 4: "#a5b4fc",
238
- 5: "#818cf8",
239
- 6: "#6366f1",
240
- 7: "#4f46e5",
241
- 8: "#4338ca",
242
- 9: "#3730a3",
243
- 10: "#312e81",
244
- 11: "#1e1b4b",
245
- 12: "#12102d"
246
- },
247
- violet: {
248
- 1: "#f5f3ff",
249
- 2: "#ede9fe",
250
- 3: "#ddd6fe",
251
- 4: "#c4b5fd",
252
- 5: "#a78bfa",
253
- 6: "#8b5cf6",
254
- 7: "#7c3aed",
255
- 8: "#6d28d9",
256
- 9: "#5b21b6",
257
- 10: "#4c1d95",
258
- 11: "#2e1065",
259
- 12: "#1c0a3d"
260
- },
261
- purple: {
262
- 1: "#faf5ff",
263
- 2: "#f3e8ff",
264
- 3: "#e9d5ff",
265
- 4: "#d8b4fe",
266
- 5: "#c084fc",
267
- 6: "#a855f7",
268
- 7: "#9333ea",
269
- 8: "#7e22ce",
270
- 9: "#6b21a8",
271
- 10: "#581c87",
272
- 11: "#3b0764",
273
- 12: "#23043c"
274
- },
275
- fuchsia: {
276
- 1: "#fdf4ff",
277
- 2: "#fae8ff",
278
- 3: "#f5d0fe",
279
- 4: "#f0abfc",
280
- 5: "#e879f9",
281
- 6: "#d946ef",
282
- 7: "#c026d3",
283
- 8: "#a21caf",
284
- 9: "#86198f",
285
- 10: "#701a75",
286
- 11: "#4a044e",
287
- 12: "#2d022f"
288
- },
289
- pink: {
290
- 1: "#fdf2f8",
291
- 2: "#fce7f3",
292
- 3: "#fbcfe8",
293
- 4: "#f9a8d4",
294
- 5: "#f472b6",
295
- 6: "#ec4899",
296
- 7: "#db2777",
297
- 8: "#be185d",
298
- 9: "#9d174d",
299
- 10: "#831843",
300
- 11: "#500724",
301
- 12: "#300415"
302
- },
303
- rose: {
304
- 1: "#fff1f2",
305
- 2: "#ffe4e6",
306
- 3: "#fecdd3",
307
- 4: "#fda4af",
308
- 5: "#fb7185",
309
- 6: "#f43f5e",
310
- 7: "#e11d48",
311
- 8: "#be123c",
312
- 9: "#9f1239",
313
- 10: "#881337",
314
- 11: "#4c0519",
315
- 12: "#2d030e"
316
- }
317
- };
318
- var defaultTheme = {
319
- colors: defaultColors,
320
- // 1 unit = 4px
321
- spacing: {
322
- px: 1,
323
- 0: 0,
324
- "0.5": 2,
325
- 1: 4,
326
- "1.5": 6,
327
- 2: 8,
328
- "2.5": 10,
329
- 3: 12,
330
- "3.5": 14,
331
- 4: 16,
332
- 5: 20,
333
- 6: 24,
334
- 7: 28,
335
- 8: 32,
336
- 9: 36,
337
- 10: 40,
338
- 11: 44,
339
- 12: 48,
340
- 14: 56,
341
- 16: 64,
342
- 20: 80,
343
- 24: 96,
344
- 28: 112,
345
- 32: 128,
346
- 36: 144,
347
- 40: 160,
348
- 44: 176,
349
- 48: 192,
350
- 52: 208,
351
- 56: 224,
352
- 60: 240,
353
- 64: 256,
354
- 72: 288,
355
- 80: 320,
356
- 96: 384,
357
- auto: "auto",
358
- full: "100%",
359
- "1/2": "50%",
360
- "1/3": "33.333333%",
361
- "2/3": "66.666667%",
362
- "1/4": "25%",
363
- "3/4": "75%",
364
- // Dynamic viewport unit — see the comment on the 'screen' standalone utilities
365
- // in utilities.ts for why dvh beats vh on mobile. Used by h-screen (w-screen
366
- // has its own standalone entry so it never reaches this spacing lookup).
367
- screen: "100dvh",
368
- min: "min-content",
369
- max: "max-content",
370
- fit: "fit-content"
371
- },
372
- fontSize: {
373
- xs: 12,
374
- sm: 14,
375
- base: 16,
376
- lg: 18,
377
- xl: 20,
378
- "2xl": 24,
379
- "3xl": 30,
380
- "4xl": 36,
381
- "5xl": 48,
382
- "6xl": 60,
383
- "7xl": 72,
384
- "8xl": 96,
385
- "9xl": 128
386
- },
387
- fontFamily: {
388
- sans: "System",
389
- mono: "Courier New",
390
- serif: "Georgia"
391
- },
392
- fontWeight: {
393
- thin: "100",
394
- extralight: "200",
395
- light: "300",
396
- normal: "400",
397
- medium: "500",
398
- semibold: "600",
399
- bold: "700",
400
- extrabold: "800",
401
- black: "900"
402
- },
403
- borderRadius: {
404
- none: 0,
405
- sm: 2,
406
- DEFAULT: 4,
407
- md: 6,
408
- lg: 8,
409
- xl: 12,
410
- "2xl": 16,
411
- "3xl": 24,
412
- full: 9999
413
- },
414
- borderWidth: {
415
- DEFAULT: 1,
416
- 0: 0,
417
- 2: 2,
418
- 4: 4,
419
- 8: 8
420
- },
421
- opacity: {
422
- 0: 0,
423
- 5: 0.05,
424
- 10: 0.1,
425
- 15: 0.15,
426
- 20: 0.2,
427
- 25: 0.25,
428
- 30: 0.3,
429
- 40: 0.4,
430
- 50: 0.5,
431
- 60: 0.6,
432
- 70: 0.7,
433
- 75: 0.75,
434
- 80: 0.8,
435
- 90: 0.9,
436
- 95: 0.95,
437
- 100: 1
438
- },
439
- lineHeight: {
440
- none: 1,
441
- tight: 1.25,
442
- snug: 1.375,
443
- normal: 1.5,
444
- relaxed: 1.625,
445
- loose: 2,
446
- // Pixel values stored as strings; styleValueToCSS passes them through, toNativeValue strips the unit
447
- 3: "12px",
448
- 4: "16px",
449
- 5: "20px",
450
- 6: "24px",
451
- 7: "28px",
452
- 8: "32px",
453
- 9: "36px",
454
- 10: "40px"
455
- },
456
- letterSpacing: {
457
- tighter: -0.8,
458
- tight: -0.4,
459
- normal: 0,
460
- wide: 0.4,
461
- wider: 0.8,
462
- widest: 1.6
463
- },
464
- zIndex: {
465
- auto: "auto",
466
- 0: 0,
467
- 10: 10,
468
- 20: 20,
469
- 30: 30,
470
- 40: 40,
471
- 50: 50
472
- },
473
- flex: {
474
- 1: 1,
475
- auto: "auto",
476
- // CSS: flex: auto = 1 1 auto; native: mapped to 1 in resolver
477
- initial: "initial",
478
- // CSS: flex: initial = 0 1 auto; native: mapped to 1
479
- none: "none"
480
- // CSS: flex: none = 0 0 auto; native: mapped to 0
481
- },
482
- shadow: {
483
- sm: {
484
- shadowColor: "#000",
485
- shadowOffset: { width: 0, height: 1 },
486
- shadowOpacity: 0.05,
487
- shadowRadius: 2,
488
- elevation: 1
489
- },
490
- DEFAULT: {
491
- shadowColor: "#000",
492
- shadowOffset: { width: 0, height: 2 },
493
- shadowOpacity: 0.1,
494
- shadowRadius: 4,
495
- elevation: 2
496
- },
497
- md: {
498
- shadowColor: "#000",
499
- shadowOffset: { width: 0, height: 4 },
500
- shadowOpacity: 0.1,
501
- shadowRadius: 8,
502
- elevation: 3
503
- },
504
- lg: {
505
- shadowColor: "#000",
506
- shadowOffset: { width: 0, height: 8 },
507
- shadowOpacity: 0.1,
508
- shadowRadius: 15,
509
- elevation: 4
510
- },
511
- xl: {
512
- shadowColor: "#000",
513
- shadowOffset: { width: 0, height: 16 },
514
- shadowOpacity: 0.1,
515
- shadowRadius: 24,
516
- elevation: 6
517
- },
518
- "2xl": {
519
- shadowColor: "#000",
520
- shadowOffset: { width: 0, height: 24 },
521
- shadowOpacity: 0.25,
522
- shadowRadius: 48,
523
- elevation: 8
524
- },
525
- none: {
526
- shadowColor: "transparent",
527
- shadowOffset: { width: 0, height: 0 },
528
- shadowOpacity: 0,
529
- shadowRadius: 0,
530
- elevation: 0
531
- }
532
- },
533
- screens: {
534
- sm: 576,
535
- // small tablets / large phones (landscape)
536
- md: 768,
537
- // tablets (iPad and up)
538
- lg: 1024,
539
- // large tablets (iPad Pro)
540
- xl: 1280,
541
- // desktop
542
- "2xl": 1536
543
- },
544
- // Empty by default — the 4 built-in presets (spin/ping/pulse/bounce) are handled
545
- // directly in the `animate` resolver, not through this theme table. This exists so
546
- // user-defined ones (kbach.config.js theme.extend.keyframes/animation) merge in
547
- // alongside them without needing to redeclare the built-ins.
548
- keyframes: {},
549
- animation: {}
550
- };
551
-
552
3
  // src/core/platform.ts
553
4
  var isWeb = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
554
5
  var isNative = !isWeb && (typeof globalThis.HermesInternal !== "undefined" || typeof globalThis.__fbBatchedBridge !== "undefined" || typeof navigator !== "undefined" && navigator.product === "ReactNative" || typeof globalThis.__REACT_NATIVE__ !== "undefined");
@@ -756,7 +207,15 @@ function matchModifier(name, isDark, state, breakpoints) {
756
207
  return getModifier(name)?.jsMatch?.(isDark, state, breakpoints) ?? false;
757
208
  }
758
209
 
210
+ // src/core/colorValue.ts
211
+ function isModeAwareColor(v) {
212
+ return typeof v === "object" && v !== null && "light" in v && "dark" in v;
213
+ }
214
+
759
215
  // src/core/resolvers/color.ts
216
+ function pickModeAwareFallback(v) {
217
+ return isModeAwareColor(v) ? v.light : v;
218
+ }
760
219
  function resolveColor(value, colors, isArbitrary) {
761
220
  if (isArbitrary) return value;
762
221
  const slashIdx = value.indexOf("/");
@@ -768,15 +227,17 @@ function resolveColor(value, colors, isArbitrary) {
768
227
  } else if (colorPart in colors) {
769
228
  const entry = colors[colorPart];
770
229
  if (typeof entry === "string") hex = entry;
771
- else if (typeof entry === "object" && "6" in entry) hex = entry["6"];
230
+ else if (isModeAwareColor(entry)) hex = pickModeAwareFallback(entry);
231
+ else if (typeof entry === "object" && "6" in entry) hex = pickModeAwareFallback(entry["6"]);
772
232
  } else {
773
233
  const lastDash = colorPart.lastIndexOf("-");
774
234
  if (lastDash > 0) {
775
235
  const colorName = colorPart.slice(0, lastDash);
776
236
  const shade = colorPart.slice(lastDash + 1);
777
237
  const scale = colors[colorName];
778
- if (scale && typeof scale === "object" && shade in scale) {
779
- hex = scale[shade] ?? null;
238
+ if (scale && typeof scale === "object" && !isModeAwareColor(scale) && shade in scale) {
239
+ const shadeVal = scale[shade];
240
+ hex = shadeVal !== void 0 ? pickModeAwareFallback(shadeVal) : null;
780
241
  }
781
242
  }
782
243
  }
@@ -1896,816 +1357,1421 @@ var layoutResolvers = {
1896
1357
  const flow = flows[value];
1897
1358
  return flow ? { gridAutoFlow: flow } : null;
1898
1359
  },
1899
- // ── Grid auto sizing (web-only) ───────────────────────────────────────────
1900
- "auto-cols": ({ value, isArbitrary }) => {
1360
+ // ── Grid auto sizing (web-only) ───────────────────────────────────────────
1361
+ "auto-cols": ({ value, isArbitrary }) => {
1362
+ if (!getEffectiveIsWeb()) return null;
1363
+ if (isArbitrary) return { gridAutoColumns: value };
1364
+ const presets = {
1365
+ auto: "auto",
1366
+ min: "min-content",
1367
+ max: "max-content",
1368
+ fr: "minmax(0, 1fr)"
1369
+ };
1370
+ const v = presets[value];
1371
+ return v ? { gridAutoColumns: v } : null;
1372
+ },
1373
+ "auto-rows": ({ value, isArbitrary }) => {
1374
+ if (!getEffectiveIsWeb()) return null;
1375
+ if (isArbitrary) return { gridAutoRows: value };
1376
+ const presets = {
1377
+ auto: "auto",
1378
+ min: "min-content",
1379
+ max: "max-content",
1380
+ fr: "minmax(0, 1fr)"
1381
+ };
1382
+ const v = presets[value];
1383
+ return v ? { gridAutoRows: v } : null;
1384
+ },
1385
+ // ── Grid column placement (web-only) ──────────────────────────────────────
1386
+ "col-span": ({ value }) => {
1387
+ if (!getEffectiveIsWeb()) return null;
1388
+ if (value === "full") return { gridColumn: "1 / -1" };
1389
+ const n = parseInt(value, 10);
1390
+ return isNaN(n) ? null : { gridColumn: `span ${n} / span ${n}` };
1391
+ },
1392
+ "col-start": ({ value, isArbitrary }) => {
1393
+ if (!getEffectiveIsWeb()) return null;
1394
+ if (value === "auto") return { gridColumnStart: "auto" };
1395
+ if (isArbitrary) return { gridColumnStart: value };
1396
+ const n = parseInt(value, 10);
1397
+ return isNaN(n) ? null : { gridColumnStart: n };
1398
+ },
1399
+ "col-end": ({ value, isArbitrary }) => {
1400
+ if (!getEffectiveIsWeb()) return null;
1401
+ if (value === "auto") return { gridColumnEnd: "auto" };
1402
+ if (isArbitrary) return { gridColumnEnd: value };
1403
+ const n = parseInt(value, 10);
1404
+ return isNaN(n) ? null : { gridColumnEnd: n };
1405
+ },
1406
+ col: ({ value, isArbitrary }) => {
1407
+ if (!getEffectiveIsWeb()) return null;
1408
+ if (!value || value === "auto") return { gridColumn: "auto" };
1409
+ if (isArbitrary) return { gridColumn: value };
1410
+ return null;
1411
+ },
1412
+ // ── Grid row placement (web-only) ─────────────────────────────────────────
1413
+ "row-span": ({ value }) => {
1414
+ if (!getEffectiveIsWeb()) return null;
1415
+ if (value === "full") return { gridRow: "1 / -1" };
1416
+ const n = parseInt(value, 10);
1417
+ return isNaN(n) ? null : { gridRow: `span ${n} / span ${n}` };
1418
+ },
1419
+ "row-start": ({ value, isArbitrary }) => {
1420
+ if (!getEffectiveIsWeb()) return null;
1421
+ if (value === "auto") return { gridRowStart: "auto" };
1422
+ if (isArbitrary) return { gridRowStart: value };
1423
+ const n = parseInt(value, 10);
1424
+ return isNaN(n) ? null : { gridRowStart: n };
1425
+ },
1426
+ "row-end": ({ value, isArbitrary }) => {
1427
+ if (!getEffectiveIsWeb()) return null;
1428
+ if (value === "auto") return { gridRowEnd: "auto" };
1429
+ if (isArbitrary) return { gridRowEnd: value };
1430
+ const n = parseInt(value, 10);
1431
+ return isNaN(n) ? null : { gridRowEnd: n };
1432
+ },
1433
+ row: ({ value, isArbitrary }) => {
1434
+ if (!getEffectiveIsWeb()) return null;
1435
+ if (!value || value === "auto") return { gridRow: "auto" };
1436
+ if (isArbitrary) return { gridRow: value };
1437
+ return null;
1438
+ },
1439
+ // ── Grid alignment (web-only) ──────────────────────────────────────────────
1440
+ // place-items is shorthand for align-items + justify-items on a grid container
1441
+ "place-items": ({ value }) => {
1442
+ if (!getEffectiveIsWeb()) return null;
1443
+ const map = {
1444
+ start: "start",
1445
+ end: "end",
1446
+ center: "center",
1447
+ stretch: "stretch",
1448
+ baseline: "baseline"
1449
+ };
1450
+ return map[value] ? { placeItems: map[value] } : null;
1451
+ },
1452
+ // place-content is shorthand for align-content + justify-content on a grid container
1453
+ "place-content": ({ value }) => {
1454
+ if (!getEffectiveIsWeb()) return null;
1455
+ const map = {
1456
+ start: "start",
1457
+ end: "end",
1458
+ center: "center",
1459
+ stretch: "stretch",
1460
+ between: "space-between",
1461
+ around: "space-around",
1462
+ evenly: "space-evenly",
1463
+ baseline: "baseline"
1464
+ };
1465
+ return map[value] ? { placeContent: map[value] } : null;
1466
+ },
1467
+ // justify-items controls inline-axis alignment of grid items within their cells
1468
+ "justify-items": ({ value }) => {
1469
+ if (!getEffectiveIsWeb()) return null;
1470
+ const map = {
1471
+ start: "start",
1472
+ end: "end",
1473
+ center: "center",
1474
+ stretch: "stretch"
1475
+ };
1476
+ return map[value] ? { justifyItems: map[value] } : null;
1477
+ },
1478
+ // place-self is shorthand for align-self + justify-self on a grid item
1479
+ "place-self": ({ value }) => {
1480
+ if (!getEffectiveIsWeb()) return null;
1481
+ const map = {
1482
+ auto: "auto",
1483
+ start: "start",
1484
+ end: "end",
1485
+ center: "center",
1486
+ stretch: "stretch"
1487
+ };
1488
+ return map[value] ? { placeSelf: map[value] } : null;
1489
+ },
1490
+ // justify-self controls inline-axis self-alignment of a grid item
1491
+ "justify-self": ({ value }) => {
1492
+ if (!getEffectiveIsWeb()) return null;
1493
+ const map = {
1494
+ auto: "auto",
1495
+ start: "start",
1496
+ end: "end",
1497
+ center: "center",
1498
+ stretch: "stretch"
1499
+ };
1500
+ return map[value] ? { justifySelf: map[value] } : null;
1501
+ },
1502
+ // ── Flex ───────────────────────────────────────────────────────────────────
1503
+ // flex-1/flex-auto/flex-none/grow/shrink are item-level sizing properties —
1504
+ // deliberately NOT implying display:flex here. See the "Flex grow / shrink"
1505
+ // comment in the standalone table above for why: these already work via the
1506
+ // PARENT's flex context regardless of this element's own display, so
1507
+ // forcing display:flex here would only be a surprising, unrelated side
1508
+ // effect on this element's OWN children.
1509
+ flex: ({ value, isArbitrary }, { flex }) => {
1510
+ if (!value) return { display: "flex" };
1511
+ if (isArbitrary) {
1512
+ const v = parseFloat(value);
1513
+ return isNaN(v) ? null : { flex: v };
1514
+ }
1515
+ if (value in flex) {
1516
+ const v = flex[value];
1517
+ if (typeof v === "string" && !getEffectiveIsWeb()) {
1518
+ if (v === "auto" || v === "initial") return { flex: 1 };
1519
+ if (v === "none") return { flex: 0 };
1520
+ return null;
1521
+ }
1522
+ return { flex: v };
1523
+ }
1524
+ const n = parseFloat(value);
1525
+ return isNaN(n) ? null : { flex: n };
1526
+ },
1527
+ grow: ({ value }, _) => {
1528
+ if (!value) return { flexGrow: 1 };
1529
+ const n = parseFloat(value);
1530
+ return { flexGrow: isNaN(n) ? 1 : n };
1531
+ },
1532
+ shrink: ({ value }, _) => {
1533
+ if (!value) return { flexShrink: 1 };
1534
+ const n = parseFloat(value);
1535
+ return { flexShrink: isNaN(n) ? 1 : n };
1536
+ },
1537
+ order: ({ value, negative }, _) => {
1538
+ const n = parseInt(value, 10);
1539
+ return isNaN(n) ? null : { order: negative ? -n : n };
1540
+ },
1541
+ // ── Z-index ────────────────────────────────────────────────────────────────
1542
+ z: ({ value, isArbitrary }, { zIndex }) => {
1543
+ if (isArbitrary) {
1544
+ const n2 = parseInt(value);
1545
+ return isNaN(n2) ? null : { zIndex: n2 };
1546
+ }
1547
+ const v = zIndex[value];
1548
+ if (v !== void 0) {
1549
+ if (v === "auto") return getEffectiveIsWeb() ? { zIndex: "auto" } : null;
1550
+ return { zIndex: v };
1551
+ }
1552
+ const n = parseInt(value);
1553
+ return isNaN(n) ? null : { zIndex: n };
1554
+ },
1555
+ // ── Aspect ratio ───────────────────────────────────────────────────────────
1556
+ aspect: ({ value, isArbitrary }) => {
1557
+ if (isArbitrary) return { aspectRatio: value };
1558
+ const presets = { auto: "auto", square: 1, video: 16 / 9 };
1559
+ if (!(value in presets)) return null;
1560
+ const v = presets[value];
1561
+ if (v === "auto") return getEffectiveIsWeb() ? { aspectRatio: "auto" } : null;
1562
+ return { aspectRatio: v };
1563
+ },
1564
+ // ── Columns (web-only) ─────────────────────────────────────────────────────
1565
+ columns: ({ value, isArbitrary }) => {
1566
+ if (!getEffectiveIsWeb()) return null;
1567
+ if (isArbitrary) return { columnCount: value };
1568
+ if (value === "auto") return { columnCount: "auto" };
1569
+ const n = parseInt(value, 10);
1570
+ if (!isNaN(n)) return { columnCount: n };
1571
+ const widths = {
1572
+ "3xs": "16rem",
1573
+ "2xs": "18rem",
1574
+ xs: "20rem",
1575
+ sm: "24rem",
1576
+ md: "28rem",
1577
+ lg: "32rem",
1578
+ xl: "36rem",
1579
+ "2xl": "42rem",
1580
+ "3xl": "48rem",
1581
+ "4xl": "56rem",
1582
+ "5xl": "64rem",
1583
+ "6xl": "72rem",
1584
+ "7xl": "80rem"
1585
+ };
1586
+ return widths[value] ? { columnWidth: widths[value] } : null;
1587
+ }
1588
+ };
1589
+
1590
+ // src/core/resolvers/typography.ts
1591
+ function resolveFontSize(value, fontSizes, isArbitrary) {
1592
+ if (isArbitrary) return getEffectiveIsWeb() ? value : toNativeValue(value);
1593
+ return fontSizes[value] ?? null;
1594
+ }
1595
+ var typographyResolvers = {
1596
+ // ── Text ───────────────────────────────────────────────────────────────────
1597
+ text: ({ value, isArbitrary }, { colors, fontSize }) => {
1598
+ const size = resolveFontSize(value, fontSize, false);
1599
+ if (!isArbitrary && size !== null) return { fontSize: size };
1600
+ if (isArbitrary) {
1601
+ if (/^\d/.test(value) || /^(calc|min|max|clamp)/.test(value)) {
1602
+ return { fontSize: getEffectiveIsWeb() ? value : toNativeValue(value) };
1603
+ }
1604
+ return { color: getEffectiveIsWeb() ? withOpacityVar(value, "--text-opacity") : value };
1605
+ }
1606
+ const color = resolveColor(value, colors, false);
1607
+ if (!color) return null;
1608
+ return { color: getEffectiveIsWeb() ? withOpacityVar(color, "--text-opacity") : color };
1609
+ },
1610
+ "text-opacity": ({ value, isArbitrary }, _) => {
1901
1611
  if (!getEffectiveIsWeb()) return null;
1902
- if (isArbitrary) return { gridAutoColumns: value };
1903
- const presets = {
1612
+ const n = parseFloat(value);
1613
+ if (isNaN(n)) return null;
1614
+ const v = isArbitrary ? n > 1 ? n / 100 : n : n / 100;
1615
+ return { "--text-opacity": v };
1616
+ },
1617
+ // ── Text decoration advanced (web-only) ───────────────────────────────────
1618
+ decoration: ({ value, isArbitrary }, { colors }) => {
1619
+ if (!getEffectiveIsWeb()) return null;
1620
+ if (isArbitrary) {
1621
+ if (/^\d/.test(value) || value.startsWith("calc(")) return { textDecorationThickness: value };
1622
+ return { textDecorationColor: value };
1623
+ }
1624
+ const thickTokens = {
1904
1625
  auto: "auto",
1905
- min: "min-content",
1906
- max: "max-content",
1907
- fr: "minmax(0, 1fr)"
1626
+ "from-font": "from-font",
1627
+ "0": "0px",
1628
+ "1": "1px",
1629
+ "2": "2px",
1630
+ "4": "4px",
1631
+ "8": "8px"
1908
1632
  };
1909
- const v = presets[value];
1910
- return v ? { gridAutoColumns: v } : null;
1633
+ if (value in thickTokens) return { textDecorationThickness: thickTokens[value] };
1634
+ const styleTokens = {
1635
+ solid: "solid",
1636
+ dashed: "dashed",
1637
+ dotted: "dotted",
1638
+ double: "double",
1639
+ wavy: "wavy"
1640
+ };
1641
+ if (value in styleTokens) return { textDecorationStyle: styleTokens[value] };
1642
+ const color = resolveColor(value, colors, false);
1643
+ return color ? { textDecorationColor: color } : null;
1911
1644
  },
1912
- "auto-rows": ({ value, isArbitrary }) => {
1645
+ "underline-offset": ({ value, isArbitrary }) => {
1913
1646
  if (!getEffectiveIsWeb()) return null;
1914
- if (isArbitrary) return { gridAutoRows: value };
1915
- const presets = {
1647
+ if (isArbitrary) return { textUnderlineOffset: value };
1648
+ const offsets = {
1916
1649
  auto: "auto",
1917
- min: "min-content",
1918
- max: "max-content",
1919
- fr: "minmax(0, 1fr)"
1650
+ "0": "0px",
1651
+ "1": "1px",
1652
+ "2": "2px",
1653
+ "4": "4px",
1654
+ "8": "8px"
1920
1655
  };
1921
- const v = presets[value];
1922
- return v ? { gridAutoRows: v } : null;
1656
+ return offsets[value] ? { textUnderlineOffset: offsets[value] } : null;
1923
1657
  },
1924
- // ── Grid column placement (web-only) ──────────────────────────────────────
1925
- "col-span": ({ value }) => {
1658
+ // ── Content (web-only — for before:/after: pseudo-elements, which don't exist on native) ──
1659
+ // Arbitrary value carries its own quotes from the bracket syntax (content-['*'] parses to
1660
+ // the literal string 'you can see it' quotes included), so it passes straight through as a
1661
+ // valid `content: '*'` CSS value with no extra wrapping needed.
1662
+ content: ({ value, isArbitrary }) => {
1926
1663
  if (!getEffectiveIsWeb()) return null;
1927
- if (value === "full") return { gridColumn: "1 / -1" };
1928
- const n = parseInt(value, 10);
1929
- return isNaN(n) ? null : { gridColumn: `span ${n} / span ${n}` };
1664
+ if (isArbitrary) return { content: value };
1665
+ const presets = { none: "none" };
1666
+ return value in presets ? { content: presets[value] } : null;
1930
1667
  },
1931
- "col-start": ({ value, isArbitrary }) => {
1932
- if (!getEffectiveIsWeb()) return null;
1933
- if (value === "auto") return { gridColumnStart: "auto" };
1934
- if (isArbitrary) return { gridColumnStart: value };
1935
- const n = parseInt(value, 10);
1936
- return isNaN(n) ? null : { gridColumnStart: n };
1668
+ // ── Font ───────────────────────────────────────────────────────────────────
1669
+ font: ({ value, isArbitrary }, { fontFamily, fontWeight }) => {
1670
+ if (isArbitrary) return { fontFamily: value };
1671
+ if (value in fontFamily) {
1672
+ const ff = fontFamily[value];
1673
+ return { fontFamily: Array.isArray(ff) ? ff.join(", ") : ff };
1674
+ }
1675
+ if (value in fontWeight) return { fontWeight: String(fontWeight[value]) };
1676
+ return null;
1937
1677
  },
1938
- "col-end": ({ value, isArbitrary }) => {
1678
+ // ── Line height ────────────────────────────────────────────────────────────
1679
+ leading: ({ value, isArbitrary }, { lineHeight }) => {
1680
+ if (isArbitrary) return { lineHeight: getEffectiveIsWeb() ? value : toNativeValue(value) };
1681
+ const v = lineHeight[value];
1682
+ if (v === void 0) return null;
1683
+ if (typeof v === "string" && !getEffectiveIsWeb()) return { lineHeight: toNativeValue(v) };
1684
+ return { lineHeight: v };
1685
+ },
1686
+ // ── Letter spacing ─────────────────────────────────────────────────────────
1687
+ tracking: ({ value, isArbitrary }, { letterSpacing }) => {
1688
+ if (isArbitrary) return { letterSpacing: getEffectiveIsWeb() ? value : toNativeValue(value) };
1689
+ const v = letterSpacing[value];
1690
+ return v !== void 0 ? { letterSpacing: v } : null;
1691
+ },
1692
+ // ── Line-clamp (web-only) ─────────────────────────────────────────────────
1693
+ "line-clamp": ({ value }) => {
1939
1694
  if (!getEffectiveIsWeb()) return null;
1940
- if (value === "auto") return { gridColumnEnd: "auto" };
1941
- if (isArbitrary) return { gridColumnEnd: value };
1695
+ if (value === "none") return { overflow: "visible", display: "block", WebkitLineClamp: "unset" };
1942
1696
  const n = parseInt(value, 10);
1943
- return isNaN(n) ? null : { gridColumnEnd: n };
1697
+ if (isNaN(n) || n < 1) return null;
1698
+ return {
1699
+ overflow: "hidden",
1700
+ display: "-webkit-box",
1701
+ WebkitBoxOrient: "vertical",
1702
+ WebkitLineClamp: n
1703
+ };
1704
+ }
1705
+ };
1706
+
1707
+ // src/core/resolvers/transform.ts
1708
+ var transformResolvers = {
1709
+ // ── Transform extras (web-only) ───────────────────────────────────────────
1710
+ "skew-x": ({ value, negative, isArbitrary }) => {
1711
+ const deg = isArbitrary ? value : `${(negative ? -1 : 1) * parseFloat(value)}deg`;
1712
+ if (!isArbitrary && isNaN(parseFloat(value))) return null;
1713
+ return getEffectiveIsWeb() ? { transform: `skewX(${deg})` } : { transform: [{ skewX: deg }] };
1944
1714
  },
1945
- col: ({ value, isArbitrary }) => {
1715
+ "skew-y": ({ value, negative, isArbitrary }) => {
1716
+ const deg = isArbitrary ? value : `${(negative ? -1 : 1) * parseFloat(value)}deg`;
1717
+ if (!isArbitrary && isNaN(parseFloat(value))) return null;
1718
+ return getEffectiveIsWeb() ? { transform: `skewY(${deg})` } : { transform: [{ skewY: deg }] };
1719
+ },
1720
+ // Arbitrary full transform string: transform-[rotate(45deg)_scale(1.5)]
1721
+ transform: ({ value, isArbitrary }) => {
1946
1722
  if (!getEffectiveIsWeb()) return null;
1947
- if (!value || value === "auto") return { gridColumn: "auto" };
1948
- if (isArbitrary) return { gridColumn: value };
1723
+ if (isArbitrary) return { transform: value.replace(/_/g, " ") };
1724
+ if (value === "none") return { transform: "none" };
1949
1725
  return null;
1950
1726
  },
1951
- // ── Grid row placement (web-only) ─────────────────────────────────────────
1952
- "row-span": ({ value }) => {
1727
+ // Transform origin
1728
+ origin: ({ value, isArbitrary }) => {
1953
1729
  if (!getEffectiveIsWeb()) return null;
1954
- if (value === "full") return { gridRow: "1 / -1" };
1955
- const n = parseInt(value, 10);
1956
- return isNaN(n) ? null : { gridRow: `span ${n} / span ${n}` };
1730
+ if (isArbitrary) return { transformOrigin: value.replace(/_/g, " ") };
1731
+ const origins = {
1732
+ center: "center",
1733
+ top: "top",
1734
+ "top-right": "top right",
1735
+ right: "right",
1736
+ "bottom-right": "bottom right",
1737
+ bottom: "bottom",
1738
+ "bottom-left": "bottom left",
1739
+ left: "left",
1740
+ "top-left": "top left"
1741
+ };
1742
+ return origins[value] ? { transformOrigin: origins[value] } : null;
1957
1743
  },
1958
- "row-start": ({ value, isArbitrary }) => {
1959
- if (!getEffectiveIsWeb()) return null;
1960
- if (value === "auto") return { gridRowStart: "auto" };
1961
- if (isArbitrary) return { gridRowStart: value };
1962
- const n = parseInt(value, 10);
1963
- return isNaN(n) ? null : { gridRowStart: n };
1744
+ // ── Scale ──────────────────────────────────────────────────────────────────
1745
+ // Non-arbitrary: scale-150 → value='150' → 150/100 = 1.5
1746
+ // Arbitrary: scale-[1.5] → value='1.5' → used as-is (already the factor)
1747
+ scale: ({ value, isArbitrary }) => {
1748
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1749
+ if (isNaN(n)) return null;
1750
+ return getEffectiveIsWeb() ? { transform: `scale(${n})` } : { transform: [{ scale: n }] };
1964
1751
  },
1965
- "row-end": ({ value, isArbitrary }) => {
1966
- if (!getEffectiveIsWeb()) return null;
1967
- if (value === "auto") return { gridRowEnd: "auto" };
1968
- if (isArbitrary) return { gridRowEnd: value };
1969
- const n = parseInt(value, 10);
1970
- return isNaN(n) ? null : { gridRowEnd: n };
1752
+ "scale-x": ({ value, isArbitrary }) => {
1753
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1754
+ if (isNaN(n)) return null;
1755
+ return getEffectiveIsWeb() ? { transform: `scaleX(${n})` } : { transform: [{ scaleX: n }] };
1756
+ },
1757
+ "scale-y": ({ value, isArbitrary }) => {
1758
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1759
+ if (isNaN(n)) return null;
1760
+ return getEffectiveIsWeb() ? { transform: `scaleY(${n})` } : { transform: [{ scaleY: n }] };
1761
+ },
1762
+ // ── Rotate ─────────────────────────────────────────────────────────────────
1763
+ rotate: ({ value, negative, isArbitrary }) => {
1764
+ if (isArbitrary) {
1765
+ const finalValue = negative ? `-${value}` : value;
1766
+ return getEffectiveIsWeb() ? { transform: `rotate(${finalValue})` } : { transform: [{ rotate: finalValue }] };
1767
+ }
1768
+ const deg = parseFloat(value);
1769
+ if (isNaN(deg)) return null;
1770
+ const finalDeg = negative ? -deg : deg;
1771
+ return getEffectiveIsWeb() ? { transform: `rotate(${finalDeg}deg)` } : { transform: [{ rotate: `${finalDeg}deg` }] };
1772
+ },
1773
+ // ── Perspective transform (cross-platform: iOS, Android, web) ────────────
1774
+ perspective: ({ value, isArbitrary }) => {
1775
+ if (isArbitrary) {
1776
+ const numPx = parseFloat(value);
1777
+ if (getEffectiveIsWeb()) return { transform: `perspective(${value})` };
1778
+ return isNaN(numPx) ? null : { transform: [{ perspective: numPx }] };
1779
+ }
1780
+ if (value === "none") return getEffectiveIsWeb() ? { transform: "none" } : null;
1781
+ const n = parseFloat(value);
1782
+ if (isNaN(n)) return null;
1783
+ return getEffectiveIsWeb() ? { transform: `perspective(${n}px)` } : { transform: [{ perspective: n }] };
1784
+ }
1785
+ };
1786
+
1787
+ // src/core/resolvers/effects.ts
1788
+ function keyframeDeclToCSS(decl) {
1789
+ return Object.entries(decl).map(([prop, val]) => `${prop.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${val}`).join("; ");
1790
+ }
1791
+ function buildKeyframeCSS(name, steps) {
1792
+ const body = Object.entries(steps).map(([selector, decl]) => `${selector} { ${keyframeDeclToCSS(decl)} }`).join(" ");
1793
+ return `@keyframes ${name} { ${body} }`;
1794
+ }
1795
+ var BUILTIN_ANIMATIONS = {
1796
+ none: { animation: "none" },
1797
+ spin: {
1798
+ animation: "kb-spin 1s linear infinite",
1799
+ __keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
1800
+ },
1801
+ ping: {
1802
+ animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
1803
+ __keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
1804
+ },
1805
+ pulse: {
1806
+ animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
1807
+ __keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
1808
+ },
1809
+ bounce: {
1810
+ animation: "kb-bounce 1s infinite",
1811
+ __keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
1812
+ }
1813
+ };
1814
+ function buildAnimationValue(animation, keyframes) {
1815
+ const words = animation.trim().split(/\s+/);
1816
+ const name = words[0];
1817
+ if (!name) return { animation };
1818
+ const builtin = BUILTIN_ANIMATIONS[name];
1819
+ if (builtin?.__keyframe) {
1820
+ const kbName = builtin.animation.split(/\s+/, 1)[0];
1821
+ return { animation: [kbName, ...words.slice(1)].join(" "), __keyframe: builtin.__keyframe };
1822
+ }
1823
+ const steps = keyframes?.[name];
1824
+ return steps ? { animation, __keyframe: buildKeyframeCSS(name, steps) } : { animation };
1825
+ }
1826
+ var effectResolvers = {
1827
+ // ── Opacity ────────────────────────────────────────────────────────────────
1828
+ opacity: ({ value, isArbitrary }, { opacity }) => {
1829
+ if (isArbitrary) {
1830
+ const v2 = parseFloat(value);
1831
+ return isNaN(v2) ? null : { opacity: v2 > 1 ? v2 / 100 : v2 };
1832
+ }
1833
+ const v = opacity[value];
1834
+ if (v !== void 0) return { opacity: v };
1835
+ const n = parseFloat(value);
1836
+ return isNaN(n) ? null : { opacity: n > 1 ? n / 100 : n };
1971
1837
  },
1972
- row: ({ value, isArbitrary }) => {
1838
+ // ── Shadow ─────────────────────────────────────────────────────────────────
1839
+ shadow: ({ value }, { shadow }) => {
1840
+ const key = value === "" ? "DEFAULT" : value;
1841
+ return shadow[key] ?? null;
1842
+ },
1843
+ // ── Animations (CSS keyframe animations, web-only) ────────────────────────
1844
+ // Each variant injects a @keyframes rule once via the __keyframe marker.
1845
+ // resolver.ts detects __keyframe, injects the rule, then strips the marker
1846
+ // so it never reaches element inline styles.
1847
+ animate: ({ value, isArbitrary }, theme) => {
1973
1848
  if (!getEffectiveIsWeb()) return null;
1974
- if (!value || value === "auto") return { gridRow: "auto" };
1975
- if (isArbitrary) return { gridRow: value };
1976
- return null;
1849
+ if (isArbitrary) return buildAnimationValue(value.replace(/_/g, " "), theme.keyframes);
1850
+ if (value in BUILTIN_ANIMATIONS) return BUILTIN_ANIMATIONS[value];
1851
+ const custom = theme.animation?.[value];
1852
+ return custom ? buildAnimationValue(custom, theme.keyframes) : null;
1977
1853
  },
1978
- // ── Grid alignment (web-only) ──────────────────────────────────────────────
1979
- // place-items is shorthand for align-items + justify-items on a grid container
1980
- "place-items": ({ value }) => {
1854
+ // ── Transition (web-only; use Animated API on native) ─────────────────────
1855
+ transition: ({ value }) => {
1981
1856
  if (!getEffectiveIsWeb()) return null;
1982
- const map = {
1983
- start: "start",
1984
- end: "end",
1985
- center: "center",
1986
- stretch: "stretch",
1987
- baseline: "baseline"
1857
+ const presets = {
1858
+ "": "color 150ms, background-color 150ms, border-color 150ms, text-decoration-color 150ms, fill 150ms, stroke 150ms, opacity 150ms, box-shadow 150ms, transform 150ms, filter 150ms, backdrop-filter 150ms",
1859
+ all: "all 150ms",
1860
+ none: "none",
1861
+ colors: "color 150ms, background-color 150ms, border-color 150ms",
1862
+ opacity: "opacity 150ms",
1863
+ shadow: "box-shadow 150ms",
1864
+ transform: "transform 150ms"
1988
1865
  };
1989
- return map[value] ? { placeItems: map[value] } : null;
1866
+ const v = presets[value];
1867
+ return v !== void 0 ? { transition: v } : null;
1990
1868
  },
1991
- // place-content is shorthand for align-content + justify-content on a grid container
1992
- "place-content": ({ value }) => {
1869
+ duration: ({ value, isArbitrary }) => {
1993
1870
  if (!getEffectiveIsWeb()) return null;
1994
- const map = {
1995
- start: "start",
1996
- end: "end",
1997
- center: "center",
1998
- stretch: "stretch",
1999
- between: "space-between",
2000
- around: "space-around",
2001
- evenly: "space-evenly",
2002
- baseline: "baseline"
2003
- };
2004
- return map[value] ? { placeContent: map[value] } : null;
1871
+ if (!isArbitrary && isNaN(parseFloat(value))) return null;
1872
+ const ms = isArbitrary ? value : `${value}ms`;
1873
+ return { transitionDuration: ms };
2005
1874
  },
2006
- // justify-items controls inline-axis alignment of grid items within their cells
2007
- "justify-items": ({ value }) => {
1875
+ delay: ({ value, isArbitrary }) => {
2008
1876
  if (!getEffectiveIsWeb()) return null;
2009
- const map = {
2010
- start: "start",
2011
- end: "end",
2012
- center: "center",
2013
- stretch: "stretch"
2014
- };
2015
- return map[value] ? { justifyItems: map[value] } : null;
1877
+ if (!isArbitrary && isNaN(parseFloat(value))) return null;
1878
+ const ms = isArbitrary ? value : `${value}ms`;
1879
+ return { transitionDelay: ms };
1880
+ }
1881
+ };
1882
+
1883
+ // src/core/resolvers/misc.ts
1884
+ var miscResolvers = {
1885
+ // ── Arbitrary CSS property (web-only): [property:value] with no utility prefix ──
1886
+ // e.g. [mask-type:luminance], [--my-var:10px]. parser.ts already isolates this case
1887
+ // as utility: '' — it's ALSO how a stray, prefix-less negative bracket ("-[10px]",
1888
+ // presumably a typo) parses, which is why this only fires when the arbitrary value
1889
+ // itself contains a "property:value" pair; anything else (no colon) still resolves
1890
+ // to null exactly as before this resolver existed, rather than guessing a property.
1891
+ //
1892
+ // Only the FIRST colon splits property from value — the value itself may contain
1893
+ // more colons (a URL's "http://…") and must stay intact: [background:url(http://x/a.png)].
1894
+ // camelToKebab() in resolver.ts is a no-op on an already-kebab-case (or "--"-prefixed
1895
+ // custom property) key, so it can be used as the style object key as-written with no
1896
+ // extra case conversion — it only needs to look like a plausible CSS property name.
1897
+ "": ({ value, isArbitrary, negative }) => {
1898
+ if (!getEffectiveIsWeb() || !isArbitrary || !value) return null;
1899
+ if (negative) return null;
1900
+ const colonIdx = value.indexOf(":");
1901
+ if (colonIdx <= 0) return null;
1902
+ const prop = value.slice(0, colonIdx).trim();
1903
+ const cssValue = value.slice(colonIdx + 1).trim();
1904
+ if (!cssValue || !/^(--[\w-]+|[a-zA-Z-]+)$/.test(prop)) return null;
1905
+ return { [prop]: cssValue };
2016
1906
  },
2017
- // place-self is shorthand for align-self + justify-self on a grid item
2018
- "place-self": ({ value }) => {
1907
+ // ── SVG stroke/fill (web-only) ────────────────────────────────────────────
1908
+ // Native is intentionally excluded: react-native-svg's <Path>/<Circle>/etc.
1909
+ // take stroke/fill as component PROPS, not style entries, so there's no
1910
+ // reliable way to apply a resolved color through the `style` prop there.
1911
+ // `stroke-{n}` sets strokeWidth (a plain number, unlike a color) — same
1912
+ // color-first-then-numeric-fallback disambiguation `border` uses above.
1913
+ stroke: ({ value, isArbitrary }, { colors }) => {
2019
1914
  if (!getEffectiveIsWeb()) return null;
2020
- const map = {
2021
- auto: "auto",
2022
- start: "start",
2023
- end: "end",
2024
- center: "center",
2025
- stretch: "stretch"
2026
- };
2027
- return map[value] ? { placeSelf: map[value] } : null;
1915
+ if (!value) return null;
1916
+ if (value === "none") return { stroke: "none" };
1917
+ if (isArbitrary) {
1918
+ const w = toNativeValue(value);
1919
+ return typeof w === "number" ? { strokeWidth: w } : { stroke: value };
1920
+ }
1921
+ const color = resolveColor(value, colors, false);
1922
+ if (color) return { stroke: color };
1923
+ const n = parseFloat(value);
1924
+ return isNaN(n) ? null : { strokeWidth: n };
2028
1925
  },
2029
- // justify-self controls inline-axis self-alignment of a grid item
2030
- "justify-self": ({ value }) => {
1926
+ fill: ({ value, isArbitrary }, { colors }) => {
2031
1927
  if (!getEffectiveIsWeb()) return null;
2032
- const map = {
2033
- auto: "auto",
2034
- start: "start",
2035
- end: "end",
2036
- center: "center",
2037
- stretch: "stretch"
2038
- };
2039
- return map[value] ? { justifySelf: map[value] } : null;
1928
+ if (!value) return null;
1929
+ if (value === "none") return { fill: "none" };
1930
+ if (isArbitrary) return { fill: value };
1931
+ const color = resolveColor(value, colors, false);
1932
+ return color ? { fill: color } : null;
2040
1933
  },
2041
- // ── Flex ───────────────────────────────────────────────────────────────────
2042
- // flex-1/flex-auto/flex-none/grow/shrink are item-level sizing properties —
2043
- // deliberately NOT implying display:flex here. See the "Flex grow / shrink"
2044
- // comment in the standalone table above for why: these already work via the
2045
- // PARENT's flex context regardless of this element's own display, so
2046
- // forcing display:flex here would only be a surprising, unrelated side
2047
- // effect on this element's OWN children.
2048
- flex: ({ value, isArbitrary }, { flex }) => {
2049
- if (!value) return { display: "flex" };
1934
+ // ── Text shadow (cross-platform: iOS, Android, web) ─────────────────────
1935
+ // Named presets give cross-platform shadow; arbitrary is web-only.
1936
+ "text-shadow": ({ value, isArbitrary }, { colors }) => {
1937
+ if (value === "none") {
1938
+ return getEffectiveIsWeb() ? { textShadow: "none" } : { textShadowColor: "transparent", textShadowOffset: { width: 0, height: 0 }, textShadowRadius: 0 };
1939
+ }
1940
+ const webPresets = {
1941
+ "": "0 2px 4px rgba(0,0,0,0.3)",
1942
+ sm: "0 1px 2px rgba(0,0,0,0.3)",
1943
+ md: "0 4px 6px rgba(0,0,0,0.3)",
1944
+ lg: "0 8px 16px rgba(0,0,0,0.5)",
1945
+ xl: "0 16px 32px rgba(0,0,0,0.5)"
1946
+ };
1947
+ const nativePresets = {
1948
+ "": { textShadowColor: "rgba(0,0,0,0.3)", textShadowOffset: { width: 0, height: 2 }, textShadowRadius: 4 },
1949
+ sm: { textShadowColor: "rgba(0,0,0,0.3)", textShadowOffset: { width: 0, height: 1 }, textShadowRadius: 2 },
1950
+ md: { textShadowColor: "rgba(0,0,0,0.3)", textShadowOffset: { width: 0, height: 4 }, textShadowRadius: 6 },
1951
+ lg: { textShadowColor: "rgba(0,0,0,0.5)", textShadowOffset: { width: 0, height: 8 }, textShadowRadius: 16 },
1952
+ xl: { textShadowColor: "rgba(0,0,0,0.5)", textShadowOffset: { width: 0, height: 16 }, textShadowRadius: 32 }
1953
+ };
1954
+ if (value in webPresets) {
1955
+ if (getEffectiveIsWeb()) return { textShadow: webPresets[value] };
1956
+ return nativePresets[value] ?? null;
1957
+ }
2050
1958
  if (isArbitrary) {
2051
- const v = parseFloat(value);
2052
- return isNaN(v) ? null : { flex: v };
1959
+ return getEffectiveIsWeb() ? { textShadow: value.replace(/_/g, " ") } : null;
2053
1960
  }
2054
- if (value in flex) {
2055
- const v = flex[value];
2056
- if (typeof v === "string" && !getEffectiveIsWeb()) {
2057
- if (v === "auto" || v === "initial") return { flex: 1 };
2058
- if (v === "none") return { flex: 0 };
2059
- return null;
1961
+ const color = resolveColor(value, colors, false);
1962
+ if (color) {
1963
+ return getEffectiveIsWeb() ? { textShadow: `0 2px 4px ${color}` } : { textShadowColor: color };
1964
+ }
1965
+ return null;
1966
+ }
1967
+ };
1968
+
1969
+ // src/core/utilities.ts
1970
+ var RESOLVERS = {
1971
+ ...colorResolvers,
1972
+ ...spacingResolvers,
1973
+ ...borderResolvers,
1974
+ ...layoutResolvers,
1975
+ ...typographyResolvers,
1976
+ ...filterResolvers,
1977
+ ...transformResolvers,
1978
+ ...effectResolvers,
1979
+ ...miscResolvers
1980
+ };
1981
+ var NAMED_GROUP_PEER_MARKER_RE = /^(group|peer)\/.+$/;
1982
+ function resolveUtility(parsed, theme) {
1983
+ if (!parsed.value) {
1984
+ if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
1985
+ if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
1986
+ if (NAMED_GROUP_PEER_MARKER_RE.test(parsed.utility)) return getEffectiveIsWeb() ? {} : null;
1987
+ }
1988
+ const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
1989
+ if (resolver) return resolver(parsed, theme);
1990
+ return null;
1991
+ }
1992
+ var PLUGIN_STANDALONE = {};
1993
+ var PLUGIN_RESOLVERS = {};
1994
+ function clearPluginUtilities() {
1995
+ for (const key of Object.keys(PLUGIN_STANDALONE)) delete PLUGIN_STANDALONE[key];
1996
+ for (const key of Object.keys(PLUGIN_RESOLVERS)) delete PLUGIN_RESOLVERS[key];
1997
+ _sortedPrefixes = null;
1998
+ _standaloneNames = null;
1999
+ }
2000
+ function getPluginStandaloneMap() {
2001
+ return PLUGIN_STANDALONE;
2002
+ }
2003
+ function isKnownUtility(utility) {
2004
+ return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS || NAMED_GROUP_PEER_MARKER_RE.test(utility);
2005
+ }
2006
+ var _sortedPrefixes = null;
2007
+ var _standaloneNames = null;
2008
+ function getBuiltinUtilityPrefixes() {
2009
+ if (!_sortedPrefixes) {
2010
+ const all = [.../* @__PURE__ */ new Set([...Object.keys(RESOLVERS), ...Object.keys(PLUGIN_RESOLVERS)])];
2011
+ _sortedPrefixes = all.sort((a, b) => b.length - a.length);
2012
+ }
2013
+ return _sortedPrefixes;
2014
+ }
2015
+ function getBuiltinStandaloneNames() {
2016
+ if (!_standaloneNames) {
2017
+ _standaloneNames = /* @__PURE__ */ new Set([...Object.keys(getStandalone()), ...Object.keys(PLUGIN_STANDALONE)]);
2018
+ }
2019
+ return _standaloneNames;
2020
+ }
2021
+
2022
+ // src/core/parser.ts
2023
+ var UNSAFE_ARBITRARY_VALUE = /[{};\\]|\/\*|\*\//;
2024
+ function isSafeArbitraryValue(value) {
2025
+ if (!UNSAFE_ARBITRARY_VALUE.test(value)) return true;
2026
+ if (process.env.NODE_ENV !== "production") {
2027
+ kbachWarn(`Unsafe arbitrary value rejected: "${value}"`);
2028
+ }
2029
+ return false;
2030
+ }
2031
+ function parseClass(className) {
2032
+ const trimmed = className.trim();
2033
+ if (!trimmed) return null;
2034
+ const modifiers = [];
2035
+ let remaining = trimmed;
2036
+ while (true) {
2037
+ const colonIdx = findOuterColon(remaining);
2038
+ if (colonIdx === -1) break;
2039
+ const candidate = remaining.slice(0, colonIdx);
2040
+ if (!isKnownModifier(candidate)) break;
2041
+ modifiers.push(candidate);
2042
+ remaining = remaining.slice(colonIdx + 1);
2043
+ }
2044
+ let important = false;
2045
+ if (remaining.startsWith("!") && remaining.length > 1) {
2046
+ important = true;
2047
+ remaining = remaining.slice(1);
2048
+ }
2049
+ let negative = false;
2050
+ if (remaining.startsWith("-") && remaining.length > 1 && remaining[1] !== "-") {
2051
+ negative = true;
2052
+ remaining = remaining.slice(1);
2053
+ }
2054
+ let bracketDepth = 0;
2055
+ let bracketStart = -1;
2056
+ for (let i = remaining.length - 1; i >= 0; i--) {
2057
+ if (remaining[i] === "]") bracketDepth++;
2058
+ else if (remaining[i] === "[") {
2059
+ bracketDepth--;
2060
+ if (bracketDepth === 0) {
2061
+ bracketStart = i;
2062
+ break;
2060
2063
  }
2061
- return { flex: v };
2062
2064
  }
2063
- const n = parseFloat(value);
2064
- return isNaN(n) ? null : { flex: n };
2065
- },
2066
- grow: ({ value }, _) => {
2067
- if (!value) return { flexGrow: 1 };
2068
- const n = parseFloat(value);
2069
- return { flexGrow: isNaN(n) ? 1 : n };
2070
- },
2071
- shrink: ({ value }, _) => {
2072
- if (!value) return { flexShrink: 1 };
2073
- const n = parseFloat(value);
2074
- return { flexShrink: isNaN(n) ? 1 : n };
2075
- },
2076
- order: ({ value, negative }, _) => {
2077
- const n = parseInt(value, 10);
2078
- return isNaN(n) ? null : { order: negative ? -n : n };
2079
- },
2080
- // ── Z-index ────────────────────────────────────────────────────────────────
2081
- z: ({ value, isArbitrary }, { zIndex }) => {
2082
- if (isArbitrary) {
2083
- const n2 = parseInt(value);
2084
- return isNaN(n2) ? null : { zIndex: n2 };
2065
+ }
2066
+ if (bracketStart > 0 && remaining[bracketStart - 1] === "-" && remaining.endsWith("]")) {
2067
+ const utility = remaining.slice(0, bracketStart - 1);
2068
+ const value = remaining.slice(bracketStart + 1, -1).replace(/_/g, " ");
2069
+ if (!isSafeArbitraryValue(value)) return null;
2070
+ return { original: trimmed, modifiers, negative, important, utility, value, isArbitrary: true };
2071
+ }
2072
+ if (remaining.endsWith("]") && bracketStart === -1) {
2073
+ if (process.env.NODE_ENV !== "production") {
2074
+ kbachWarn(`Unbalanced brackets: "${trimmed}"`);
2085
2075
  }
2086
- const v = zIndex[value];
2087
- if (v !== void 0) {
2088
- if (v === "auto") return getEffectiveIsWeb() ? { zIndex: "auto" } : null;
2089
- return { zIndex: v };
2076
+ }
2077
+ if (remaining.startsWith("[") && remaining.endsWith("]")) {
2078
+ const value = remaining.slice(1, -1).replace(/_/g, " ");
2079
+ if (!isSafeArbitraryValue(value)) return null;
2080
+ return { original: trimmed, modifiers, negative, important, utility: "", value, isArbitrary: true };
2081
+ }
2082
+ if (getBuiltinStandaloneNames().has(remaining)) {
2083
+ return { original: trimmed, modifiers, negative, important, utility: remaining, value: "", isArbitrary: false };
2084
+ }
2085
+ for (const prefix of getBuiltinUtilityPrefixes()) {
2086
+ if (remaining === prefix) {
2087
+ return { original: trimmed, modifiers, negative, important, utility: prefix, value: "", isArbitrary: false };
2090
2088
  }
2091
- const n = parseInt(value);
2092
- return isNaN(n) ? null : { zIndex: n };
2093
- },
2094
- // ── Aspect ratio ───────────────────────────────────────────────────────────
2095
- aspect: ({ value, isArbitrary }) => {
2096
- if (isArbitrary) return { aspectRatio: value };
2097
- const presets = { auto: "auto", square: 1, video: 16 / 9 };
2098
- if (!(value in presets)) return null;
2099
- const v = presets[value];
2100
- if (v === "auto") return getEffectiveIsWeb() ? { aspectRatio: "auto" } : null;
2101
- return { aspectRatio: v };
2102
- },
2103
- // ── Columns (web-only) ─────────────────────────────────────────────────────
2104
- columns: ({ value, isArbitrary }) => {
2105
- if (!getEffectiveIsWeb()) return null;
2106
- if (isArbitrary) return { columnCount: value };
2107
- if (value === "auto") return { columnCount: "auto" };
2108
- const n = parseInt(value, 10);
2109
- if (!isNaN(n)) return { columnCount: n };
2110
- const widths = {
2111
- "3xs": "16rem",
2112
- "2xs": "18rem",
2113
- xs: "20rem",
2114
- sm: "24rem",
2115
- md: "28rem",
2116
- lg: "32rem",
2117
- xl: "36rem",
2118
- "2xl": "42rem",
2119
- "3xl": "48rem",
2120
- "4xl": "56rem",
2121
- "5xl": "64rem",
2122
- "6xl": "72rem",
2123
- "7xl": "80rem"
2089
+ if (remaining.startsWith(prefix + "-")) {
2090
+ const value = remaining.slice(prefix.length + 1);
2091
+ return { original: trimmed, modifiers, negative, important, utility: prefix, value, isArbitrary: false };
2092
+ }
2093
+ }
2094
+ const firstDash = remaining.indexOf("-");
2095
+ if (firstDash > 0) {
2096
+ return {
2097
+ original: trimmed,
2098
+ modifiers,
2099
+ negative,
2100
+ important,
2101
+ utility: remaining.slice(0, firstDash),
2102
+ value: remaining.slice(firstDash + 1),
2103
+ isArbitrary: false
2124
2104
  };
2125
- return widths[value] ? { columnWidth: widths[value] } : null;
2126
2105
  }
2127
- };
2128
-
2129
- // src/core/resolvers/typography.ts
2130
- function resolveFontSize(value, fontSizes, isArbitrary) {
2131
- if (isArbitrary) return getEffectiveIsWeb() ? value : toNativeValue(value);
2132
- return fontSizes[value] ?? null;
2106
+ return { original: trimmed, modifiers, negative, important, utility: remaining, value: "", isArbitrary: false };
2133
2107
  }
2134
- var typographyResolvers = {
2135
- // ── Text ───────────────────────────────────────────────────────────────────
2136
- text: ({ value, isArbitrary }, { colors, fontSize }) => {
2137
- const size = resolveFontSize(value, fontSize, false);
2138
- if (!isArbitrary && size !== null) return { fontSize: size };
2139
- if (isArbitrary) {
2140
- if (/^\d/.test(value) || /^(calc|min|max|clamp)/.test(value)) {
2141
- return { fontSize: getEffectiveIsWeb() ? value : toNativeValue(value) };
2108
+ function splitClassTokens(classString) {
2109
+ const tokens = [];
2110
+ let current = "";
2111
+ let depth = 0;
2112
+ let parenDepth = 0;
2113
+ for (let i = 0; i < classString.length; i++) {
2114
+ const ch = classString[i];
2115
+ if (ch === "[") {
2116
+ depth++;
2117
+ current += ch;
2118
+ continue;
2119
+ }
2120
+ if (ch === "]") {
2121
+ depth--;
2122
+ current += ch;
2123
+ continue;
2124
+ }
2125
+ if (ch === "(") {
2126
+ parenDepth++;
2127
+ current += ch;
2128
+ continue;
2129
+ }
2130
+ if (ch === ")") {
2131
+ parenDepth--;
2132
+ current += ch;
2133
+ continue;
2134
+ }
2135
+ if (/\s/.test(ch)) {
2136
+ if (depth === 0 && parenDepth === 0) {
2137
+ if (current) {
2138
+ tokens.push(current);
2139
+ current = "";
2140
+ }
2142
2141
  }
2143
- return { color: getEffectiveIsWeb() ? withOpacityVar(value, "--text-opacity") : value };
2142
+ } else {
2143
+ current += ch;
2144
2144
  }
2145
- const color = resolveColor(value, colors, false);
2146
- if (!color) return null;
2147
- return { color: getEffectiveIsWeb() ? withOpacityVar(color, "--text-opacity") : color };
2145
+ }
2146
+ if (current) tokens.push(current);
2147
+ return tokens;
2148
+ }
2149
+ function normalizeClassString(classString) {
2150
+ return splitClassTokens(classString).join(" ");
2151
+ }
2152
+ function parseClasses(classString) {
2153
+ const results = [];
2154
+ for (const token of splitClassTokens(classString)) {
2155
+ const parsed = parseClass(token);
2156
+ if (parsed) results.push(parsed);
2157
+ }
2158
+ return results;
2159
+ }
2160
+ function findOuterColon(s) {
2161
+ let depth = 0;
2162
+ for (let i = 0; i < s.length; i++) {
2163
+ const ch = s[i];
2164
+ if (ch === "[") depth++;
2165
+ else if (ch === "]") depth--;
2166
+ else if (ch === ":" && depth === 0) return i;
2167
+ }
2168
+ return -1;
2169
+ }
2170
+
2171
+ // src/core/modeAwareColors.ts
2172
+ var _modeAwareMapCache = /* @__PURE__ */ new WeakMap();
2173
+ function buildModeAwareMap(colors) {
2174
+ const map = /* @__PURE__ */ new Map();
2175
+ for (const [name, entry] of Object.entries(colors)) {
2176
+ if (isModeAwareColor(entry)) {
2177
+ map.set(name, entry);
2178
+ } else if (entry && typeof entry === "object") {
2179
+ for (const [shade, val] of Object.entries(entry)) {
2180
+ if (isModeAwareColor(val)) map.set(`${name}-${shade}`, val);
2181
+ }
2182
+ }
2183
+ }
2184
+ return map;
2185
+ }
2186
+ function getModeAwareMap(colors) {
2187
+ let map = _modeAwareMapCache.get(colors);
2188
+ if (!map) {
2189
+ map = buildModeAwareMap(colors);
2190
+ _modeAwareMapCache.set(colors, map);
2191
+ }
2192
+ return map;
2193
+ }
2194
+ function expandModeAwareColorClasses(classString, colors) {
2195
+ const map = getModeAwareMap(colors);
2196
+ if (map.size === 0) return classString;
2197
+ const tokens = splitClassTokens(classString);
2198
+ let changed = false;
2199
+ const out = [];
2200
+ for (const token of tokens) {
2201
+ const parsed = parseClass(token);
2202
+ if (!parsed || parsed.isArbitrary) {
2203
+ out.push(token);
2204
+ continue;
2205
+ }
2206
+ if (parsed.modifiers.includes("dark") || parsed.modifiers.includes("light")) {
2207
+ out.push(token);
2208
+ continue;
2209
+ }
2210
+ const slashIdx = parsed.value.indexOf("/");
2211
+ const colorPart = slashIdx > 0 ? parsed.value.slice(0, slashIdx) : parsed.value;
2212
+ const opacitySuffix = slashIdx > 0 ? parsed.value.slice(slashIdx) : "";
2213
+ const pair = map.get(colorPart);
2214
+ if (!pair) {
2215
+ out.push(token);
2216
+ continue;
2217
+ }
2218
+ changed = true;
2219
+ const bang = parsed.important ? "!" : "";
2220
+ const modPrefix = parsed.modifiers.map((m) => `${m}:`).join("");
2221
+ out.push(`${bang}${modPrefix}${parsed.utility}-[${pair.light}]${opacitySuffix}`);
2222
+ out.push(`${bang}dark:${modPrefix}${parsed.utility}-[${pair.dark}]${opacitySuffix}`);
2223
+ }
2224
+ return changed ? out.join(" ") : classString;
2225
+ }
2226
+
2227
+ // src/core/theme.ts
2228
+ var defaultColors = {
2229
+ transparent: "transparent",
2230
+ current: "currentColor",
2231
+ black: "#000000",
2232
+ white: "#ffffff",
2233
+ slate: {
2234
+ 1: "#f8fafc",
2235
+ 2: "#f1f5f9",
2236
+ 3: "#e2e8f0",
2237
+ 4: "#cbd5e1",
2238
+ 5: "#94a3b8",
2239
+ 6: "#64748b",
2240
+ 7: "#475569",
2241
+ 8: "#334155",
2242
+ 9: "#1e293b",
2243
+ 10: "#0f172a",
2244
+ 11: "#020617",
2245
+ 12: "#01020a"
2246
+ },
2247
+ gray: {
2248
+ 1: "#f9fafb",
2249
+ 2: "#f3f4f6",
2250
+ 3: "#e5e7eb",
2251
+ 4: "#d1d5db",
2252
+ 5: "#9ca3af",
2253
+ 6: "#6b7280",
2254
+ 7: "#4b5563",
2255
+ 8: "#374151",
2256
+ 9: "#1f2937",
2257
+ 10: "#111827",
2258
+ 11: "#030712",
2259
+ 12: "#020409"
2148
2260
  },
2149
- "text-opacity": ({ value, isArbitrary }, _) => {
2150
- if (!getEffectiveIsWeb()) return null;
2151
- const n = parseFloat(value);
2152
- if (isNaN(n)) return null;
2153
- const v = isArbitrary ? n > 1 ? n / 100 : n : n / 100;
2154
- return { "--text-opacity": v };
2261
+ zinc: {
2262
+ 1: "#fafafa",
2263
+ 2: "#f4f4f5",
2264
+ 3: "#e4e4e7",
2265
+ 4: "#d4d4d8",
2266
+ 5: "#a1a1aa",
2267
+ 6: "#71717a",
2268
+ 7: "#52525b",
2269
+ 8: "#3f3f46",
2270
+ 9: "#27272a",
2271
+ 10: "#18181b",
2272
+ 11: "#09090b",
2273
+ 12: "#050506"
2155
2274
  },
2156
- // ── Text decoration advanced (web-only) ───────────────────────────────────
2157
- decoration: ({ value, isArbitrary }, { colors }) => {
2158
- if (!getEffectiveIsWeb()) return null;
2159
- if (isArbitrary) {
2160
- if (/^\d/.test(value) || value.startsWith("calc(")) return { textDecorationThickness: value };
2161
- return { textDecorationColor: value };
2162
- }
2163
- const thickTokens = {
2164
- auto: "auto",
2165
- "from-font": "from-font",
2166
- "0": "0px",
2167
- "1": "1px",
2168
- "2": "2px",
2169
- "4": "4px",
2170
- "8": "8px"
2171
- };
2172
- if (value in thickTokens) return { textDecorationThickness: thickTokens[value] };
2173
- const styleTokens = {
2174
- solid: "solid",
2175
- dashed: "dashed",
2176
- dotted: "dotted",
2177
- double: "double",
2178
- wavy: "wavy"
2179
- };
2180
- if (value in styleTokens) return { textDecorationStyle: styleTokens[value] };
2181
- const color = resolveColor(value, colors, false);
2182
- return color ? { textDecorationColor: color } : null;
2275
+ neutral: {
2276
+ 1: "#fafafa",
2277
+ 2: "#f5f5f5",
2278
+ 3: "#e5e5e5",
2279
+ 4: "#d4d4d4",
2280
+ 5: "#a3a3a3",
2281
+ 6: "#737373",
2282
+ 7: "#525252",
2283
+ 8: "#404040",
2284
+ 9: "#262626",
2285
+ 10: "#171717",
2286
+ 11: "#0a0a0a",
2287
+ 12: "#050505"
2183
2288
  },
2184
- "underline-offset": ({ value, isArbitrary }) => {
2185
- if (!getEffectiveIsWeb()) return null;
2186
- if (isArbitrary) return { textUnderlineOffset: value };
2187
- const offsets = {
2188
- auto: "auto",
2189
- "0": "0px",
2190
- "1": "1px",
2191
- "2": "2px",
2192
- "4": "4px",
2193
- "8": "8px"
2194
- };
2195
- return offsets[value] ? { textUnderlineOffset: offsets[value] } : null;
2289
+ stone: {
2290
+ 1: "#fafaf9",
2291
+ 2: "#f5f5f4",
2292
+ 3: "#e7e5e4",
2293
+ 4: "#d6d3d1",
2294
+ 5: "#a8a29e",
2295
+ 6: "#78716c",
2296
+ 7: "#57534e",
2297
+ 8: "#44403c",
2298
+ 9: "#292524",
2299
+ 10: "#1c1917",
2300
+ 11: "#0c0a09",
2301
+ 12: "#070605"
2196
2302
  },
2197
- // ── Content (web-only — for before:/after: pseudo-elements, which don't exist on native) ──
2198
- // Arbitrary value carries its own quotes from the bracket syntax (content-['*'] parses to
2199
- // the literal string 'you can see it' quotes included), so it passes straight through as a
2200
- // valid `content: '*'` CSS value with no extra wrapping needed.
2201
- content: ({ value, isArbitrary }) => {
2202
- if (!getEffectiveIsWeb()) return null;
2203
- if (isArbitrary) return { content: value };
2204
- const presets = { none: "none" };
2205
- return value in presets ? { content: presets[value] } : null;
2303
+ red: {
2304
+ 1: "#fef2f2",
2305
+ 2: "#fee2e2",
2306
+ 3: "#fecaca",
2307
+ 4: "#fca5a5",
2308
+ 5: "#f87171",
2309
+ 6: "#ef4444",
2310
+ 7: "#dc2626",
2311
+ 8: "#b91c1c",
2312
+ 9: "#991b1b",
2313
+ 10: "#7f1d1d",
2314
+ 11: "#450a0a",
2315
+ 12: "#280606"
2206
2316
  },
2207
- // ── Font ───────────────────────────────────────────────────────────────────
2208
- font: ({ value, isArbitrary }, { fontFamily, fontWeight }) => {
2209
- if (isArbitrary) return { fontFamily: value };
2210
- if (value in fontFamily) {
2211
- const ff = fontFamily[value];
2212
- return { fontFamily: Array.isArray(ff) ? ff.join(", ") : ff };
2213
- }
2214
- if (value in fontWeight) return { fontWeight: String(fontWeight[value]) };
2215
- return null;
2317
+ orange: {
2318
+ 1: "#fff7ed",
2319
+ 2: "#ffedd5",
2320
+ 3: "#fed7aa",
2321
+ 4: "#fdba74",
2322
+ 5: "#fb923c",
2323
+ 6: "#f97316",
2324
+ 7: "#ea580c",
2325
+ 8: "#c2410c",
2326
+ 9: "#9a3412",
2327
+ 10: "#7c2d12",
2328
+ 11: "#431407",
2329
+ 12: "#270c04"
2216
2330
  },
2217
- // ── Line height ────────────────────────────────────────────────────────────
2218
- leading: ({ value, isArbitrary }, { lineHeight }) => {
2219
- if (isArbitrary) return { lineHeight: getEffectiveIsWeb() ? value : toNativeValue(value) };
2220
- const v = lineHeight[value];
2221
- if (v === void 0) return null;
2222
- if (typeof v === "string" && !getEffectiveIsWeb()) return { lineHeight: toNativeValue(v) };
2223
- return { lineHeight: v };
2331
+ amber: {
2332
+ 1: "#fffbeb",
2333
+ 2: "#fef3c7",
2334
+ 3: "#fde68a",
2335
+ 4: "#fcd34d",
2336
+ 5: "#fbbf24",
2337
+ 6: "#f59e0b",
2338
+ 7: "#d97706",
2339
+ 8: "#b45309",
2340
+ 9: "#92400e",
2341
+ 10: "#78350f",
2342
+ 11: "#451a03",
2343
+ 12: "#291002"
2224
2344
  },
2225
- // ── Letter spacing ─────────────────────────────────────────────────────────
2226
- tracking: ({ value, isArbitrary }, { letterSpacing }) => {
2227
- if (isArbitrary) return { letterSpacing: getEffectiveIsWeb() ? value : toNativeValue(value) };
2228
- const v = letterSpacing[value];
2229
- return v !== void 0 ? { letterSpacing: v } : null;
2345
+ yellow: {
2346
+ 1: "#fefce8",
2347
+ 2: "#fef9c3",
2348
+ 3: "#fef08a",
2349
+ 4: "#fde047",
2350
+ 5: "#facc15",
2351
+ 6: "#eab308",
2352
+ 7: "#ca8a04",
2353
+ 8: "#a16207",
2354
+ 9: "#854d0e",
2355
+ 10: "#713f12",
2356
+ 11: "#422006",
2357
+ 12: "#271304"
2230
2358
  },
2231
- // ── Line-clamp (web-only) ─────────────────────────────────────────────────
2232
- "line-clamp": ({ value }) => {
2233
- if (!getEffectiveIsWeb()) return null;
2234
- if (value === "none") return { overflow: "visible", display: "block", WebkitLineClamp: "unset" };
2235
- const n = parseInt(value, 10);
2236
- if (isNaN(n) || n < 1) return null;
2237
- return {
2238
- overflow: "hidden",
2239
- display: "-webkit-box",
2240
- WebkitBoxOrient: "vertical",
2241
- WebkitLineClamp: n
2242
- };
2243
- }
2244
- };
2245
-
2246
- // src/core/resolvers/transform.ts
2247
- var transformResolvers = {
2248
- // ── Transform extras (web-only) ───────────────────────────────────────────
2249
- "skew-x": ({ value, negative, isArbitrary }) => {
2250
- const deg = isArbitrary ? value : `${(negative ? -1 : 1) * parseFloat(value)}deg`;
2251
- if (!isArbitrary && isNaN(parseFloat(value))) return null;
2252
- return getEffectiveIsWeb() ? { transform: `skewX(${deg})` } : { transform: [{ skewX: deg }] };
2359
+ lime: {
2360
+ 1: "#f7fee7",
2361
+ 2: "#ecfccb",
2362
+ 3: "#d9f99d",
2363
+ 4: "#bef264",
2364
+ 5: "#a3e635",
2365
+ 6: "#84cc16",
2366
+ 7: "#65a30d",
2367
+ 8: "#4d7c0f",
2368
+ 9: "#3f6212",
2369
+ 10: "#365314",
2370
+ 11: "#1a2e05",
2371
+ 12: "#0f1b03"
2372
+ },
2373
+ green: {
2374
+ 1: "#f0fdf4",
2375
+ 2: "#dcfce7",
2376
+ 3: "#bbf7d0",
2377
+ 4: "#86efac",
2378
+ 5: "#4ade80",
2379
+ 6: "#22c55e",
2380
+ 7: "#16a34a",
2381
+ 8: "#15803d",
2382
+ 9: "#166534",
2383
+ 10: "#14532d",
2384
+ 11: "#052e16",
2385
+ 12: "#031b0d"
2386
+ },
2387
+ emerald: {
2388
+ 1: "#ecfdf5",
2389
+ 2: "#d1fae5",
2390
+ 3: "#a7f3d0",
2391
+ 4: "#6ee7b7",
2392
+ 5: "#34d399",
2393
+ 6: "#10b981",
2394
+ 7: "#059669",
2395
+ 8: "#047857",
2396
+ 9: "#065f46",
2397
+ 10: "#064e3b",
2398
+ 11: "#022c22",
2399
+ 12: "#011a14"
2400
+ },
2401
+ teal: {
2402
+ 1: "#f0fdfa",
2403
+ 2: "#ccfbf1",
2404
+ 3: "#99f6e4",
2405
+ 4: "#5eead4",
2406
+ 5: "#2dd4bf",
2407
+ 6: "#14b8a6",
2408
+ 7: "#0d9488",
2409
+ 8: "#0f766e",
2410
+ 9: "#115e59",
2411
+ 10: "#134e4a",
2412
+ 11: "#042f2e",
2413
+ 12: "#021c1b"
2414
+ },
2415
+ cyan: {
2416
+ 1: "#ecfeff",
2417
+ 2: "#cffafe",
2418
+ 3: "#a5f3fc",
2419
+ 4: "#67e8f9",
2420
+ 5: "#22d3ee",
2421
+ 6: "#06b6d4",
2422
+ 7: "#0891b2",
2423
+ 8: "#0e7490",
2424
+ 9: "#155e75",
2425
+ 10: "#164e63",
2426
+ 11: "#083344",
2427
+ 12: "#041e28"
2253
2428
  },
2254
- "skew-y": ({ value, negative, isArbitrary }) => {
2255
- const deg = isArbitrary ? value : `${(negative ? -1 : 1) * parseFloat(value)}deg`;
2256
- if (!isArbitrary && isNaN(parseFloat(value))) return null;
2257
- return getEffectiveIsWeb() ? { transform: `skewY(${deg})` } : { transform: [{ skewY: deg }] };
2429
+ sky: {
2430
+ 1: "#f0f9ff",
2431
+ 2: "#e0f2fe",
2432
+ 3: "#bae6fd",
2433
+ 4: "#7dd3fc",
2434
+ 5: "#38bdf8",
2435
+ 6: "#0ea5e9",
2436
+ 7: "#0284c7",
2437
+ 8: "#0369a1",
2438
+ 9: "#075985",
2439
+ 10: "#0c4a6e",
2440
+ 11: "#082f49",
2441
+ 12: "#041b2b"
2258
2442
  },
2259
- // Arbitrary full transform string: transform-[rotate(45deg)_scale(1.5)]
2260
- transform: ({ value, isArbitrary }) => {
2261
- if (!getEffectiveIsWeb()) return null;
2262
- if (isArbitrary) return { transform: value.replace(/_/g, " ") };
2263
- if (value === "none") return { transform: "none" };
2264
- return null;
2443
+ blue: {
2444
+ 1: "#eff6ff",
2445
+ 2: "#dbeafe",
2446
+ 3: "#bfdbfe",
2447
+ 4: "#93c5fd",
2448
+ 5: "#60a5fa",
2449
+ 6: "#3b82f6",
2450
+ 7: "#2563eb",
2451
+ 8: "#1d4ed8",
2452
+ 9: "#1e40af",
2453
+ 10: "#1e3a8a",
2454
+ 11: "#172554",
2455
+ 12: "#0d1633"
2265
2456
  },
2266
- // Transform origin
2267
- origin: ({ value, isArbitrary }) => {
2268
- if (!getEffectiveIsWeb()) return null;
2269
- if (isArbitrary) return { transformOrigin: value.replace(/_/g, " ") };
2270
- const origins = {
2271
- center: "center",
2272
- top: "top",
2273
- "top-right": "top right",
2274
- right: "right",
2275
- "bottom-right": "bottom right",
2276
- bottom: "bottom",
2277
- "bottom-left": "bottom left",
2278
- left: "left",
2279
- "top-left": "top left"
2280
- };
2281
- return origins[value] ? { transformOrigin: origins[value] } : null;
2457
+ indigo: {
2458
+ 1: "#eef2ff",
2459
+ 2: "#e0e7ff",
2460
+ 3: "#c7d2fe",
2461
+ 4: "#a5b4fc",
2462
+ 5: "#818cf8",
2463
+ 6: "#6366f1",
2464
+ 7: "#4f46e5",
2465
+ 8: "#4338ca",
2466
+ 9: "#3730a3",
2467
+ 10: "#312e81",
2468
+ 11: "#1e1b4b",
2469
+ 12: "#12102d"
2282
2470
  },
2283
- // ── Scale ──────────────────────────────────────────────────────────────────
2284
- // Non-arbitrary: scale-150 → value='150' → 150/100 = 1.5
2285
- // Arbitrary: scale-[1.5] → value='1.5' → used as-is (already the factor)
2286
- scale: ({ value, isArbitrary }) => {
2287
- const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
2288
- if (isNaN(n)) return null;
2289
- return getEffectiveIsWeb() ? { transform: `scale(${n})` } : { transform: [{ scale: n }] };
2471
+ violet: {
2472
+ 1: "#f5f3ff",
2473
+ 2: "#ede9fe",
2474
+ 3: "#ddd6fe",
2475
+ 4: "#c4b5fd",
2476
+ 5: "#a78bfa",
2477
+ 6: "#8b5cf6",
2478
+ 7: "#7c3aed",
2479
+ 8: "#6d28d9",
2480
+ 9: "#5b21b6",
2481
+ 10: "#4c1d95",
2482
+ 11: "#2e1065",
2483
+ 12: "#1c0a3d"
2290
2484
  },
2291
- "scale-x": ({ value, isArbitrary }) => {
2292
- const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
2293
- if (isNaN(n)) return null;
2294
- return getEffectiveIsWeb() ? { transform: `scaleX(${n})` } : { transform: [{ scaleX: n }] };
2485
+ purple: {
2486
+ 1: "#faf5ff",
2487
+ 2: "#f3e8ff",
2488
+ 3: "#e9d5ff",
2489
+ 4: "#d8b4fe",
2490
+ 5: "#c084fc",
2491
+ 6: "#a855f7",
2492
+ 7: "#9333ea",
2493
+ 8: "#7e22ce",
2494
+ 9: "#6b21a8",
2495
+ 10: "#581c87",
2496
+ 11: "#3b0764",
2497
+ 12: "#23043c"
2295
2498
  },
2296
- "scale-y": ({ value, isArbitrary }) => {
2297
- const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
2298
- if (isNaN(n)) return null;
2299
- return getEffectiveIsWeb() ? { transform: `scaleY(${n})` } : { transform: [{ scaleY: n }] };
2499
+ fuchsia: {
2500
+ 1: "#fdf4ff",
2501
+ 2: "#fae8ff",
2502
+ 3: "#f5d0fe",
2503
+ 4: "#f0abfc",
2504
+ 5: "#e879f9",
2505
+ 6: "#d946ef",
2506
+ 7: "#c026d3",
2507
+ 8: "#a21caf",
2508
+ 9: "#86198f",
2509
+ 10: "#701a75",
2510
+ 11: "#4a044e",
2511
+ 12: "#2d022f"
2300
2512
  },
2301
- // ── Rotate ─────────────────────────────────────────────────────────────────
2302
- rotate: ({ value, negative, isArbitrary }) => {
2303
- if (isArbitrary) {
2304
- const finalValue = negative ? `-${value}` : value;
2305
- return getEffectiveIsWeb() ? { transform: `rotate(${finalValue})` } : { transform: [{ rotate: finalValue }] };
2306
- }
2307
- const deg = parseFloat(value);
2308
- if (isNaN(deg)) return null;
2309
- const finalDeg = negative ? -deg : deg;
2310
- return getEffectiveIsWeb() ? { transform: `rotate(${finalDeg}deg)` } : { transform: [{ rotate: `${finalDeg}deg` }] };
2513
+ pink: {
2514
+ 1: "#fdf2f8",
2515
+ 2: "#fce7f3",
2516
+ 3: "#fbcfe8",
2517
+ 4: "#f9a8d4",
2518
+ 5: "#f472b6",
2519
+ 6: "#ec4899",
2520
+ 7: "#db2777",
2521
+ 8: "#be185d",
2522
+ 9: "#9d174d",
2523
+ 10: "#831843",
2524
+ 11: "#500724",
2525
+ 12: "#300415"
2311
2526
  },
2312
- // ── Perspective transform (cross-platform: iOS, Android, web) ────────────
2313
- perspective: ({ value, isArbitrary }) => {
2314
- if (isArbitrary) {
2315
- const numPx = parseFloat(value);
2316
- if (getEffectiveIsWeb()) return { transform: `perspective(${value})` };
2317
- return isNaN(numPx) ? null : { transform: [{ perspective: numPx }] };
2318
- }
2319
- if (value === "none") return getEffectiveIsWeb() ? { transform: "none" } : null;
2320
- const n = parseFloat(value);
2321
- if (isNaN(n)) return null;
2322
- return getEffectiveIsWeb() ? { transform: `perspective(${n}px)` } : { transform: [{ perspective: n }] };
2527
+ rose: {
2528
+ 1: "#fff1f2",
2529
+ 2: "#ffe4e6",
2530
+ 3: "#fecdd3",
2531
+ 4: "#fda4af",
2532
+ 5: "#fb7185",
2533
+ 6: "#f43f5e",
2534
+ 7: "#e11d48",
2535
+ 8: "#be123c",
2536
+ 9: "#9f1239",
2537
+ 10: "#881337",
2538
+ 11: "#4c0519",
2539
+ 12: "#2d030e"
2323
2540
  }
2324
2541
  };
2325
-
2326
- // src/core/resolvers/effects.ts
2327
- function keyframeDeclToCSS(decl) {
2328
- return Object.entries(decl).map(([prop, val]) => `${prop.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${val}`).join("; ");
2329
- }
2330
- function buildKeyframeCSS(name, steps) {
2331
- const body = Object.entries(steps).map(([selector, decl]) => `${selector} { ${keyframeDeclToCSS(decl)} }`).join(" ");
2332
- return `@keyframes ${name} { ${body} }`;
2333
- }
2334
- var BUILTIN_ANIMATIONS = {
2335
- none: { animation: "none" },
2336
- spin: {
2337
- animation: "kb-spin 1s linear infinite",
2338
- __keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
2542
+ var defaultTheme = {
2543
+ colors: defaultColors,
2544
+ // 1 unit = 4px
2545
+ spacing: {
2546
+ px: 1,
2547
+ 0: 0,
2548
+ "0.5": 2,
2549
+ 1: 4,
2550
+ "1.5": 6,
2551
+ 2: 8,
2552
+ "2.5": 10,
2553
+ 3: 12,
2554
+ "3.5": 14,
2555
+ 4: 16,
2556
+ 5: 20,
2557
+ 6: 24,
2558
+ 7: 28,
2559
+ 8: 32,
2560
+ 9: 36,
2561
+ 10: 40,
2562
+ 11: 44,
2563
+ 12: 48,
2564
+ 14: 56,
2565
+ 16: 64,
2566
+ 20: 80,
2567
+ 24: 96,
2568
+ 28: 112,
2569
+ 32: 128,
2570
+ 36: 144,
2571
+ 40: 160,
2572
+ 44: 176,
2573
+ 48: 192,
2574
+ 52: 208,
2575
+ 56: 224,
2576
+ 60: 240,
2577
+ 64: 256,
2578
+ 72: 288,
2579
+ 80: 320,
2580
+ 96: 384,
2581
+ auto: "auto",
2582
+ full: "100%",
2583
+ "1/2": "50%",
2584
+ "1/3": "33.333333%",
2585
+ "2/3": "66.666667%",
2586
+ "1/4": "25%",
2587
+ "3/4": "75%",
2588
+ // Dynamic viewport unit — see the comment on the 'screen' standalone utilities
2589
+ // in utilities.ts for why dvh beats vh on mobile. Used by h-screen (w-screen
2590
+ // has its own standalone entry so it never reaches this spacing lookup).
2591
+ screen: "100dvh",
2592
+ min: "min-content",
2593
+ max: "max-content",
2594
+ fit: "fit-content"
2339
2595
  },
2340
- ping: {
2341
- animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
2342
- __keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
2596
+ fontSize: {
2597
+ xs: 12,
2598
+ sm: 14,
2599
+ base: 16,
2600
+ lg: 18,
2601
+ xl: 20,
2602
+ "2xl": 24,
2603
+ "3xl": 30,
2604
+ "4xl": 36,
2605
+ "5xl": 48,
2606
+ "6xl": 60,
2607
+ "7xl": 72,
2608
+ "8xl": 96,
2609
+ "9xl": 128
2343
2610
  },
2344
- pulse: {
2345
- animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
2346
- __keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
2611
+ fontFamily: {
2612
+ sans: "System",
2613
+ mono: "Courier New",
2614
+ serif: "Georgia"
2347
2615
  },
2348
- bounce: {
2349
- animation: "kb-bounce 1s infinite",
2350
- __keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
2351
- }
2352
- };
2353
- function buildAnimationValue(animation, keyframes) {
2354
- const words = animation.trim().split(/\s+/);
2355
- const name = words[0];
2356
- if (!name) return { animation };
2357
- const builtin = BUILTIN_ANIMATIONS[name];
2358
- if (builtin?.__keyframe) {
2359
- const kbName = builtin.animation.split(/\s+/, 1)[0];
2360
- return { animation: [kbName, ...words.slice(1)].join(" "), __keyframe: builtin.__keyframe };
2361
- }
2362
- const steps = keyframes?.[name];
2363
- return steps ? { animation, __keyframe: buildKeyframeCSS(name, steps) } : { animation };
2364
- }
2365
- var effectResolvers = {
2366
- // ── Opacity ────────────────────────────────────────────────────────────────
2367
- opacity: ({ value, isArbitrary }, { opacity }) => {
2368
- if (isArbitrary) {
2369
- const v2 = parseFloat(value);
2370
- return isNaN(v2) ? null : { opacity: v2 > 1 ? v2 / 100 : v2 };
2371
- }
2372
- const v = opacity[value];
2373
- if (v !== void 0) return { opacity: v };
2374
- const n = parseFloat(value);
2375
- return isNaN(n) ? null : { opacity: n > 1 ? n / 100 : n };
2616
+ fontWeight: {
2617
+ thin: "100",
2618
+ extralight: "200",
2619
+ light: "300",
2620
+ normal: "400",
2621
+ medium: "500",
2622
+ semibold: "600",
2623
+ bold: "700",
2624
+ extrabold: "800",
2625
+ black: "900"
2376
2626
  },
2377
- // ── Shadow ─────────────────────────────────────────────────────────────────
2378
- shadow: ({ value }, { shadow }) => {
2379
- const key = value === "" ? "DEFAULT" : value;
2380
- return shadow[key] ?? null;
2627
+ borderRadius: {
2628
+ none: 0,
2629
+ sm: 2,
2630
+ DEFAULT: 4,
2631
+ md: 6,
2632
+ lg: 8,
2633
+ xl: 12,
2634
+ "2xl": 16,
2635
+ "3xl": 24,
2636
+ full: 9999
2381
2637
  },
2382
- // ── Animations (CSS keyframe animations, web-only) ────────────────────────
2383
- // Each variant injects a @keyframes rule once via the __keyframe marker.
2384
- // resolver.ts detects __keyframe, injects the rule, then strips the marker
2385
- // so it never reaches element inline styles.
2386
- animate: ({ value, isArbitrary }, theme) => {
2387
- if (!getEffectiveIsWeb()) return null;
2388
- if (isArbitrary) return buildAnimationValue(value.replace(/_/g, " "), theme.keyframes);
2389
- if (value in BUILTIN_ANIMATIONS) return BUILTIN_ANIMATIONS[value];
2390
- const custom = theme.animation?.[value];
2391
- return custom ? buildAnimationValue(custom, theme.keyframes) : null;
2638
+ borderWidth: {
2639
+ DEFAULT: 1,
2640
+ 0: 0,
2641
+ 2: 2,
2642
+ 4: 4,
2643
+ 8: 8
2392
2644
  },
2393
- // ── Transition (web-only; use Animated API on native) ─────────────────────
2394
- transition: ({ value }) => {
2395
- if (!getEffectiveIsWeb()) return null;
2396
- const presets = {
2397
- "": "color 150ms, background-color 150ms, border-color 150ms, text-decoration-color 150ms, fill 150ms, stroke 150ms, opacity 150ms, box-shadow 150ms, transform 150ms, filter 150ms, backdrop-filter 150ms",
2398
- all: "all 150ms",
2399
- none: "none",
2400
- colors: "color 150ms, background-color 150ms, border-color 150ms",
2401
- opacity: "opacity 150ms",
2402
- shadow: "box-shadow 150ms",
2403
- transform: "transform 150ms"
2404
- };
2405
- const v = presets[value];
2406
- return v !== void 0 ? { transition: v } : null;
2645
+ opacity: {
2646
+ 0: 0,
2647
+ 5: 0.05,
2648
+ 10: 0.1,
2649
+ 15: 0.15,
2650
+ 20: 0.2,
2651
+ 25: 0.25,
2652
+ 30: 0.3,
2653
+ 40: 0.4,
2654
+ 50: 0.5,
2655
+ 60: 0.6,
2656
+ 70: 0.7,
2657
+ 75: 0.75,
2658
+ 80: 0.8,
2659
+ 90: 0.9,
2660
+ 95: 0.95,
2661
+ 100: 1
2407
2662
  },
2408
- duration: ({ value, isArbitrary }) => {
2409
- if (!getEffectiveIsWeb()) return null;
2410
- if (!isArbitrary && isNaN(parseFloat(value))) return null;
2411
- const ms = isArbitrary ? value : `${value}ms`;
2412
- return { transitionDuration: ms };
2663
+ lineHeight: {
2664
+ none: 1,
2665
+ tight: 1.25,
2666
+ snug: 1.375,
2667
+ normal: 1.5,
2668
+ relaxed: 1.625,
2669
+ loose: 2,
2670
+ // Pixel values stored as strings; styleValueToCSS passes them through, toNativeValue strips the unit
2671
+ 3: "12px",
2672
+ 4: "16px",
2673
+ 5: "20px",
2674
+ 6: "24px",
2675
+ 7: "28px",
2676
+ 8: "32px",
2677
+ 9: "36px",
2678
+ 10: "40px"
2413
2679
  },
2414
- delay: ({ value, isArbitrary }) => {
2415
- if (!getEffectiveIsWeb()) return null;
2416
- if (!isArbitrary && isNaN(parseFloat(value))) return null;
2417
- const ms = isArbitrary ? value : `${value}ms`;
2418
- return { transitionDelay: ms };
2419
- }
2420
- };
2421
-
2422
- // src/core/resolvers/misc.ts
2423
- var miscResolvers = {
2424
- // ── Arbitrary CSS property (web-only): [property:value] with no utility prefix ──
2425
- // e.g. [mask-type:luminance], [--my-var:10px]. parser.ts already isolates this case
2426
- // as utility: '' — it's ALSO how a stray, prefix-less negative bracket ("-[10px]",
2427
- // presumably a typo) parses, which is why this only fires when the arbitrary value
2428
- // itself contains a "property:value" pair; anything else (no colon) still resolves
2429
- // to null exactly as before this resolver existed, rather than guessing a property.
2430
- //
2431
- // Only the FIRST colon splits property from value — the value itself may contain
2432
- // more colons (a URL's "http://…") and must stay intact: [background:url(http://x/a.png)].
2433
- // camelToKebab() in resolver.ts is a no-op on an already-kebab-case (or "--"-prefixed
2434
- // custom property) key, so it can be used as the style object key as-written with no
2435
- // extra case conversion — it only needs to look like a plausible CSS property name.
2436
- "": ({ value, isArbitrary, negative }) => {
2437
- if (!getEffectiveIsWeb() || !isArbitrary || !value) return null;
2438
- if (negative) return null;
2439
- const colonIdx = value.indexOf(":");
2440
- if (colonIdx <= 0) return null;
2441
- const prop = value.slice(0, colonIdx).trim();
2442
- const cssValue = value.slice(colonIdx + 1).trim();
2443
- if (!cssValue || !/^(--[\w-]+|[a-zA-Z-]+)$/.test(prop)) return null;
2444
- return { [prop]: cssValue };
2680
+ letterSpacing: {
2681
+ tighter: -0.8,
2682
+ tight: -0.4,
2683
+ normal: 0,
2684
+ wide: 0.4,
2685
+ wider: 0.8,
2686
+ widest: 1.6
2445
2687
  },
2446
- // ── SVG stroke/fill (web-only) ────────────────────────────────────────────
2447
- // Native is intentionally excluded: react-native-svg's <Path>/<Circle>/etc.
2448
- // take stroke/fill as component PROPS, not style entries, so there's no
2449
- // reliable way to apply a resolved color through the `style` prop there.
2450
- // `stroke-{n}` sets strokeWidth (a plain number, unlike a color) — same
2451
- // color-first-then-numeric-fallback disambiguation `border` uses above.
2452
- stroke: ({ value, isArbitrary }, { colors }) => {
2453
- if (!getEffectiveIsWeb()) return null;
2454
- if (!value) return null;
2455
- if (value === "none") return { stroke: "none" };
2456
- if (isArbitrary) {
2457
- const w = toNativeValue(value);
2458
- return typeof w === "number" ? { strokeWidth: w } : { stroke: value };
2459
- }
2460
- const color = resolveColor(value, colors, false);
2461
- if (color) return { stroke: color };
2462
- const n = parseFloat(value);
2463
- return isNaN(n) ? null : { strokeWidth: n };
2688
+ zIndex: {
2689
+ auto: "auto",
2690
+ 0: 0,
2691
+ 10: 10,
2692
+ 20: 20,
2693
+ 30: 30,
2694
+ 40: 40,
2695
+ 50: 50
2464
2696
  },
2465
- fill: ({ value, isArbitrary }, { colors }) => {
2466
- if (!getEffectiveIsWeb()) return null;
2467
- if (!value) return null;
2468
- if (value === "none") return { fill: "none" };
2469
- if (isArbitrary) return { fill: value };
2470
- const color = resolveColor(value, colors, false);
2471
- return color ? { fill: color } : null;
2697
+ flex: {
2698
+ 1: 1,
2699
+ auto: "auto",
2700
+ // CSS: flex: auto = 1 1 auto; native: mapped to 1 in resolver
2701
+ initial: "initial",
2702
+ // CSS: flex: initial = 0 1 auto; native: mapped to 1
2703
+ none: "none"
2704
+ // CSS: flex: none = 0 0 auto; native: mapped to 0
2472
2705
  },
2473
- // ── Text shadow (cross-platform: iOS, Android, web) ─────────────────────
2474
- // Named presets give cross-platform shadow; arbitrary is web-only.
2475
- "text-shadow": ({ value, isArbitrary }, { colors }) => {
2476
- if (value === "none") {
2477
- return getEffectiveIsWeb() ? { textShadow: "none" } : { textShadowColor: "transparent", textShadowOffset: { width: 0, height: 0 }, textShadowRadius: 0 };
2478
- }
2479
- const webPresets = {
2480
- "": "0 2px 4px rgba(0,0,0,0.3)",
2481
- sm: "0 1px 2px rgba(0,0,0,0.3)",
2482
- md: "0 4px 6px rgba(0,0,0,0.3)",
2483
- lg: "0 8px 16px rgba(0,0,0,0.5)",
2484
- xl: "0 16px 32px rgba(0,0,0,0.5)"
2485
- };
2486
- const nativePresets = {
2487
- "": { textShadowColor: "rgba(0,0,0,0.3)", textShadowOffset: { width: 0, height: 2 }, textShadowRadius: 4 },
2488
- sm: { textShadowColor: "rgba(0,0,0,0.3)", textShadowOffset: { width: 0, height: 1 }, textShadowRadius: 2 },
2489
- md: { textShadowColor: "rgba(0,0,0,0.3)", textShadowOffset: { width: 0, height: 4 }, textShadowRadius: 6 },
2490
- lg: { textShadowColor: "rgba(0,0,0,0.5)", textShadowOffset: { width: 0, height: 8 }, textShadowRadius: 16 },
2491
- xl: { textShadowColor: "rgba(0,0,0,0.5)", textShadowOffset: { width: 0, height: 16 }, textShadowRadius: 32 }
2492
- };
2493
- if (value in webPresets) {
2494
- if (getEffectiveIsWeb()) return { textShadow: webPresets[value] };
2495
- return nativePresets[value] ?? null;
2496
- }
2497
- if (isArbitrary) {
2498
- return getEffectiveIsWeb() ? { textShadow: value.replace(/_/g, " ") } : null;
2499
- }
2500
- const color = resolveColor(value, colors, false);
2501
- if (color) {
2502
- return getEffectiveIsWeb() ? { textShadow: `0 2px 4px ${color}` } : { textShadowColor: color };
2706
+ shadow: {
2707
+ sm: {
2708
+ shadowColor: "#000",
2709
+ shadowOffset: { width: 0, height: 1 },
2710
+ shadowOpacity: 0.05,
2711
+ shadowRadius: 2,
2712
+ elevation: 1
2713
+ },
2714
+ DEFAULT: {
2715
+ shadowColor: "#000",
2716
+ shadowOffset: { width: 0, height: 2 },
2717
+ shadowOpacity: 0.1,
2718
+ shadowRadius: 4,
2719
+ elevation: 2
2720
+ },
2721
+ md: {
2722
+ shadowColor: "#000",
2723
+ shadowOffset: { width: 0, height: 4 },
2724
+ shadowOpacity: 0.1,
2725
+ shadowRadius: 8,
2726
+ elevation: 3
2727
+ },
2728
+ lg: {
2729
+ shadowColor: "#000",
2730
+ shadowOffset: { width: 0, height: 8 },
2731
+ shadowOpacity: 0.1,
2732
+ shadowRadius: 15,
2733
+ elevation: 4
2734
+ },
2735
+ xl: {
2736
+ shadowColor: "#000",
2737
+ shadowOffset: { width: 0, height: 16 },
2738
+ shadowOpacity: 0.1,
2739
+ shadowRadius: 24,
2740
+ elevation: 6
2741
+ },
2742
+ "2xl": {
2743
+ shadowColor: "#000",
2744
+ shadowOffset: { width: 0, height: 24 },
2745
+ shadowOpacity: 0.25,
2746
+ shadowRadius: 48,
2747
+ elevation: 8
2748
+ },
2749
+ none: {
2750
+ shadowColor: "transparent",
2751
+ shadowOffset: { width: 0, height: 0 },
2752
+ shadowOpacity: 0,
2753
+ shadowRadius: 0,
2754
+ elevation: 0
2503
2755
  }
2504
- return null;
2505
- }
2506
- };
2507
-
2508
- // src/core/utilities.ts
2509
- var RESOLVERS = {
2510
- ...colorResolvers,
2511
- ...spacingResolvers,
2512
- ...borderResolvers,
2513
- ...layoutResolvers,
2514
- ...typographyResolvers,
2515
- ...filterResolvers,
2516
- ...transformResolvers,
2517
- ...effectResolvers,
2518
- ...miscResolvers
2756
+ },
2757
+ screens: {
2758
+ sm: 576,
2759
+ // small tablets / large phones (landscape)
2760
+ md: 768,
2761
+ // tablets (iPad and up)
2762
+ lg: 1024,
2763
+ // large tablets (iPad Pro)
2764
+ xl: 1280,
2765
+ // desktop
2766
+ "2xl": 1536
2767
+ },
2768
+ // Empty by default — the 4 built-in presets (spin/ping/pulse/bounce) are handled
2769
+ // directly in the `animate` resolver, not through this theme table. This exists so
2770
+ // user-defined ones (kbach.config.js theme.extend.keyframes/animation) merge in
2771
+ // alongside them without needing to redeclare the built-ins.
2772
+ keyframes: {},
2773
+ animation: {}
2519
2774
  };
2520
- var NAMED_GROUP_PEER_MARKER_RE = /^(group|peer)\/.+$/;
2521
- function resolveUtility(parsed, theme) {
2522
- if (!parsed.value) {
2523
- if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
2524
- if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
2525
- if (NAMED_GROUP_PEER_MARKER_RE.test(parsed.utility)) return getEffectiveIsWeb() ? {} : null;
2526
- }
2527
- const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
2528
- if (resolver) return resolver(parsed, theme);
2529
- return null;
2530
- }
2531
- var PLUGIN_STANDALONE = {};
2532
- var PLUGIN_RESOLVERS = {};
2533
- function clearPluginUtilities() {
2534
- for (const key of Object.keys(PLUGIN_STANDALONE)) delete PLUGIN_STANDALONE[key];
2535
- for (const key of Object.keys(PLUGIN_RESOLVERS)) delete PLUGIN_RESOLVERS[key];
2536
- _sortedPrefixes = null;
2537
- _standaloneNames = null;
2538
- }
2539
- function getPluginStandaloneMap() {
2540
- return PLUGIN_STANDALONE;
2541
- }
2542
- function isKnownUtility(utility) {
2543
- return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS || NAMED_GROUP_PEER_MARKER_RE.test(utility);
2544
- }
2545
- var _sortedPrefixes = null;
2546
- var _standaloneNames = null;
2547
- function getBuiltinUtilityPrefixes() {
2548
- if (!_sortedPrefixes) {
2549
- const all = [.../* @__PURE__ */ new Set([...Object.keys(RESOLVERS), ...Object.keys(PLUGIN_RESOLVERS)])];
2550
- _sortedPrefixes = all.sort((a, b) => b.length - a.length);
2551
- }
2552
- return _sortedPrefixes;
2553
- }
2554
- function getBuiltinStandaloneNames() {
2555
- if (!_standaloneNames) {
2556
- _standaloneNames = /* @__PURE__ */ new Set([...Object.keys(getStandalone()), ...Object.keys(PLUGIN_STANDALONE)]);
2557
- }
2558
- return _standaloneNames;
2559
- }
2560
-
2561
- // src/core/parser.ts
2562
- var UNSAFE_ARBITRARY_VALUE = /[{};\\]|\/\*|\*\//;
2563
- function isSafeArbitraryValue(value) {
2564
- if (!UNSAFE_ARBITRARY_VALUE.test(value)) return true;
2565
- if (process.env.NODE_ENV !== "production") {
2566
- kbachWarn(`Unsafe arbitrary value rejected: "${value}"`);
2567
- }
2568
- return false;
2569
- }
2570
- function parseClass(className) {
2571
- const trimmed = className.trim();
2572
- if (!trimmed) return null;
2573
- const modifiers = [];
2574
- let remaining = trimmed;
2575
- while (true) {
2576
- const colonIdx = findOuterColon(remaining);
2577
- if (colonIdx === -1) break;
2578
- const candidate = remaining.slice(0, colonIdx);
2579
- if (!isKnownModifier(candidate)) break;
2580
- modifiers.push(candidate);
2581
- remaining = remaining.slice(colonIdx + 1);
2582
- }
2583
- let important = false;
2584
- if (remaining.startsWith("!") && remaining.length > 1) {
2585
- important = true;
2586
- remaining = remaining.slice(1);
2587
- }
2588
- let negative = false;
2589
- if (remaining.startsWith("-") && remaining.length > 1 && remaining[1] !== "-") {
2590
- negative = true;
2591
- remaining = remaining.slice(1);
2592
- }
2593
- let bracketDepth = 0;
2594
- let bracketStart = -1;
2595
- for (let i = remaining.length - 1; i >= 0; i--) {
2596
- if (remaining[i] === "]") bracketDepth++;
2597
- else if (remaining[i] === "[") {
2598
- bracketDepth--;
2599
- if (bracketDepth === 0) {
2600
- bracketStart = i;
2601
- break;
2602
- }
2603
- }
2604
- }
2605
- if (bracketStart > 0 && remaining[bracketStart - 1] === "-" && remaining.endsWith("]")) {
2606
- const utility = remaining.slice(0, bracketStart - 1);
2607
- const value = remaining.slice(bracketStart + 1, -1).replace(/_/g, " ");
2608
- if (!isSafeArbitraryValue(value)) return null;
2609
- return { original: trimmed, modifiers, negative, important, utility, value, isArbitrary: true };
2610
- }
2611
- if (remaining.endsWith("]") && bracketStart === -1) {
2612
- if (process.env.NODE_ENV !== "production") {
2613
- kbachWarn(`Unbalanced brackets: "${trimmed}"`);
2614
- }
2615
- }
2616
- if (remaining.startsWith("[") && remaining.endsWith("]")) {
2617
- const value = remaining.slice(1, -1).replace(/_/g, " ");
2618
- if (!isSafeArbitraryValue(value)) return null;
2619
- return { original: trimmed, modifiers, negative, important, utility: "", value, isArbitrary: true };
2620
- }
2621
- if (getBuiltinStandaloneNames().has(remaining)) {
2622
- return { original: trimmed, modifiers, negative, important, utility: remaining, value: "", isArbitrary: false };
2623
- }
2624
- for (const prefix of getBuiltinUtilityPrefixes()) {
2625
- if (remaining === prefix) {
2626
- return { original: trimmed, modifiers, negative, important, utility: prefix, value: "", isArbitrary: false };
2627
- }
2628
- if (remaining.startsWith(prefix + "-")) {
2629
- const value = remaining.slice(prefix.length + 1);
2630
- return { original: trimmed, modifiers, negative, important, utility: prefix, value, isArbitrary: false };
2631
- }
2632
- }
2633
- const firstDash = remaining.indexOf("-");
2634
- if (firstDash > 0) {
2635
- return {
2636
- original: trimmed,
2637
- modifiers,
2638
- negative,
2639
- important,
2640
- utility: remaining.slice(0, firstDash),
2641
- value: remaining.slice(firstDash + 1),
2642
- isArbitrary: false
2643
- };
2644
- }
2645
- return { original: trimmed, modifiers, negative, important, utility: remaining, value: "", isArbitrary: false };
2646
- }
2647
- function splitClassTokens(classString) {
2648
- const tokens = [];
2649
- let current = "";
2650
- let depth = 0;
2651
- let parenDepth = 0;
2652
- for (let i = 0; i < classString.length; i++) {
2653
- const ch = classString[i];
2654
- if (ch === "[") {
2655
- depth++;
2656
- current += ch;
2657
- continue;
2658
- }
2659
- if (ch === "]") {
2660
- depth--;
2661
- current += ch;
2662
- continue;
2663
- }
2664
- if (ch === "(") {
2665
- parenDepth++;
2666
- current += ch;
2667
- continue;
2668
- }
2669
- if (ch === ")") {
2670
- parenDepth--;
2671
- current += ch;
2672
- continue;
2673
- }
2674
- if (/\s/.test(ch)) {
2675
- if (depth === 0 && parenDepth === 0) {
2676
- if (current) {
2677
- tokens.push(current);
2678
- current = "";
2679
- }
2680
- }
2681
- } else {
2682
- current += ch;
2683
- }
2684
- }
2685
- if (current) tokens.push(current);
2686
- return tokens;
2687
- }
2688
- function normalizeClassString(classString) {
2689
- return splitClassTokens(classString).join(" ");
2690
- }
2691
- function parseClasses(classString) {
2692
- const results = [];
2693
- for (const token of splitClassTokens(classString)) {
2694
- const parsed = parseClass(token);
2695
- if (parsed) results.push(parsed);
2696
- }
2697
- return results;
2698
- }
2699
- function findOuterColon(s) {
2700
- let depth = 0;
2701
- for (let i = 0; i < s.length; i++) {
2702
- const ch = s[i];
2703
- if (ch === "[") depth++;
2704
- else if (ch === "]") depth--;
2705
- else if (ch === ":" && depth === 0) return i;
2706
- }
2707
- return -1;
2708
- }
2709
2775
 
2710
2776
  // src/core/reset.ts
2711
2777
  var RESET_STYLE_ID = "kbach-reset";
@@ -3057,7 +3123,7 @@ function resolve(classString, theme, darkMode = "attribute") {
3057
3123
  if (cached) return cached;
3058
3124
  const result = {};
3059
3125
  const onWeb = isWeb;
3060
- for (const parsed of parseClasses(classString)) {
3126
+ for (const parsed of parseClasses(expandModeAwareColorClasses(classString, theme.colors))) {
3061
3127
  const styles = resolveUtility(parsed, theme);
3062
3128
  if (!styles) {
3063
3129
  if (process.env.NODE_ENV !== "production" && !parsed.isArbitrary && !isKnownUtility(parsed.utility) && !parsed.original.startsWith("__")) {
@@ -3166,8 +3232,9 @@ function resolveOneRef(ref, colors) {
3166
3232
  const shade = ref.slice(lastDash + 1);
3167
3233
  if (!/^\d+$/.test(shade)) return null;
3168
3234
  const entry = colors[name];
3169
- if (!entry || typeof entry !== "object") return null;
3170
- return entry[shade] ?? null;
3235
+ if (!entry || typeof entry !== "object" || isModeAwareColor(entry)) return null;
3236
+ const target = entry[shade];
3237
+ return typeof target === "string" ? target : null;
3171
3238
  }
3172
3239
  function resolveColorRefs(colors) {
3173
3240
  const MAX_DEPTH = 5;
@@ -3180,14 +3247,17 @@ function resolveColorRefs(colors) {
3180
3247
  }
3181
3248
  return current;
3182
3249
  }
3250
+ function resolveValue(val) {
3251
+ return typeof val === "string" ? resolveChain(val) : { light: resolveChain(val.light), dark: resolveChain(val.dark) };
3252
+ }
3183
3253
  const out = {};
3184
3254
  for (const [key, val] of Object.entries(colors)) {
3185
- if (typeof val === "string") {
3186
- out[key] = resolveChain(val);
3255
+ if (typeof val === "string" || isModeAwareColor(val)) {
3256
+ out[key] = resolveValue(val);
3187
3257
  } else {
3188
3258
  const shades = {};
3189
- for (const [shade, hex] of Object.entries(val)) {
3190
- shades[shade] = resolveChain(hex);
3259
+ for (const [shade, v] of Object.entries(val)) {
3260
+ shades[shade] = resolveValue(v);
3191
3261
  }
3192
3262
  out[key] = shades;
3193
3263
  }
@@ -3841,10 +3911,7 @@ var InteractiveWrapper = forwardRef(
3841
3911
  InteractiveWrapper.displayName = "Kbach.InteractiveWrapper";
3842
3912
 
3843
3913
  export {
3844
- defaultColors,
3845
- defaultTheme,
3846
- RESET_STYLE_ID,
3847
- BASE_RESET,
3914
+ isModeAwareColor,
3848
3915
  isWeb,
3849
3916
  isNative,
3850
3917
  setResolveTarget,
@@ -3858,6 +3925,11 @@ export {
3858
3925
  splitClassTokens,
3859
3926
  normalizeClassString,
3860
3927
  parseClasses,
3928
+ expandModeAwareColorClasses,
3929
+ defaultColors,
3930
+ defaultTheme,
3931
+ RESET_STYLE_ID,
3932
+ BASE_RESET,
3861
3933
  syncGlobalWidth,
3862
3934
  syncGlobalScreens,
3863
3935
  getGlobalScreens,