@kbach/ui 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4187 @@
1
+ 'use client';
2
+
3
+ // src/core/platform.ts
4
+ var isWeb = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
5
+ var isNative = !isWeb && (typeof globalThis.HermesInternal !== "undefined" || typeof globalThis.__fbBatchedBridge !== "undefined" || typeof navigator !== "undefined" && navigator.product === "ReactNative" || typeof globalThis.__REACT_NATIVE__ !== "undefined" || typeof globalThis.nativeFabricUIManager !== "undefined" || typeof globalThis.__turboModuleProxy !== "undefined" || typeof globalThis.RN$Bridgeless !== "undefined" || typeof globalThis.nativePerformanceNow !== "undefined");
6
+ var _resolveTargetOverride = null;
7
+ function setResolveTarget(target) {
8
+ _resolveTargetOverride = target;
9
+ }
10
+ function getEffectiveIsWeb() {
11
+ if (_resolveTargetOverride) return _resolveTargetOverride === "web";
12
+ return isWeb || !isNative;
13
+ }
14
+ function toNativeValue(raw) {
15
+ if (/^-?\d+(\.\d+)?px$/.test(raw)) return parseFloat(raw);
16
+ if (/^-?\d+(\.\d+)?rem$/.test(raw)) return parseFloat(raw) * 16;
17
+ if (/^-?\d+(\.\d+)?em$/.test(raw)) return parseFloat(raw) * 16;
18
+ if (/^-?\d+(\.\d+)?$/.test(raw)) return parseFloat(raw);
19
+ return raw;
20
+ }
21
+ function escapeCSSSelector(cls) {
22
+ return cls.replace(/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
23
+ }
24
+
25
+ // src/core/devWarn.ts
26
+ function kbachWarn(message) {
27
+ console.warn(
28
+ "%c[kbach]%c " + message,
29
+ "color:#8b5cf6;font-weight:700",
30
+ "color:inherit;font-weight:400"
31
+ );
32
+ }
33
+
34
+ // src/core/cache.ts
35
+ var LRUCache = class {
36
+ constructor(capacity = 1e4, onEvict) {
37
+ this.capacity = capacity;
38
+ if (capacity <= 0) throw new Error("LRUCache: capacity must be a positive integer");
39
+ this.cache = /* @__PURE__ */ new Map();
40
+ this.onEvict = onEvict;
41
+ }
42
+ get(key) {
43
+ if (!this.cache.has(key)) return void 0;
44
+ const value = this.cache.get(key);
45
+ this.cache.delete(key);
46
+ this.cache.set(key, value);
47
+ return value;
48
+ }
49
+ set(key, value) {
50
+ if (this.cache.has(key)) {
51
+ this.cache.delete(key);
52
+ } else if (this.cache.size >= this.capacity) {
53
+ const oldestKey = this.cache.keys().next().value;
54
+ const oldestValue = this.cache.get(oldestKey);
55
+ this.cache.delete(oldestKey);
56
+ this.onEvict?.(oldestKey, oldestValue);
57
+ }
58
+ this.cache.set(key, value);
59
+ return this;
60
+ }
61
+ has(key) {
62
+ return this.cache.has(key);
63
+ }
64
+ delete(key) {
65
+ return this.cache.delete(key);
66
+ }
67
+ clear() {
68
+ this.cache.clear();
69
+ }
70
+ get size() {
71
+ return this.cache.size;
72
+ }
73
+ };
74
+
75
+ // src/core/registry.ts
76
+ var BUILTIN_MODIFIERS = {
77
+ // ── Mode ─────────────────────────────────────────────────────────────────
78
+ dark: { order: 60, darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
79
+ "not-light": { order: 60, darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
80
+ light: { order: 60, darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
81
+ "not-dark": { order: 60, darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
82
+ // ── Interactive — JS state + CSS pseudo ───────────────────────────────────
83
+ hover: { order: 10, pseudo: ":hover", jsBehavior: "interactive", jsMatch: (_, s) => !!s.hover },
84
+ "not-hover": { order: 10, pseudo: ":not(:hover)", jsBehavior: "interactive", jsMatch: (_, s) => !s.hover },
85
+ focus: { order: 20, pseudo: ":focus", jsBehavior: "interactive", jsMatch: (_, s) => !!s.focus },
86
+ "not-focus": { order: 20, pseudo: ":not(:focus)", jsBehavior: "interactive", jsMatch: (_, s) => !s.focus },
87
+ active: { order: 30, pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.active || !!s.pressed },
88
+ "not-active": { order: 30, pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.active && !s.pressed },
89
+ pressed: { order: 30, pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.pressed || !!s.active },
90
+ "not-pressed": { order: 30, pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.pressed && !s.active },
91
+ disabled: { order: 40, pseudo: ":disabled", jsBehavior: "interactive", jsMatch: (_, s) => !!s.disabled },
92
+ "not-disabled": { order: 40, pseudo: ":not(:disabled)", jsBehavior: "interactive", jsMatch: (_, s) => !s.disabled },
93
+ checked: { order: 35, pseudo: ":checked", jsBehavior: "interactive", jsMatch: (_, s) => !!s.checked },
94
+ "not-checked": { order: 35, pseudo: ":not(:checked)", jsBehavior: "interactive", jsMatch: (_, s) => !s.checked },
95
+ visited: { order: 5, pseudo: ":visited", jsBehavior: "interactive", jsMatch: (_, s) => !!s.visited },
96
+ "not-visited": { order: 5, pseudo: ":not(:visited)", jsBehavior: "interactive", jsMatch: (_, s) => !s.visited },
97
+ placeholder: { order: 0, pseudo: "::placeholder", jsBehavior: "interactive", jsMatch: (_, s) => !!s.placeholder },
98
+ // ── CSS-only pseudo-classes (structural) ──────────────────────────────────
99
+ first: { order: 0, pseudo: ":first-child", jsBehavior: "css-only", forcesImportant: true },
100
+ last: { order: 0, pseudo: ":last-child", jsBehavior: "css-only", forcesImportant: true },
101
+ odd: { order: 0, pseudo: ":nth-child(odd)", jsBehavior: "css-only", forcesImportant: true },
102
+ even: { order: 0, pseudo: ":nth-child(even)", jsBehavior: "css-only", forcesImportant: true },
103
+ only: { order: 0, pseudo: ":only-child", jsBehavior: "css-only", forcesImportant: true },
104
+ "focus-within": { order: 15, pseudo: ":focus-within", jsBehavior: "css-only", forcesImportant: true },
105
+ "focus-visible": { order: 25, pseudo: ":focus-visible", jsBehavior: "css-only", forcesImportant: true },
106
+ // ── CSS-only pseudo-elements ───────────────────────────────────────────────
107
+ before: { order: 0, pseudo: "::before", jsBehavior: "css-only" },
108
+ after: { order: 0, pseudo: "::after", jsBehavior: "css-only" },
109
+ selection: { order: 0, pseudo: "::selection", jsBehavior: "css-only" },
110
+ "first-letter": { order: 0, pseudo: "::first-letter", jsBehavior: "css-only" },
111
+ "first-line": { order: 0, pseudo: "::first-line", jsBehavior: "css-only" },
112
+ marker: { order: 0, pseudo: "::marker", jsBehavior: "css-only" },
113
+ // ── Group / peer ancestor selectors (CSS-only) ─────────────────────────────
114
+ "group-hover": { order: 10, ancestorSelector: ".group:hover ", jsBehavior: "css-only", forcesImportant: true },
115
+ "group-focus": { order: 20, ancestorSelector: ".group:focus ", jsBehavior: "css-only", forcesImportant: true },
116
+ "peer-hover": { order: 10, ancestorSelector: ".peer:hover ~ ", jsBehavior: "css-only", forcesImportant: true },
117
+ "peer-focus": { order: 20, ancestorSelector: ".peer:focus ~ ", jsBehavior: "css-only", forcesImportant: true },
118
+ // ── Responsive — both CSS (@media min-width) and JS (breakpoints set) ──────
119
+ sm: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("sm") },
120
+ md: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("md") },
121
+ lg: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("lg") },
122
+ xl: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("xl") },
123
+ "2xl": { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("2xl") },
124
+ // ── Print media (CSS-only) ─────────────────────────────────────────────────
125
+ print: { order: 70, mediaQuery: "print", jsBehavior: "css-only", forcesImportant: true },
126
+ // ── Orientation / accessibility media (CSS-only) ───────────────────────────
127
+ landscape: { order: 70, mediaQuery: "(orientation: landscape)", jsBehavior: "css-only", forcesImportant: true },
128
+ portrait: { order: 70, mediaQuery: "(orientation: portrait)", jsBehavior: "css-only", forcesImportant: true },
129
+ "motion-reduce": { order: 70, mediaQuery: "(prefers-reduced-motion: reduce)", jsBehavior: "css-only", forcesImportant: true },
130
+ "motion-safe": { order: 70, mediaQuery: "(prefers-reduced-motion: no-preference)", jsBehavior: "css-only", forcesImportant: true },
131
+ "contrast-more": { order: 70, mediaQuery: "(prefers-contrast: more)", jsBehavior: "css-only", forcesImportant: true },
132
+ "contrast-less": { order: 70, mediaQuery: "(prefers-contrast: less)", jsBehavior: "css-only", forcesImportant: true },
133
+ // ── Directionality (CSS-only) ──────────────────────────────────────────────
134
+ rtl: { order: 80, dirSelector: '[dir="rtl"] ', jsBehavior: "css-only", forcesImportant: true },
135
+ ltr: { order: 80, dirSelector: '[dir="ltr"] ', jsBehavior: "css-only", forcesImportant: true }
136
+ };
137
+ var _pluginModifiers = {};
138
+ var _allNames = null;
139
+ var _interactiveNames = null;
140
+ var _modeNames = null;
141
+ var _responsiveNames = null;
142
+ function _invalidate() {
143
+ _allNames = null;
144
+ _interactiveNames = null;
145
+ _modeNames = null;
146
+ _responsiveNames = null;
147
+ }
148
+ function registerModifier(name, def) {
149
+ if (process.env.NODE_ENV !== "production" && name in BUILTIN_MODIFIERS) {
150
+ kbachWarn(`"${name}" is built-in \u2014 pick another modifier name`);
151
+ return;
152
+ }
153
+ _pluginModifiers[name] = def;
154
+ _invalidate();
155
+ }
156
+ function clearPluginModifiers() {
157
+ for (const k of Object.keys(_pluginModifiers)) delete _pluginModifiers[k];
158
+ _invalidate();
159
+ }
160
+ var NAMED_GROUP_PEER_RE = /^(group|peer)-(hover|focus)\/(.+)$/;
161
+ var _namedModifierCache = new LRUCache(1e4);
162
+ function getNamedGroupPeerModifier(name) {
163
+ const cached = _namedModifierCache.get(name);
164
+ if (cached) return cached;
165
+ const m = NAMED_GROUP_PEER_RE.exec(name);
166
+ if (!m) return void 0;
167
+ const [, kind, trigger, groupName] = m;
168
+ const escapedName = escapeCSSSelector(groupName);
169
+ const pseudo = trigger === "hover" ? ":hover" : ":focus";
170
+ const combinator = kind === "group" ? " " : " ~ ";
171
+ const def = {
172
+ // Matches the plain group-hover/group-focus/peer-hover/peer-focus order below.
173
+ order: trigger === "hover" ? 10 : 20,
174
+ ancestorSelector: `.${kind}\\/${escapedName}${pseudo}${combinator}`,
175
+ jsBehavior: "css-only",
176
+ forcesImportant: true
177
+ };
178
+ _namedModifierCache.set(name, def);
179
+ return def;
180
+ }
181
+ function getModifier(name) {
182
+ return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name] ?? getNamedGroupPeerModifier(name);
183
+ }
184
+ function isKnownModifier(name) {
185
+ return name in BUILTIN_MODIFIERS || name in _pluginModifiers || NAMED_GROUP_PEER_RE.test(name);
186
+ }
187
+ function _allEntries() {
188
+ return [...Object.entries(BUILTIN_MODIFIERS), ...Object.entries(_pluginModifiers)];
189
+ }
190
+ function getInteractiveModifiers() {
191
+ if (!_interactiveNames) {
192
+ _interactiveNames = new Set(_allEntries().filter(([, d]) => d.jsBehavior === "interactive").map(([k]) => k));
193
+ }
194
+ return _interactiveNames;
195
+ }
196
+ function getModeModifiers() {
197
+ if (!_modeNames) {
198
+ _modeNames = new Set(_allEntries().filter(([, d]) => d.jsBehavior === "mode").map(([k]) => k));
199
+ }
200
+ return _modeNames;
201
+ }
202
+ function getResponsiveModifiers() {
203
+ if (!_responsiveNames) {
204
+ _responsiveNames = new Set(_allEntries().filter(([, d]) => d.jsBehavior === "responsive").map(([k]) => k));
205
+ }
206
+ return _responsiveNames;
207
+ }
208
+ function getModifierOrder(bucketKey) {
209
+ if (bucketKey === "base") return -1;
210
+ let max = 0;
211
+ for (const mod of bucketKey.split(":")) {
212
+ const order = getModifier(mod)?.order ?? 0;
213
+ if (order > max) max = order;
214
+ }
215
+ return max;
216
+ }
217
+ function matchModifier(name, isDark, state, breakpoints) {
218
+ return getModifier(name)?.jsMatch?.(isDark, state, breakpoints) ?? false;
219
+ }
220
+
221
+ // src/core/colorValue.ts
222
+ function isModeAwareColor(v) {
223
+ return typeof v === "object" && v !== null && "light" in v && "dark" in v;
224
+ }
225
+ function splitColorShadeRef(ref) {
226
+ const lastDash = ref.lastIndexOf("-");
227
+ if (lastDash <= 0) return null;
228
+ return { name: ref.slice(0, lastDash), shade: ref.slice(lastDash + 1) };
229
+ }
230
+
231
+ // src/core/resolvers/color.ts
232
+ function pickModeAwareFallback(v) {
233
+ return isModeAwareColor(v) ? v.light : v;
234
+ }
235
+ function resolveColor(value, colors, isArbitrary) {
236
+ if (isArbitrary) return value;
237
+ const slashIdx = value.indexOf("/");
238
+ const colorPart = slashIdx > 0 ? value.slice(0, slashIdx) : value;
239
+ const opacityPart = slashIdx > 0 ? value.slice(slashIdx + 1) : null;
240
+ let hex = null;
241
+ if (colorPart.startsWith("[") && colorPart.endsWith("]")) {
242
+ hex = colorPart.slice(1, -1);
243
+ } else if (colorPart in colors) {
244
+ const entry = colors[colorPart];
245
+ if (typeof entry === "string") hex = entry;
246
+ else if (isModeAwareColor(entry)) hex = pickModeAwareFallback(entry);
247
+ else if (typeof entry === "object" && "6" in entry) hex = pickModeAwareFallback(entry["6"]);
248
+ } else {
249
+ const split = splitColorShadeRef(colorPart);
250
+ if (split) {
251
+ const scale = colors[split.name];
252
+ if (scale && typeof scale === "object" && !isModeAwareColor(scale) && split.shade in scale) {
253
+ const shadeVal = scale[split.shade];
254
+ hex = shadeVal !== void 0 ? pickModeAwareFallback(shadeVal) : null;
255
+ }
256
+ }
257
+ }
258
+ if (!hex) return null;
259
+ if (!opacityPart) {
260
+ if (!getEffectiveIsWeb() && hex.startsWith("#") && hex.length === 9) {
261
+ return hexToRgba(hex, Math.round(parseInt(hex.slice(7, 9), 16) / 255 * 1e3) / 1e3);
262
+ }
263
+ return hex;
264
+ }
265
+ let alpha;
266
+ if (opacityPart.startsWith("[") && opacityPart.endsWith("]")) {
267
+ const v = parseFloat(opacityPart.slice(1, -1));
268
+ alpha = v > 1 ? v / 100 : v;
269
+ } else {
270
+ alpha = parseFloat(opacityPart) / 100;
271
+ }
272
+ if (isNaN(alpha)) return hex;
273
+ if (alpha < 0) alpha = 0;
274
+ else if (alpha > 1) alpha = 1;
275
+ return hexToRgba(hex, alpha);
276
+ }
277
+ function parseHexRgb(hex) {
278
+ const h = hex.replace("#", "");
279
+ if (h.length === 3 || h.length === 4) {
280
+ return [
281
+ parseInt(h[0] + h[0], 16),
282
+ parseInt(h[1] + h[1], 16),
283
+ parseInt(h[2] + h[2], 16)
284
+ ];
285
+ }
286
+ if (h.length === 6 || h.length === 8) {
287
+ return [
288
+ parseInt(h.slice(0, 2), 16),
289
+ parseInt(h.slice(2, 4), 16),
290
+ parseInt(h.slice(4, 6), 16)
291
+ ];
292
+ }
293
+ return null;
294
+ }
295
+ function hexToRgba(hex, alpha) {
296
+ const rgb = parseHexRgb(hex);
297
+ if (!rgb) return hex;
298
+ const [r, g, b] = rgb;
299
+ return `rgba(${r},${g},${b},${alpha})`;
300
+ }
301
+ function withOpacityVar(color, varName) {
302
+ if (!color.startsWith("#")) return color;
303
+ const rgb = parseHexRgb(color);
304
+ if (!rgb) return color;
305
+ const [r, g, b] = rgb;
306
+ return `rgba(${r},${g},${b},var(${varName},1))`;
307
+ }
308
+ var colorResolvers = {
309
+ // ── Background ─────────────────────────────────────────────────────────────
310
+ bg: ({ value, isArbitrary }, { colors }) => {
311
+ const color = resolveColor(value, colors, isArbitrary);
312
+ if (!color) return null;
313
+ return { backgroundColor: getEffectiveIsWeb() ? withOpacityVar(color, "--bg-opacity") : color };
314
+ },
315
+ "bg-opacity": ({ value, isArbitrary }, _) => {
316
+ if (!getEffectiveIsWeb()) return null;
317
+ const n = parseFloat(value);
318
+ if (isNaN(n)) return null;
319
+ const v = isArbitrary ? n > 1 ? n / 100 : n : n / 100;
320
+ return { "--bg-opacity": v };
321
+ },
322
+ // ── Background gradient (CSS variable gradient stops, web-only) ──────────
323
+ "bg-gradient-to": ({ value }) => {
324
+ if (!getEffectiveIsWeb()) return null;
325
+ const directions = {
326
+ t: "to top",
327
+ tr: "to top right",
328
+ r: "to right",
329
+ br: "to bottom right",
330
+ b: "to bottom",
331
+ bl: "to bottom left",
332
+ l: "to left",
333
+ tl: "to top left"
334
+ };
335
+ const dir = directions[value];
336
+ if (!dir) return null;
337
+ return {
338
+ backgroundImage: `linear-gradient(${dir}, var(--kb-gradient-from, transparent), var(--kb-gradient-stops, transparent))`
339
+ };
340
+ },
341
+ from: ({ value, isArbitrary }, { colors }) => {
342
+ if (!getEffectiveIsWeb()) return null;
343
+ const color = resolveColor(value, colors, isArbitrary);
344
+ if (!color) return null;
345
+ return {
346
+ "--kb-gradient-from": color,
347
+ "--kb-gradient-stops": `var(--kb-gradient-from), var(--kb-gradient-to, transparent)`
348
+ };
349
+ },
350
+ via: ({ value, isArbitrary }, { colors }) => {
351
+ if (!getEffectiveIsWeb()) return null;
352
+ const color = resolveColor(value, colors, isArbitrary);
353
+ if (!color) return null;
354
+ return {
355
+ "--kb-gradient-via": color,
356
+ "--kb-gradient-stops": `var(--kb-gradient-from), var(--kb-gradient-via), var(--kb-gradient-to, transparent)`
357
+ };
358
+ },
359
+ to: ({ value, isArbitrary }, { colors }) => {
360
+ if (!getEffectiveIsWeb()) return null;
361
+ const color = resolveColor(value, colors, isArbitrary);
362
+ if (!color) return null;
363
+ return { "--kb-gradient-to": color };
364
+ },
365
+ // ── Tint color (native-only, for Image and icon components) ──────────────
366
+ tint: ({ value, isArbitrary }, { colors }) => {
367
+ if (getEffectiveIsWeb()) return null;
368
+ const color = resolveColor(value, colors, isArbitrary);
369
+ return color ? { tintColor: color } : null;
370
+ },
371
+ // ── Caret color (web-only) ────────────────────────────────────────────────
372
+ caret: ({ value, isArbitrary }, { colors }) => {
373
+ if (!getEffectiveIsWeb()) return null;
374
+ if (value === "auto" || value === "transparent") return { caretColor: value };
375
+ const color = resolveColor(value, colors, isArbitrary);
376
+ return color ? { caretColor: color } : null;
377
+ },
378
+ // ── Accent color (web-only) ───────────────────────────────────────────────
379
+ accent: ({ value, isArbitrary }, { colors }) => {
380
+ if (!getEffectiveIsWeb()) return null;
381
+ if (value === "auto") return { accentColor: "auto" };
382
+ const color = resolveColor(value, colors, isArbitrary);
383
+ return color ? { accentColor: color } : null;
384
+ }
385
+ };
386
+
387
+ // src/core/resolvers/spacing.ts
388
+ function resolveSpacing(value, negative, spacing, isArbitrary) {
389
+ if (isArbitrary) {
390
+ const resolved = getEffectiveIsWeb() ? value : toNativeValue(value);
391
+ if (negative) {
392
+ if (typeof resolved === "number") return -resolved;
393
+ if (typeof resolved === "string") {
394
+ if (resolved.startsWith("-")) return resolved.slice(1);
395
+ if (/^\d/.test(resolved)) return `-${resolved}`;
396
+ if (/^(calc|var|min|max|clamp|env)\s*\(/.test(resolved)) return `calc(-1 * (${resolved}))`;
397
+ }
398
+ }
399
+ return resolved;
400
+ }
401
+ const raw = spacing[value];
402
+ if (raw === void 0) return null;
403
+ if (typeof raw === "number") return negative ? -raw : raw;
404
+ if (raw === "auto") return "auto";
405
+ if (negative && typeof raw === "string" && raw.endsWith("%")) {
406
+ return `-${raw}`;
407
+ }
408
+ return raw;
409
+ }
410
+ function resolveSizing(value, spacing, isArbitrary) {
411
+ if (isArbitrary) return getEffectiveIsWeb() ? value : toNativeValue(value);
412
+ const raw = spacing[value];
413
+ if (raw !== void 0) return raw;
414
+ if (/^\d+\/\d+$/.test(value)) {
415
+ const [num, den] = value.split("/").map(Number);
416
+ if (!den) return null;
417
+ return `${(num / den * 100).toFixed(6)}%`;
418
+ }
419
+ return null;
420
+ }
421
+ function makeSpacingResolver(prop) {
422
+ return ({ value, negative, isArbitrary }, { spacing }) => {
423
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
424
+ return v !== null ? { [prop]: v } : null;
425
+ };
426
+ }
427
+ var PADDING_MARGIN_PROPS = {
428
+ p: "padding",
429
+ px: "paddingHorizontal",
430
+ py: "paddingVertical",
431
+ pt: "paddingTop",
432
+ pr: "paddingRight",
433
+ pb: "paddingBottom",
434
+ pl: "paddingLeft",
435
+ m: "margin",
436
+ mx: "marginHorizontal",
437
+ my: "marginVertical",
438
+ mt: "marginTop",
439
+ mr: "marginRight",
440
+ mb: "marginBottom",
441
+ ml: "marginLeft"
442
+ };
443
+ var spacingResolvers = {
444
+ // ── Sizing ─────────────────────────────────────────────────────────────────
445
+ w: ({ value, isArbitrary }, { spacing }) => {
446
+ const v = resolveSizing(value, spacing, isArbitrary);
447
+ return v !== null ? { width: v } : null;
448
+ },
449
+ h: ({ value, isArbitrary }, { spacing }) => {
450
+ const v = resolveSizing(value, spacing, isArbitrary);
451
+ return v !== null ? { height: v } : null;
452
+ },
453
+ "min-w": ({ value, isArbitrary }, { spacing }) => {
454
+ const v = resolveSizing(value, spacing, isArbitrary);
455
+ return v !== null ? { minWidth: v } : null;
456
+ },
457
+ "min-h": ({ value, isArbitrary }, { spacing }) => {
458
+ const v = resolveSizing(value, spacing, isArbitrary);
459
+ return v !== null ? { minHeight: v } : null;
460
+ },
461
+ "max-w": ({ value, isArbitrary }, { spacing }) => {
462
+ const v = resolveSizing(value, spacing, isArbitrary);
463
+ return v !== null ? { maxWidth: v } : null;
464
+ },
465
+ "max-h": ({ value, isArbitrary }, { spacing }) => {
466
+ const v = resolveSizing(value, spacing, isArbitrary);
467
+ return v !== null ? { maxHeight: v } : null;
468
+ },
469
+ // ── Size shorthand (sets width AND height in one utility) ────────────────
470
+ size: ({ value, isArbitrary }, { spacing }) => {
471
+ const v = resolveSizing(value, spacing, isArbitrary);
472
+ return v !== null ? { width: v, height: v } : null;
473
+ },
474
+ // ── Flex basis ─────────────────────────────────────────────────────────────
475
+ basis: ({ value, isArbitrary }, { spacing }) => {
476
+ const v = resolveSizing(value, spacing, isArbitrary);
477
+ return v !== null ? { flexBasis: v } : null;
478
+ },
479
+ // ── Gap ────────────────────────────────────────────────────────────────────
480
+ gap: ({ value, negative, isArbitrary }, { spacing }) => {
481
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
482
+ return v !== null ? { gap: v } : null;
483
+ },
484
+ "gap-x": ({ value, negative, isArbitrary }, { spacing }) => {
485
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
486
+ return v !== null ? { columnGap: v } : null;
487
+ },
488
+ "gap-y": ({ value, negative, isArbitrary }, { spacing }) => {
489
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
490
+ return v !== null ? { rowGap: v } : null;
491
+ },
492
+ // ── Position ───────────────────────────────────────────────────────────────
493
+ top: ({ value, negative, isArbitrary }, { spacing }) => {
494
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
495
+ return v !== null ? { top: v } : null;
496
+ },
497
+ right: ({ value, negative, isArbitrary }, { spacing }) => {
498
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
499
+ return v !== null ? { right: v } : null;
500
+ },
501
+ bottom: ({ value, negative, isArbitrary }, { spacing }) => {
502
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
503
+ return v !== null ? { bottom: v } : null;
504
+ },
505
+ left: ({ value, negative, isArbitrary }, { spacing }) => {
506
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
507
+ return v !== null ? { left: v } : null;
508
+ },
509
+ inset: ({ value, negative, isArbitrary }, { spacing }) => {
510
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
511
+ return v !== null ? { top: v, right: v, bottom: v, left: v } : null;
512
+ },
513
+ "inset-x": ({ value, negative, isArbitrary }, { spacing }) => {
514
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
515
+ return v !== null ? { left: v, right: v } : null;
516
+ },
517
+ "inset-y": ({ value, negative, isArbitrary }, { spacing }) => {
518
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
519
+ return v !== null ? { top: v, bottom: v } : null;
520
+ },
521
+ // ── Translate ──────────────────────────────────────────────────────────────
522
+ "translate-x": ({ value, negative, isArbitrary }, { spacing }) => {
523
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
524
+ if (v === null) return null;
525
+ if (getEffectiveIsWeb()) return { transform: `translateX(${typeof v === "number" ? `${v}px` : v})` };
526
+ if (typeof v === "string") {
527
+ const n = parseFloat(v);
528
+ return isNaN(n) ? null : { transform: [{ translateX: n }] };
529
+ }
530
+ return { transform: [{ translateX: v }] };
531
+ },
532
+ "translate-y": ({ value, negative, isArbitrary }, { spacing }) => {
533
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
534
+ if (v === null) return null;
535
+ if (getEffectiveIsWeb()) return { transform: `translateY(${typeof v === "number" ? `${v}px` : v})` };
536
+ if (typeof v === "string") {
537
+ const n = parseFloat(v);
538
+ return isNaN(n) ? null : { transform: [{ translateY: n }] };
539
+ }
540
+ return { transform: [{ translateY: v }] };
541
+ },
542
+ // ── Space between ─────────────────────────────────────────────────────────
543
+ // On web: emits __spaceX/__spaceY markers → resolver generates > * + * CSS rules.
544
+ // On native (RN 0.71+): uses columnGap/rowGap directly.
545
+ "space-x": ({ value, negative, isArbitrary }, { spacing }) => {
546
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
547
+ if (v === null) return null;
548
+ if (getEffectiveIsWeb()) return { __spaceX: v };
549
+ return { columnGap: v };
550
+ },
551
+ "space-y": ({ value, negative, isArbitrary }, { spacing }) => {
552
+ const v = resolveSpacing(value, negative, spacing, isArbitrary);
553
+ if (v === null) return null;
554
+ if (getEffectiveIsWeb()) return { __spaceY: v };
555
+ return { rowGap: v };
556
+ }
557
+ };
558
+ for (const [utility, prop] of Object.entries(PADDING_MARGIN_PROPS)) {
559
+ spacingResolvers[utility] = makeSpacingResolver(prop);
560
+ }
561
+
562
+ // src/core/resolvers/border.ts
563
+ function resolveRadius(value, radii, isArbitrary) {
564
+ if (isArbitrary) return getEffectiveIsWeb() ? value : toNativeValue(value);
565
+ const key = value === "" ? "DEFAULT" : value;
566
+ return radii[key] ?? null;
567
+ }
568
+ function makeBorderSideResolver(widthProp, colorProp) {
569
+ return ({ value, isArbitrary }, { colors, borderWidth }) => {
570
+ if (!value) return { [widthProp]: 1 };
571
+ if (isArbitrary) {
572
+ const w2 = toNativeValue(value);
573
+ if (typeof w2 === "number") return { [widthProp]: getEffectiveIsWeb() ? value : w2 };
574
+ return { [colorProp]: value };
575
+ }
576
+ const color = resolveColor(value, colors, false);
577
+ if (color) return { [colorProp]: color };
578
+ const w = borderWidth[value];
579
+ return w !== void 0 ? { [widthProp]: w } : null;
580
+ };
581
+ }
582
+ var BORDER_SIDE_PROPS = {
583
+ "border-t": ["borderTopWidth", "borderTopColor"],
584
+ "border-r": ["borderRightWidth", "borderRightColor"],
585
+ "border-b": ["borderBottomWidth", "borderBottomColor"],
586
+ "border-l": ["borderLeftWidth", "borderLeftColor"]
587
+ };
588
+ var borderResolvers = {
589
+ // ── Border width ───────────────────────────────────────────────────────────
590
+ border: ({ value, isArbitrary }, { colors, borderWidth, spacing }) => {
591
+ if (!value) return { borderWidth: borderWidth["DEFAULT"] ?? 1 };
592
+ if (isArbitrary) {
593
+ const w2 = toNativeValue(value);
594
+ if (typeof w2 === "number") return { borderWidth: getEffectiveIsWeb() ? value : w2 };
595
+ return { borderColor: value };
596
+ }
597
+ const color = resolveColor(value, colors, false);
598
+ if (color) return { borderColor: color };
599
+ const w = borderWidth[value] ?? spacing[value];
600
+ if (w !== void 0) {
601
+ const numW = typeof w === "number" ? w : parseFloat(String(w));
602
+ if (isNaN(numW)) return null;
603
+ return { borderWidth: numW };
604
+ }
605
+ return null;
606
+ },
607
+ // border-t/-r/-b/-l generated below via makeBorderSideResolver() — see BORDER_SIDE_PROPS.
608
+ // ── Border radius ──────────────────────────────────────────────────────────
609
+ rounded: ({ value, isArbitrary }, { borderRadius }) => {
610
+ const r = resolveRadius(value, borderRadius, isArbitrary);
611
+ return r !== null ? { borderRadius: r } : null;
612
+ },
613
+ "rounded-t": ({ value, isArbitrary }, { borderRadius }) => {
614
+ const r = resolveRadius(value, borderRadius, isArbitrary);
615
+ return r !== null ? { borderTopLeftRadius: r, borderTopRightRadius: r } : null;
616
+ },
617
+ "rounded-r": ({ value, isArbitrary }, { borderRadius }) => {
618
+ const r = resolveRadius(value, borderRadius, isArbitrary);
619
+ return r !== null ? { borderTopRightRadius: r, borderBottomRightRadius: r } : null;
620
+ },
621
+ "rounded-b": ({ value, isArbitrary }, { borderRadius }) => {
622
+ const r = resolveRadius(value, borderRadius, isArbitrary);
623
+ return r !== null ? { borderBottomLeftRadius: r, borderBottomRightRadius: r } : null;
624
+ },
625
+ "rounded-l": ({ value, isArbitrary }, { borderRadius }) => {
626
+ const r = resolveRadius(value, borderRadius, isArbitrary);
627
+ return r !== null ? { borderTopLeftRadius: r, borderBottomLeftRadius: r } : null;
628
+ },
629
+ "rounded-tl": ({ value, isArbitrary }, { borderRadius }) => {
630
+ const r = resolveRadius(value, borderRadius, isArbitrary);
631
+ return r !== null ? { borderTopLeftRadius: r } : null;
632
+ },
633
+ "rounded-tr": ({ value, isArbitrary }, { borderRadius }) => {
634
+ const r = resolveRadius(value, borderRadius, isArbitrary);
635
+ return r !== null ? { borderTopRightRadius: r } : null;
636
+ },
637
+ "rounded-bl": ({ value, isArbitrary }, { borderRadius }) => {
638
+ const r = resolveRadius(value, borderRadius, isArbitrary);
639
+ return r !== null ? { borderBottomLeftRadius: r } : null;
640
+ },
641
+ "rounded-br": ({ value, isArbitrary }, { borderRadius }) => {
642
+ const r = resolveRadius(value, borderRadius, isArbitrary);
643
+ return r !== null ? { borderBottomRightRadius: r } : null;
644
+ },
645
+ // ── Outline extended (web-only) ───────────────────────────────────────────
646
+ outline: ({ value, isArbitrary }, { colors }) => {
647
+ if (!getEffectiveIsWeb()) return null;
648
+ if (!value) return { outline: "2px solid transparent", outlineOffset: "2px" };
649
+ if (value === "none") return { outline: "none", outlineOffset: "0" };
650
+ if (isArbitrary) {
651
+ if (/^\d/.test(value) || value.startsWith("calc(")) return { outlineWidth: value };
652
+ return { outlineColor: value };
653
+ }
654
+ const widths = { "0": "0px", "1": "1px", "2": "2px", "4": "4px", "8": "8px" };
655
+ if (value in widths) return { outlineWidth: widths[value] };
656
+ const color = resolveColor(value, colors, false);
657
+ return color ? { outlineColor: color } : null;
658
+ },
659
+ "outline-offset": ({ value, isArbitrary }) => {
660
+ if (!getEffectiveIsWeb()) return null;
661
+ if (isArbitrary) return { outlineOffset: value };
662
+ const offsets = { "0": "0px", "1": "1px", "2": "2px", "4": "4px", "8": "8px" };
663
+ return offsets[value] ? { outlineOffset: offsets[value] } : null;
664
+ },
665
+ // ── Divide (child combinator CSS, web-only) ───────────────────────────────
666
+ // These return special __divide* markers that resolver.ts converts to
667
+ // .cls > * + * { border-... } CSS rules. No inline style is applied.
668
+ "divide-x": ({ value, isArbitrary }) => {
669
+ if (!getEffectiveIsWeb()) return null;
670
+ if (value === "reverse") return null;
671
+ if (!value) return { __divideX: 1 };
672
+ const n = parseFloat(value);
673
+ return isNaN(n) ? null : { __divideX: n };
674
+ },
675
+ "divide-y": ({ value, isArbitrary }) => {
676
+ if (!getEffectiveIsWeb()) return null;
677
+ if (value === "reverse") return null;
678
+ if (!value) return { __divideY: 1 };
679
+ const n = parseFloat(value);
680
+ return isNaN(n) ? null : { __divideY: n };
681
+ },
682
+ divide: ({ value, isArbitrary }, { colors }) => {
683
+ if (!getEffectiveIsWeb()) return null;
684
+ if (isArbitrary) return { __divideColor: value };
685
+ const styleTokens = {
686
+ solid: "solid",
687
+ dashed: "dashed",
688
+ dotted: "dotted",
689
+ double: "double",
690
+ none: "none"
691
+ };
692
+ if (value in styleTokens) return { __divideStyle: styleTokens[value] };
693
+ const color = resolveColor(value, colors, false);
694
+ return color ? { __divideColor: color } : null;
695
+ },
696
+ // ── Ring ───────────────────────────────────────────────────────────────────
697
+ // Web: box-shadow outline ring (doesn't affect layout).
698
+ // Native: React Native has no box-shadow, so this falls back to borderWidth/
699
+ // borderColor — the closest visual approximation (used by other RN Tailwind-
700
+ // likes for the same reason). Unlike the web ring, this DOES affect layout,
701
+ // and it shares its properties with the `border` utility — combining
702
+ // `border-*` and `ring-*` on the same native element means whichever class
703
+ // comes later wins, since both ultimately set borderWidth/borderColor.
704
+ ring: ({ value, isArbitrary }, { colors }) => {
705
+ const onWeb = getEffectiveIsWeb();
706
+ const DEFAULT_COLOR = "rgba(59, 130, 246, 0.5)";
707
+ const DEFAULT_WIDTH = 3;
708
+ const asNative = (w, color2) => w === 0 ? { borderWidth: 0 } : { borderWidth: w, borderColor: color2 };
709
+ if (!value) {
710
+ return onWeb ? { boxShadow: `0 0 0 ${DEFAULT_WIDTH}px ${DEFAULT_COLOR}` } : asNative(DEFAULT_WIDTH, DEFAULT_COLOR);
711
+ }
712
+ if (value === "inset") {
713
+ return onWeb ? { boxShadow: `inset 0 0 0 ${DEFAULT_WIDTH}px ${DEFAULT_COLOR}` } : asNative(DEFAULT_WIDTH, DEFAULT_COLOR);
714
+ }
715
+ const widthTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
716
+ if (!isArbitrary && value in widthTokens) {
717
+ const w = widthTokens[value];
718
+ return onWeb ? { boxShadow: w === 0 ? "none" : `0 0 0 ${w}px ${DEFAULT_COLOR}` } : asNative(w, DEFAULT_COLOR);
719
+ }
720
+ if (isArbitrary) {
721
+ const numMatch = /^(\d+(?:\.\d+)?)(px|rem|em|vw|vh)?$/.exec(value);
722
+ if (numMatch) {
723
+ const unit = numMatch[2] ?? "px";
724
+ if (onWeb) return { boxShadow: `0 0 0 ${numMatch[1]}${unit} ${DEFAULT_COLOR}` };
725
+ return unit === "px" ? asNative(parseFloat(numMatch[1]), DEFAULT_COLOR) : null;
726
+ }
727
+ return onWeb ? { boxShadow: value.replace(/_/g, " ") } : null;
728
+ }
729
+ const color = resolveColor(value, colors, false);
730
+ if (color) {
731
+ return onWeb ? { boxShadow: `0 0 0 ${DEFAULT_WIDTH}px ${color}` } : asNative(DEFAULT_WIDTH, color);
732
+ }
733
+ return null;
734
+ },
735
+ // Web-only, unlike `ring` above: this stacks a second box-shadow layer to
736
+ // create a gap between the element and the ring. There's no native
737
+ // equivalent to approximate that with (a border can't create a gap outside
738
+ // its own element without an extra wrapper view), so this stays a no-op
739
+ // on native rather than rendering a misleading half-translation.
740
+ "ring-offset": ({ value, isArbitrary }, _theme) => {
741
+ if (!getEffectiveIsWeb()) return null;
742
+ const DEFAULT_RING_COLOR = "rgba(59, 130, 246, 0.5)";
743
+ const DEFAULT_RING_WIDTH = 3;
744
+ const offsetTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
745
+ let offsetWidth;
746
+ if (!isArbitrary) {
747
+ offsetWidth = value in offsetTokens ? offsetTokens[value] : null;
748
+ } else {
749
+ offsetWidth = /^-?\d+(\.\d+)?$/.test(value) ? parseFloat(value) : value;
750
+ }
751
+ if (offsetWidth === null || typeof offsetWidth === "number" && isNaN(offsetWidth)) return null;
752
+ const isZero = offsetWidth === 0 || offsetWidth === "0";
753
+ const offsetCss = typeof offsetWidth === "number" ? `${offsetWidth}px` : offsetWidth;
754
+ return {
755
+ boxShadow: isZero ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetCss} #fff, 0 0 0 calc(${offsetCss} + ${DEFAULT_RING_WIDTH}px) ${DEFAULT_RING_COLOR}`
756
+ };
757
+ }
758
+ };
759
+ for (const [utility, [widthProp, colorProp]] of Object.entries(BORDER_SIDE_PROPS)) {
760
+ borderResolvers[utility] = makeBorderSideResolver(widthProp, colorProp);
761
+ }
762
+
763
+ // src/core/resolvers/filters.ts
764
+ var FILTER_COMPOSE = "var(--kb-blur,) var(--kb-brightness,) var(--kb-contrast,) var(--kb-grayscale,) var(--kb-hue-rotate,) var(--kb-invert,) var(--kb-saturate,) var(--kb-sepia,) var(--kb-drop-shadow,)";
765
+ var BACKDROP_FILTER_COMPOSE = "var(--kb-backdrop-blur,) var(--kb-backdrop-brightness,) var(--kb-backdrop-contrast,) var(--kb-backdrop-grayscale,) var(--kb-backdrop-hue-rotate,) var(--kb-backdrop-invert,) var(--kb-backdrop-opacity,) var(--kb-backdrop-saturate,) var(--kb-backdrop-sepia,)";
766
+ var filterResolvers = {
767
+ // ── CSS Filters (composable via CSS variables, web-only) ─────────────────
768
+ blur: ({ value, isArbitrary }) => {
769
+ if (!getEffectiveIsWeb()) return null;
770
+ const sizes = {
771
+ "": "blur(8px)",
772
+ sm: "blur(4px)",
773
+ md: "blur(12px)",
774
+ lg: "blur(16px)",
775
+ xl: "blur(24px)",
776
+ "2xl": "blur(40px)",
777
+ "3xl": "blur(64px)"
778
+ };
779
+ if (value === "none") return { "--kb-blur": "", filter: FILTER_COMPOSE };
780
+ const v = isArbitrary ? `blur(${value})` : sizes[value];
781
+ return v !== void 0 ? { "--kb-blur": v, filter: FILTER_COMPOSE } : null;
782
+ },
783
+ brightness: ({ value, isArbitrary }) => {
784
+ if (!getEffectiveIsWeb()) return null;
785
+ const v = isArbitrary ? `brightness(${value})` : `brightness(${parseFloat(value) / 100})`;
786
+ return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-brightness": v, filter: FILTER_COMPOSE };
787
+ },
788
+ contrast: ({ value, isArbitrary }) => {
789
+ if (!getEffectiveIsWeb()) return null;
790
+ const v = isArbitrary ? `contrast(${value})` : `contrast(${parseFloat(value) / 100})`;
791
+ return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-contrast": v, filter: FILTER_COMPOSE };
792
+ },
793
+ grayscale: ({ value, isArbitrary }) => {
794
+ if (!getEffectiveIsWeb()) return null;
795
+ const v = isArbitrary ? `grayscale(${value})` : value === "0" ? "grayscale(0)" : "grayscale(100%)";
796
+ return { "--kb-grayscale": v, filter: FILTER_COMPOSE };
797
+ },
798
+ "hue-rotate": ({ value, negative, isArbitrary }) => {
799
+ if (!getEffectiveIsWeb()) return null;
800
+ const deg = isArbitrary ? value : `${(negative ? -1 : 1) * parseFloat(value)}deg`;
801
+ if (isNaN(parseFloat(deg)) && !isArbitrary) return null;
802
+ return { "--kb-hue-rotate": `hue-rotate(${deg})`, filter: FILTER_COMPOSE };
803
+ },
804
+ invert: ({ value, isArbitrary }) => {
805
+ if (!getEffectiveIsWeb()) return null;
806
+ const v = isArbitrary ? `invert(${value})` : value === "0" ? "invert(0)" : "invert(100%)";
807
+ return { "--kb-invert": v, filter: FILTER_COMPOSE };
808
+ },
809
+ saturate: ({ value, isArbitrary }) => {
810
+ if (!getEffectiveIsWeb()) return null;
811
+ const v = isArbitrary ? `saturate(${value})` : `saturate(${parseFloat(value) / 100})`;
812
+ return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-saturate": v, filter: FILTER_COMPOSE };
813
+ },
814
+ sepia: ({ value, isArbitrary }) => {
815
+ if (!getEffectiveIsWeb()) return null;
816
+ const v = isArbitrary ? `sepia(${value})` : value === "0" ? "sepia(0)" : "sepia(100%)";
817
+ return { "--kb-sepia": v, filter: FILTER_COMPOSE };
818
+ },
819
+ "drop-shadow": ({ value, isArbitrary }) => {
820
+ if (!getEffectiveIsWeb()) return null;
821
+ if (isArbitrary) return { "--kb-drop-shadow": `drop-shadow(${value.replace(/_/g, " ")})`, filter: FILTER_COMPOSE };
822
+ const presets = {
823
+ "": "drop-shadow(0 1px 2px rgb(0 0 0/0.1)) drop-shadow(0 1px 1px rgb(0 0 0/0.06))",
824
+ sm: "drop-shadow(0 1px 1px rgb(0 0 0/0.05))",
825
+ md: "drop-shadow(0 4px 3px rgb(0 0 0/0.07)) drop-shadow(0 2px 2px rgb(0 0 0/0.06))",
826
+ lg: "drop-shadow(0 10px 8px rgb(0 0 0/0.04)) drop-shadow(0 4px 3px rgb(0 0 0/0.1))",
827
+ xl: "drop-shadow(0 20px 13px rgb(0 0 0/0.03)) drop-shadow(0 8px 5px rgb(0 0 0/0.08))",
828
+ "2xl": "drop-shadow(0 25px 25px rgb(0 0 0/0.15))",
829
+ none: "drop-shadow(0 0 #0000)"
830
+ };
831
+ const v = presets[value];
832
+ return v !== void 0 ? { "--kb-drop-shadow": v, filter: FILTER_COMPOSE } : null;
833
+ },
834
+ // Arbitrary full filter string: filter-[blur(4px)_grayscale(1)]
835
+ filter: ({ value, isArbitrary }) => {
836
+ if (!getEffectiveIsWeb()) return null;
837
+ if (isArbitrary) return { filter: value.replace(/_/g, " ") };
838
+ if (value === "none") return { filter: "none" };
839
+ return null;
840
+ },
841
+ // ── Backdrop Filters (composable via CSS variables, web-only) ────────────
842
+ "backdrop-blur": ({ value, isArbitrary }) => {
843
+ if (!getEffectiveIsWeb()) return null;
844
+ const sizes = {
845
+ "": "blur(8px)",
846
+ sm: "blur(4px)",
847
+ md: "blur(12px)",
848
+ lg: "blur(16px)",
849
+ xl: "blur(24px)",
850
+ "2xl": "blur(40px)",
851
+ "3xl": "blur(64px)"
852
+ };
853
+ if (value === "none") return { "--kb-backdrop-blur": "", backdropFilter: BACKDROP_FILTER_COMPOSE };
854
+ const v = isArbitrary ? `blur(${value})` : sizes[value];
855
+ return v !== void 0 ? { "--kb-backdrop-blur": v, backdropFilter: BACKDROP_FILTER_COMPOSE } : null;
856
+ },
857
+ "backdrop-brightness": ({ value, isArbitrary }) => {
858
+ if (!getEffectiveIsWeb()) return null;
859
+ const v = isArbitrary ? `brightness(${value})` : `brightness(${parseFloat(value) / 100})`;
860
+ return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-backdrop-brightness": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
861
+ },
862
+ "backdrop-contrast": ({ value, isArbitrary }) => {
863
+ if (!getEffectiveIsWeb()) return null;
864
+ const v = isArbitrary ? `contrast(${value})` : `contrast(${parseFloat(value) / 100})`;
865
+ return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-backdrop-contrast": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
866
+ },
867
+ "backdrop-grayscale": ({ value, isArbitrary }) => {
868
+ if (!getEffectiveIsWeb()) return null;
869
+ const v = isArbitrary ? `grayscale(${value})` : value === "0" ? "grayscale(0)" : "grayscale(100%)";
870
+ return { "--kb-backdrop-grayscale": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
871
+ },
872
+ "backdrop-hue-rotate": ({ value, negative, isArbitrary }) => {
873
+ if (!getEffectiveIsWeb()) return null;
874
+ const deg = isArbitrary ? value : `${(negative ? -1 : 1) * parseFloat(value)}deg`;
875
+ if (isNaN(parseFloat(deg)) && !isArbitrary) return null;
876
+ return { "--kb-backdrop-hue-rotate": `hue-rotate(${deg})`, backdropFilter: BACKDROP_FILTER_COMPOSE };
877
+ },
878
+ "backdrop-invert": ({ value, isArbitrary }) => {
879
+ if (!getEffectiveIsWeb()) return null;
880
+ const v = isArbitrary ? `invert(${value})` : value === "0" ? "invert(0)" : "invert(100%)";
881
+ return { "--kb-backdrop-invert": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
882
+ },
883
+ "backdrop-opacity": ({ value, isArbitrary }) => {
884
+ if (!getEffectiveIsWeb()) return null;
885
+ const v = isArbitrary ? `opacity(${value})` : `opacity(${parseFloat(value) / 100})`;
886
+ return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-backdrop-opacity": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
887
+ },
888
+ "backdrop-saturate": ({ value, isArbitrary }) => {
889
+ if (!getEffectiveIsWeb()) return null;
890
+ const v = isArbitrary ? `saturate(${value})` : `saturate(${parseFloat(value) / 100})`;
891
+ return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-backdrop-saturate": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
892
+ },
893
+ "backdrop-sepia": ({ value, isArbitrary }) => {
894
+ if (!getEffectiveIsWeb()) return null;
895
+ const v = isArbitrary ? `sepia(${value})` : value === "0" ? "sepia(0)" : "sepia(100%)";
896
+ return { "--kb-backdrop-sepia": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
897
+ },
898
+ "backdrop-filter": ({ value, isArbitrary }) => {
899
+ if (!getEffectiveIsWeb()) return null;
900
+ if (isArbitrary) return { backdropFilter: value.replace(/_/g, " ") };
901
+ if (value === "none") return { backdropFilter: "none" };
902
+ return null;
903
+ },
904
+ // ── Mix / background blend mode (web-only) ───────────────────────────────
905
+ "mix-blend": ({ value }) => {
906
+ if (!getEffectiveIsWeb()) return null;
907
+ const modes = [
908
+ "normal",
909
+ "multiply",
910
+ "screen",
911
+ "overlay",
912
+ "darken",
913
+ "lighten",
914
+ "color-dodge",
915
+ "color-burn",
916
+ "hard-light",
917
+ "soft-light",
918
+ "difference",
919
+ "exclusion",
920
+ "hue",
921
+ "saturation",
922
+ "color",
923
+ "luminosity",
924
+ "plus-lighter"
925
+ ];
926
+ return modes.includes(value) ? { mixBlendMode: value } : null;
927
+ },
928
+ "bg-blend": ({ value }) => {
929
+ if (!getEffectiveIsWeb()) return null;
930
+ const modes = [
931
+ "normal",
932
+ "multiply",
933
+ "screen",
934
+ "overlay",
935
+ "darken",
936
+ "lighten",
937
+ "color-dodge",
938
+ "color-burn",
939
+ "hard-light",
940
+ "soft-light",
941
+ "difference",
942
+ "exclusion",
943
+ "hue",
944
+ "saturation",
945
+ "color",
946
+ "luminosity"
947
+ ];
948
+ return modes.includes(value) ? { backgroundBlendMode: value } : null;
949
+ },
950
+ // ── Will-change (web-only) ─────────────────────────────────────────────────
951
+ "will-change": ({ value, isArbitrary }) => {
952
+ if (!getEffectiveIsWeb()) return null;
953
+ if (isArbitrary) return { willChange: value.replace(/_/g, ", ") };
954
+ const presets = {
955
+ auto: "auto",
956
+ scroll: "scroll-position",
957
+ contents: "contents",
958
+ transform: "transform"
959
+ };
960
+ return presets[value] ? { willChange: presets[value] } : null;
961
+ }
962
+ };
963
+
964
+ // src/core/resolvers/layout.ts
965
+ var _standalone = null;
966
+ var _standaloneWeb = null;
967
+ function buildStandalone(web) {
968
+ return {
969
+ // Display
970
+ // React Native only supports display:'flex'|'none'. Setting 'flex' explicitly is
971
+ // a no-op normally, but is needed to re-show an element that was hidden via `hidden`.
972
+ // `grid`, `contents`, `flow-root` have no native equivalent and stay null there.
973
+ // The inline-* family (inline, inline-block, inline-flex, inline-grid) falls back
974
+ // to 'flex' on native instead — like plain `flex`, this is a no-op most of the
975
+ // time, but without it these utilities couldn't re-show a `hidden` element on
976
+ // native either (null → no style applied → still display:'none').
977
+ flex: { display: "flex" },
978
+ block: web ? { display: "block" } : null,
979
+ "inline-block": { display: web ? "inline-block" : "flex" },
980
+ inline: { display: web ? "inline" : "flex" },
981
+ grid: web ? { display: "grid" } : null,
982
+ "inline-flex": { display: web ? "inline-flex" : "flex" },
983
+ "inline-grid": { display: web ? "inline-grid" : "flex" },
984
+ hidden: { display: "none" },
985
+ contents: web ? { display: "contents" } : null,
986
+ "flow-root": web ? { display: "flow-root" } : null,
987
+ // Flex direction, wrap, grow/shrink, and alignment (both web and native) —
988
+ // every one of these is meaningless without the element also being a flex
989
+ // container. React Native Views are ALWAYS flex containers by default, so
990
+ // on native this was already true for free; on web a plain <div> defaults
991
+ // to display:block, so e.g. `items-center` alone silently did nothing.
992
+ // Folding `display: 'flex'` into these on web closes that native/web gap —
993
+ // `flex-1 items-center justify-center` now behaves the same on both,
994
+ // without also needing a separate `flex` class.
995
+ "flex-row": { flexDirection: "row", ...web ? { display: "flex" } : {} },
996
+ "flex-col": { flexDirection: "column", ...web ? { display: "flex" } : {} },
997
+ "flex-row-reverse": { flexDirection: "row-reverse", ...web ? { display: "flex" } : {} },
998
+ "flex-col-reverse": { flexDirection: "column-reverse", ...web ? { display: "flex" } : {} },
999
+ // Flex wrap (both)
1000
+ "flex-wrap": { flexWrap: "wrap", ...web ? { display: "flex" } : {} },
1001
+ "flex-wrap-reverse": { flexWrap: "wrap-reverse", ...web ? { display: "flex" } : {} },
1002
+ "flex-nowrap": { flexWrap: "nowrap", ...web ? { display: "flex" } : {} },
1003
+ // Flex grow / shrink (both) — deliberately NOT folding in display:flex
1004
+ // like the container-level properties above. flex-grow/flex-shrink are
1005
+ // item-level: they already take effect purely via the PARENT already
1006
+ // being a flex container, with zero dependency on this element's own
1007
+ // display. Forcing display:flex here would instead be an unrelated side
1008
+ // effect on THIS element's own children (turning what may be an ordinary
1009
+ // block stack into a flex row) — exactly the kind of surprise the
1010
+ // container-level properties above can never cause, since those are
1011
+ // no-ops without display:flex to begin with.
1012
+ "flex-grow": { flexGrow: 1 },
1013
+ "flex-grow-0": { flexGrow: 0 },
1014
+ "flex-shrink": { flexShrink: 1 },
1015
+ "flex-shrink-0": { flexShrink: 0 },
1016
+ // Align items (both)
1017
+ "items-start": { alignItems: "flex-start", ...web ? { display: "flex" } : {} },
1018
+ "items-end": { alignItems: "flex-end", ...web ? { display: "flex" } : {} },
1019
+ "items-center": { alignItems: "center", ...web ? { display: "flex" } : {} },
1020
+ "items-baseline": { alignItems: "baseline", ...web ? { display: "flex" } : {} },
1021
+ "items-stretch": { alignItems: "stretch", ...web ? { display: "flex" } : {} },
1022
+ // Justify content (both)
1023
+ "justify-start": { justifyContent: "flex-start", ...web ? { display: "flex" } : {} },
1024
+ "justify-end": { justifyContent: "flex-end", ...web ? { display: "flex" } : {} },
1025
+ "justify-center": { justifyContent: "center", ...web ? { display: "flex" } : {} },
1026
+ "justify-between": { justifyContent: "space-between", ...web ? { display: "flex" } : {} },
1027
+ "justify-around": { justifyContent: "space-around", ...web ? { display: "flex" } : {} },
1028
+ "justify-evenly": { justifyContent: "space-evenly", ...web ? { display: "flex" } : {} },
1029
+ // Align content (both)
1030
+ "content-start": { alignContent: "flex-start", ...web ? { display: "flex" } : {} },
1031
+ "content-end": { alignContent: "flex-end", ...web ? { display: "flex" } : {} },
1032
+ "content-center": { alignContent: "center", ...web ? { display: "flex" } : {} },
1033
+ "content-between": { alignContent: "space-between", ...web ? { display: "flex" } : {} },
1034
+ "content-around": { alignContent: "space-around", ...web ? { display: "flex" } : {} },
1035
+ "content-evenly": { alignContent: "space-evenly", ...web ? { display: "flex" } : {} },
1036
+ "content-stretch": { alignContent: "stretch", ...web ? { display: "flex" } : {} },
1037
+ // Align self (both)
1038
+ "self-auto": { alignSelf: "auto" },
1039
+ "self-start": { alignSelf: "flex-start" },
1040
+ "self-end": { alignSelf: "flex-end" },
1041
+ "self-center": { alignSelf: "center" },
1042
+ "self-stretch": { alignSelf: "stretch" },
1043
+ "self-baseline": { alignSelf: "baseline" },
1044
+ // Text align (both)
1045
+ "text-left": { textAlign: "left" },
1046
+ "text-right": { textAlign: "right" },
1047
+ "text-center": { textAlign: "center" },
1048
+ "text-justify": { textAlign: "justify" },
1049
+ // Font weight shortcuts (both)
1050
+ "font-thin": { fontWeight: "100" },
1051
+ "font-extralight": { fontWeight: "200" },
1052
+ "font-light": { fontWeight: "300" },
1053
+ "font-normal": { fontWeight: "400" },
1054
+ "font-medium": { fontWeight: "500" },
1055
+ "font-semibold": { fontWeight: "600" },
1056
+ "font-bold": { fontWeight: "700" },
1057
+ "font-extrabold": { fontWeight: "800" },
1058
+ "font-black": { fontWeight: "900" },
1059
+ // Text decoration (overline is web-only)
1060
+ underline: { textDecorationLine: "underline" },
1061
+ overline: web ? { textDecorationLine: "overline" } : null,
1062
+ "line-through": { textDecorationLine: "line-through" },
1063
+ "no-underline": { textDecorationLine: "none" },
1064
+ // Text transform (both)
1065
+ uppercase: { textTransform: "uppercase" },
1066
+ lowercase: { textTransform: "lowercase" },
1067
+ capitalize: { textTransform: "capitalize" },
1068
+ "normal-case": { textTransform: "none" },
1069
+ // Font style (both)
1070
+ italic: { fontStyle: "italic" },
1071
+ "non-italic": { fontStyle: "normal" },
1072
+ // Position (fixed/sticky/static are web-only; RN only supports relative/absolute)
1073
+ relative: { position: "relative" },
1074
+ absolute: { position: "absolute" },
1075
+ fixed: web ? { position: "fixed" } : null,
1076
+ sticky: web ? { position: "sticky" } : null,
1077
+ static: web ? { position: "static" } : null,
1078
+ // Visibility (no `visibility` prop on native; use opacity instead)
1079
+ visible: web ? { visibility: "visible" } : null,
1080
+ invisible: web ? { visibility: "hidden" } : { opacity: 0 },
1081
+ // Overflow (RN supports 'hidden' | 'visible' | 'scroll'; not 'auto')
1082
+ "overflow-hidden": { overflow: "hidden" },
1083
+ "overflow-visible": { overflow: "visible" },
1084
+ "overflow-scroll": { overflow: "scroll" },
1085
+ "overflow-auto": web ? { overflow: "auto" } : { overflow: "scroll" },
1086
+ // overflowX / overflowY are web-only
1087
+ "overflow-x-hidden": web ? { overflowX: "hidden" } : null,
1088
+ "overflow-x-scroll": web ? { overflowX: "scroll" } : null,
1089
+ "overflow-x-auto": web ? { overflowX: "auto" } : null,
1090
+ "overflow-y-hidden": web ? { overflowY: "hidden" } : null,
1091
+ "overflow-y-scroll": web ? { overflowY: "scroll" } : null,
1092
+ "overflow-y-auto": web ? { overflowY: "auto" } : null,
1093
+ // Object fit (web-only; use resizeMode prop on RN Image)
1094
+ "object-contain": web ? { objectFit: "contain" } : null,
1095
+ "object-cover": web ? { objectFit: "cover" } : null,
1096
+ "object-fill": web ? { objectFit: "fill" } : null,
1097
+ "object-none": web ? { objectFit: "none" } : null,
1098
+ "object-scale-down": web ? { objectFit: "scale-down" } : null,
1099
+ // Border style (both)
1100
+ "border-solid": { borderStyle: "solid" },
1101
+ "border-dashed": { borderStyle: "dashed" },
1102
+ "border-dotted": { borderStyle: "dotted" },
1103
+ "border-none": { borderWidth: 0 },
1104
+ // Border sides default width (both)
1105
+ "border-t": { borderTopWidth: 1 },
1106
+ "border-r": { borderRightWidth: 1 },
1107
+ "border-b": { borderBottomWidth: 1 },
1108
+ "border-l": { borderLeftWidth: 1 },
1109
+ // Cursor (web-only)
1110
+ "cursor-auto": web ? { cursor: "auto" } : null,
1111
+ "cursor-default": web ? { cursor: "default" } : null,
1112
+ "cursor-pointer": web ? { cursor: "pointer" } : null,
1113
+ "cursor-wait": web ? { cursor: "wait" } : null,
1114
+ "cursor-text": web ? { cursor: "text" } : null,
1115
+ "cursor-move": web ? { cursor: "move" } : null,
1116
+ "cursor-not-allowed": web ? { cursor: "not-allowed" } : null,
1117
+ // User select (web-only)
1118
+ "select-none": web ? { userSelect: "none" } : null,
1119
+ "select-text": web ? { userSelect: "text" } : null,
1120
+ "select-all": web ? { userSelect: "all" } : null,
1121
+ "select-auto": web ? { userSelect: "auto" } : null,
1122
+ // Pointer events (both)
1123
+ "pointer-events-none": { pointerEvents: "none" },
1124
+ "pointer-events-auto": { pointerEvents: "auto" },
1125
+ // Whitespace (web-only; RN Text uses numberOfLines prop instead)
1126
+ "whitespace-normal": web ? { whiteSpace: "normal" } : null,
1127
+ "whitespace-nowrap": web ? { whiteSpace: "nowrap" } : null,
1128
+ "whitespace-pre": web ? { whiteSpace: "pre" } : null,
1129
+ "whitespace-pre-wrap": web ? { whiteSpace: "pre-wrap" } : null,
1130
+ "whitespace-pre-line": web ? { whiteSpace: "pre-line" } : null,
1131
+ // Word break (web-only)
1132
+ "break-normal": web ? { overflowWrap: "normal", wordBreak: "normal" } : null,
1133
+ "break-words": web ? { overflowWrap: "break-word" } : null,
1134
+ "break-all": web ? { wordBreak: "break-all" } : null,
1135
+ // Misc web-only
1136
+ truncate: web ? { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } : null,
1137
+ "box-border": web ? { boxSizing: "border-box" } : null,
1138
+ "box-content": web ? { boxSizing: "content-box" } : null,
1139
+ "appearance-none": web ? { appearance: "none" } : null,
1140
+ "outline-none": web ? { outline: "none", outlineOffset: "0" } : null,
1141
+ outline: web ? { outline: "2px solid transparent", outlineOffset: "2px" } : null,
1142
+ resize: web ? { resize: "both" } : null,
1143
+ "resize-none": web ? { resize: "none" } : null,
1144
+ "resize-y": web ? { resize: "vertical" } : null,
1145
+ "resize-x": web ? { resize: "horizontal" } : null,
1146
+ antialiased: web ? { WebkitFontSmoothing: "antialiased", MozOsxFontSmoothing: "grayscale" } : null,
1147
+ "subpixel-antialiased": web ? { WebkitFontSmoothing: "subpixel-antialiased", MozOsxFontSmoothing: "auto" } : null,
1148
+ "overflow-ellipsis": web ? { textOverflow: "ellipsis" } : null,
1149
+ "sr-only": web ? {
1150
+ position: "absolute",
1151
+ width: 1,
1152
+ height: 1,
1153
+ padding: 0,
1154
+ margin: -1,
1155
+ overflow: "hidden",
1156
+ clip: "rect(0,0,0,0)",
1157
+ whiteSpace: "nowrap",
1158
+ border: 0
1159
+ } : null,
1160
+ "not-sr-only": web ? {
1161
+ position: "static",
1162
+ width: "auto",
1163
+ height: "auto",
1164
+ padding: 0,
1165
+ margin: 0,
1166
+ overflow: "visible",
1167
+ clip: "auto",
1168
+ whiteSpace: "normal"
1169
+ } : null,
1170
+ // Lists (web-only)
1171
+ "list-none": web ? { listStyleType: "none" } : null,
1172
+ "list-disc": web ? { listStyleType: "disc" } : null,
1173
+ "list-decimal": web ? { listStyleType: "decimal" } : null,
1174
+ // Background clip (web-only)
1175
+ "bg-clip-border": web ? { backgroundClip: "border-box" } : null,
1176
+ "bg-clip-padding": web ? { backgroundClip: "padding-box" } : null,
1177
+ "bg-clip-content": web ? { backgroundClip: "content-box" } : null,
1178
+ "bg-clip-text": web ? { backgroundClip: "text", WebkitBackgroundClip: "text" } : null,
1179
+ // Background image
1180
+ "bg-none": web ? { backgroundImage: "none" } : null,
1181
+ // Background size (web-only)
1182
+ "bg-auto": web ? { backgroundSize: "auto" } : null,
1183
+ "bg-cover": web ? { backgroundSize: "cover" } : null,
1184
+ "bg-contain": web ? { backgroundSize: "contain" } : null,
1185
+ // Background position (web-only)
1186
+ "bg-center": web ? { backgroundPosition: "center" } : null,
1187
+ "bg-top": web ? { backgroundPosition: "top" } : null,
1188
+ "bg-bottom": web ? { backgroundPosition: "bottom" } : null,
1189
+ "bg-left": web ? { backgroundPosition: "left" } : null,
1190
+ "bg-right": web ? { backgroundPosition: "right" } : null,
1191
+ "bg-left-top": web ? { backgroundPosition: "left top" } : null,
1192
+ "bg-left-bottom": web ? { backgroundPosition: "left bottom" } : null,
1193
+ "bg-right-top": web ? { backgroundPosition: "right top" } : null,
1194
+ "bg-right-bottom": web ? { backgroundPosition: "right bottom" } : null,
1195
+ // Background repeat (web-only)
1196
+ "bg-repeat": web ? { backgroundRepeat: "repeat" } : null,
1197
+ "bg-no-repeat": web ? { backgroundRepeat: "no-repeat" } : null,
1198
+ "bg-repeat-x": web ? { backgroundRepeat: "repeat-x" } : null,
1199
+ "bg-repeat-y": web ? { backgroundRepeat: "repeat-y" } : null,
1200
+ "bg-repeat-round": web ? { backgroundRepeat: "round" } : null,
1201
+ "bg-repeat-space": web ? { backgroundRepeat: "space" } : null,
1202
+ // Background attachment (web-only)
1203
+ "bg-fixed": web ? { backgroundAttachment: "fixed" } : null,
1204
+ "bg-local": web ? { backgroundAttachment: "local" } : null,
1205
+ "bg-scroll": web ? { backgroundAttachment: "scroll" } : null,
1206
+ // Object position (web-only)
1207
+ "object-center": web ? { objectPosition: "center" } : null,
1208
+ "object-top": web ? { objectPosition: "top" } : null,
1209
+ "object-bottom": web ? { objectPosition: "bottom" } : null,
1210
+ "object-left": web ? { objectPosition: "left" } : null,
1211
+ "object-right": web ? { objectPosition: "right" } : null,
1212
+ "object-left-top": web ? { objectPosition: "left top" } : null,
1213
+ "object-left-bottom": web ? { objectPosition: "left bottom" } : null,
1214
+ "object-right-top": web ? { objectPosition: "right top" } : null,
1215
+ "object-right-bottom": web ? { objectPosition: "right bottom" } : null,
1216
+ // Table (web-only)
1217
+ table: web ? { display: "table" } : null,
1218
+ "table-auto": web ? { tableLayout: "auto" } : null,
1219
+ "table-fixed": web ? { tableLayout: "fixed" } : null,
1220
+ "caption-top": web ? { captionSide: "top" } : null,
1221
+ "caption-bottom": web ? { captionSide: "bottom" } : null,
1222
+ "border-collapse": web ? { borderCollapse: "collapse" } : null,
1223
+ "border-separate": web ? { borderCollapse: "separate" } : null,
1224
+ // List style position (web-only)
1225
+ "list-inside": web ? { listStylePosition: "inside" } : null,
1226
+ "list-outside": web ? { listStylePosition: "outside" } : null,
1227
+ // Font variant numeric (web-only)
1228
+ "normal-nums": web ? { fontVariantNumeric: "normal" } : null,
1229
+ ordinal: web ? { fontVariantNumeric: "ordinal" } : null,
1230
+ "slashed-zero": web ? { fontVariantNumeric: "slashed-zero" } : null,
1231
+ "lining-nums": web ? { fontVariantNumeric: "lining-nums" } : null,
1232
+ "oldstyle-nums": web ? { fontVariantNumeric: "oldstyle-nums" } : null,
1233
+ "proportional-nums": web ? { fontVariantNumeric: "proportional-nums" } : null,
1234
+ "tabular-nums": web ? { fontVariantNumeric: "tabular-nums" } : null,
1235
+ "diagonal-fractions": web ? { fontVariantNumeric: "diagonal-fractions" } : null,
1236
+ "stacked-fractions": web ? { fontVariantNumeric: "stacked-fractions" } : null,
1237
+ // Isolation (web-only)
1238
+ isolate: web ? { isolation: "isolate" } : null,
1239
+ "isolation-auto": web ? { isolation: "auto" } : null,
1240
+ // Backface visibility (both iOS and Android)
1241
+ "backface-visible": { backfaceVisibility: "visible" },
1242
+ "backface-hidden": { backfaceVisibility: "hidden" },
1243
+ // CSS filters — standalone = default/full effect (web-only)
1244
+ grayscale: web ? { "--kb-grayscale": "grayscale(100%)", filter: FILTER_COMPOSE } : null,
1245
+ invert: web ? { "--kb-invert": "invert(100%)", filter: FILTER_COMPOSE } : null,
1246
+ sepia: web ? { "--kb-sepia": "sepia(100%)", filter: FILTER_COMPOSE } : null,
1247
+ // Divide none
1248
+ "divide-none": web ? { __divideX: 0, __divideY: 0 } : null,
1249
+ // Group / peer markers (no-op: no style emitted; just ensures isKnownUtility returns true)
1250
+ group: web ? {} : null,
1251
+ peer: web ? {} : null,
1252
+ // Text-wrap (web-only)
1253
+ "text-wrap": web ? { textWrap: "wrap" } : null,
1254
+ "text-nowrap": web ? { textWrap: "nowrap" } : null,
1255
+ "text-balance": web ? { textWrap: "balance" } : null,
1256
+ "text-pretty": web ? { textWrap: "pretty" } : null,
1257
+ // Screen sizing — dvw/dvh (dynamic viewport units) instead of vw/vh: on mobile
1258
+ // browsers, vh/vw are pinned to the LARGEST viewport size (address bar hidden),
1259
+ // so `h-screen` overflows behind the address bar when it's shown. dvh/dvw track
1260
+ // the actual visible viewport as browser chrome shows/hides. Desktop behavior
1261
+ // is unchanged since there's no dynamic chrome to account for.
1262
+ // (vw/dvw values are web-only; vh/dvh values work cross-platform via spacing scale for h-*)
1263
+ "w-screen": web ? { width: "100dvw" } : null,
1264
+ "min-h-screen": { minHeight: "100dvh" },
1265
+ "max-h-screen": { maxHeight: "100dvh" },
1266
+ "min-w-screen": web ? { minWidth: "100dvw" } : null,
1267
+ "max-w-screen": web ? { maxWidth: "100dvw" } : null,
1268
+ // max-w named container sizes (mirrors Tailwind's container scale)
1269
+ "max-w-none": { maxWidth: "none" },
1270
+ "max-w-xs": { maxWidth: 320 },
1271
+ "max-w-sm": { maxWidth: 384 },
1272
+ "max-w-md": { maxWidth: 448 },
1273
+ "max-w-lg": { maxWidth: 512 },
1274
+ "max-w-xl": { maxWidth: 576 },
1275
+ "max-w-2xl": { maxWidth: 672 },
1276
+ "max-w-3xl": { maxWidth: 768 },
1277
+ "max-w-4xl": { maxWidth: 896 },
1278
+ "max-w-5xl": { maxWidth: 1024 },
1279
+ "max-w-6xl": { maxWidth: 1152 },
1280
+ "max-w-7xl": { maxWidth: 1280 },
1281
+ "max-w-prose": web ? { maxWidth: "65ch" } : null,
1282
+ // Extended cursors (web-only)
1283
+ "cursor-grab": web ? { cursor: "grab" } : null,
1284
+ "cursor-grabbing": web ? { cursor: "grabbing" } : null,
1285
+ "cursor-zoom-in": web ? { cursor: "zoom-in" } : null,
1286
+ "cursor-zoom-out": web ? { cursor: "zoom-out" } : null,
1287
+ "cursor-crosshair": web ? { cursor: "crosshair" } : null,
1288
+ "cursor-help": web ? { cursor: "help" } : null,
1289
+ "cursor-none": web ? { cursor: "none" } : null,
1290
+ // Overflow clip (web-only)
1291
+ "overflow-clip": web ? { overflow: "clip" } : null,
1292
+ "overflow-x-clip": web ? { overflowX: "clip" } : null,
1293
+ "overflow-y-clip": web ? { overflowY: "clip" } : null,
1294
+ // Scroll behavior (web-only)
1295
+ "scroll-smooth": web ? { scrollBehavior: "smooth" } : null,
1296
+ "scroll-auto": web ? { scrollBehavior: "auto" } : null,
1297
+ // Float (web-only)
1298
+ "float-left": web ? { float: "left" } : null,
1299
+ "float-right": web ? { float: "right" } : null,
1300
+ "float-start": web ? { float: "inline-start" } : null,
1301
+ "float-end": web ? { float: "inline-end" } : null,
1302
+ "float-none": web ? { float: "none" } : null,
1303
+ // Clear (web-only)
1304
+ "clear-left": web ? { clear: "left" } : null,
1305
+ "clear-right": web ? { clear: "right" } : null,
1306
+ "clear-both": web ? { clear: "both" } : null,
1307
+ "clear-start": web ? { clear: "inline-start" } : null,
1308
+ "clear-end": web ? { clear: "inline-end" } : null,
1309
+ "clear-none": web ? { clear: "none" } : null,
1310
+ // Vertical align (web-only)
1311
+ "align-baseline": web ? { verticalAlign: "baseline" } : null,
1312
+ "align-top": web ? { verticalAlign: "top" } : null,
1313
+ "align-middle": web ? { verticalAlign: "middle" } : null,
1314
+ "align-bottom": web ? { verticalAlign: "bottom" } : null,
1315
+ "align-text-top": web ? { verticalAlign: "text-top" } : null,
1316
+ "align-text-bottom": web ? { verticalAlign: "text-bottom" } : null,
1317
+ "align-sub": web ? { verticalAlign: "sub" } : null,
1318
+ "align-super": web ? { verticalAlign: "super" } : null,
1319
+ // Touch action (web-only)
1320
+ "touch-auto": web ? { touchAction: "auto" } : null,
1321
+ "touch-none": web ? { touchAction: "none" } : null,
1322
+ "touch-pan-x": web ? { touchAction: "pan-x" } : null,
1323
+ "touch-pan-y": web ? { touchAction: "pan-y" } : null,
1324
+ "touch-pan-left": web ? { touchAction: "pan-left" } : null,
1325
+ "touch-pan-right": web ? { touchAction: "pan-right" } : null,
1326
+ "touch-pan-up": web ? { touchAction: "pan-up" } : null,
1327
+ "touch-pan-down": web ? { touchAction: "pan-down" } : null,
1328
+ "touch-pinch-zoom": web ? { touchAction: "pinch-zoom" } : null,
1329
+ "touch-manipulation": web ? { touchAction: "manipulation" } : null
1330
+ };
1331
+ }
1332
+ function getStandalone() {
1333
+ const web = getEffectiveIsWeb();
1334
+ if (!_standalone || _standaloneWeb !== web) {
1335
+ _standalone = buildStandalone(web);
1336
+ _standaloneWeb = web;
1337
+ }
1338
+ return _standalone;
1339
+ }
1340
+ var layoutResolvers = {
1341
+ // ── Grid (responsive column count: grid-1 … grid-12, or arbitrary) ───────
1342
+ grid: ({ value, isArbitrary }) => {
1343
+ if (!value) return null;
1344
+ if (isArbitrary) return { display: "grid", gridTemplateColumns: value };
1345
+ const n = parseInt(value, 10);
1346
+ if (isNaN(n) || n < 1 || n > 12) return null;
1347
+ return { display: "grid", gridTemplateColumns: `repeat(${n}, minmax(0, 1fr))` };
1348
+ },
1349
+ // ── Grid template columns/rows (Tailwind-style aliases, web-only) ──────────
1350
+ "grid-cols": ({ value, isArbitrary }) => {
1351
+ if (!getEffectiveIsWeb()) return null;
1352
+ if (!value) return null;
1353
+ if (isArbitrary) return { display: "grid", gridTemplateColumns: value };
1354
+ if (value === "none") return { display: "grid", gridTemplateColumns: "none" };
1355
+ const n = parseInt(value, 10);
1356
+ if (isNaN(n) || n < 1 || n > 12) return null;
1357
+ return { display: "grid", gridTemplateColumns: `repeat(${n}, minmax(0, 1fr))` };
1358
+ },
1359
+ "grid-rows": ({ value, isArbitrary }) => {
1360
+ if (!getEffectiveIsWeb()) return null;
1361
+ if (!value) return null;
1362
+ if (isArbitrary) return { gridTemplateRows: value };
1363
+ if (value === "none") return { gridTemplateRows: "none" };
1364
+ const n = parseInt(value, 10);
1365
+ if (isNaN(n) || n < 1) return null;
1366
+ return { gridTemplateRows: `repeat(${n}, minmax(0, 1fr))` };
1367
+ },
1368
+ // ── Grid auto flow (web-only) ─────────────────────────────────────────────
1369
+ "grid-flow": ({ value }) => {
1370
+ if (!getEffectiveIsWeb()) return null;
1371
+ const flows = {
1372
+ row: "row",
1373
+ col: "column",
1374
+ dense: "dense",
1375
+ "row-dense": "row dense",
1376
+ "col-dense": "column dense"
1377
+ };
1378
+ const flow = flows[value];
1379
+ return flow ? { gridAutoFlow: flow } : null;
1380
+ },
1381
+ // ── Grid auto sizing (web-only) ───────────────────────────────────────────
1382
+ "auto-cols": ({ value, isArbitrary }) => {
1383
+ if (!getEffectiveIsWeb()) return null;
1384
+ if (isArbitrary) return { gridAutoColumns: value };
1385
+ const presets = {
1386
+ auto: "auto",
1387
+ min: "min-content",
1388
+ max: "max-content",
1389
+ fr: "minmax(0, 1fr)"
1390
+ };
1391
+ const v = presets[value];
1392
+ return v ? { gridAutoColumns: v } : null;
1393
+ },
1394
+ "auto-rows": ({ value, isArbitrary }) => {
1395
+ if (!getEffectiveIsWeb()) return null;
1396
+ if (isArbitrary) return { gridAutoRows: value };
1397
+ const presets = {
1398
+ auto: "auto",
1399
+ min: "min-content",
1400
+ max: "max-content",
1401
+ fr: "minmax(0, 1fr)"
1402
+ };
1403
+ const v = presets[value];
1404
+ return v ? { gridAutoRows: v } : null;
1405
+ },
1406
+ // ── Grid column placement (web-only) ──────────────────────────────────────
1407
+ "col-span": ({ value }) => {
1408
+ if (!getEffectiveIsWeb()) return null;
1409
+ if (value === "full") return { gridColumn: "1 / -1" };
1410
+ const n = parseInt(value, 10);
1411
+ return isNaN(n) ? null : { gridColumn: `span ${n} / span ${n}` };
1412
+ },
1413
+ "col-start": ({ value, isArbitrary }) => {
1414
+ if (!getEffectiveIsWeb()) return null;
1415
+ if (value === "auto") return { gridColumnStart: "auto" };
1416
+ if (isArbitrary) return { gridColumnStart: value };
1417
+ const n = parseInt(value, 10);
1418
+ return isNaN(n) ? null : { gridColumnStart: n };
1419
+ },
1420
+ "col-end": ({ value, isArbitrary }) => {
1421
+ if (!getEffectiveIsWeb()) return null;
1422
+ if (value === "auto") return { gridColumnEnd: "auto" };
1423
+ if (isArbitrary) return { gridColumnEnd: value };
1424
+ const n = parseInt(value, 10);
1425
+ return isNaN(n) ? null : { gridColumnEnd: n };
1426
+ },
1427
+ col: ({ value, isArbitrary }) => {
1428
+ if (!getEffectiveIsWeb()) return null;
1429
+ if (!value || value === "auto") return { gridColumn: "auto" };
1430
+ if (isArbitrary) return { gridColumn: value };
1431
+ return null;
1432
+ },
1433
+ // ── Grid row placement (web-only) ─────────────────────────────────────────
1434
+ "row-span": ({ value }) => {
1435
+ if (!getEffectiveIsWeb()) return null;
1436
+ if (value === "full") return { gridRow: "1 / -1" };
1437
+ const n = parseInt(value, 10);
1438
+ return isNaN(n) ? null : { gridRow: `span ${n} / span ${n}` };
1439
+ },
1440
+ "row-start": ({ value, isArbitrary }) => {
1441
+ if (!getEffectiveIsWeb()) return null;
1442
+ if (value === "auto") return { gridRowStart: "auto" };
1443
+ if (isArbitrary) return { gridRowStart: value };
1444
+ const n = parseInt(value, 10);
1445
+ return isNaN(n) ? null : { gridRowStart: n };
1446
+ },
1447
+ "row-end": ({ value, isArbitrary }) => {
1448
+ if (!getEffectiveIsWeb()) return null;
1449
+ if (value === "auto") return { gridRowEnd: "auto" };
1450
+ if (isArbitrary) return { gridRowEnd: value };
1451
+ const n = parseInt(value, 10);
1452
+ return isNaN(n) ? null : { gridRowEnd: n };
1453
+ },
1454
+ row: ({ value, isArbitrary }) => {
1455
+ if (!getEffectiveIsWeb()) return null;
1456
+ if (!value || value === "auto") return { gridRow: "auto" };
1457
+ if (isArbitrary) return { gridRow: value };
1458
+ return null;
1459
+ },
1460
+ // ── Grid alignment (web-only) ──────────────────────────────────────────────
1461
+ // place-items is shorthand for align-items + justify-items on a grid container
1462
+ "place-items": ({ value }) => {
1463
+ if (!getEffectiveIsWeb()) return null;
1464
+ const map = {
1465
+ start: "start",
1466
+ end: "end",
1467
+ center: "center",
1468
+ stretch: "stretch",
1469
+ baseline: "baseline"
1470
+ };
1471
+ return map[value] ? { placeItems: map[value] } : null;
1472
+ },
1473
+ // place-content is shorthand for align-content + justify-content on a grid container
1474
+ "place-content": ({ value }) => {
1475
+ if (!getEffectiveIsWeb()) return null;
1476
+ const map = {
1477
+ start: "start",
1478
+ end: "end",
1479
+ center: "center",
1480
+ stretch: "stretch",
1481
+ between: "space-between",
1482
+ around: "space-around",
1483
+ evenly: "space-evenly",
1484
+ baseline: "baseline"
1485
+ };
1486
+ return map[value] ? { placeContent: map[value] } : null;
1487
+ },
1488
+ // justify-items controls inline-axis alignment of grid items within their cells
1489
+ "justify-items": ({ value }) => {
1490
+ if (!getEffectiveIsWeb()) return null;
1491
+ const map = {
1492
+ start: "start",
1493
+ end: "end",
1494
+ center: "center",
1495
+ stretch: "stretch"
1496
+ };
1497
+ return map[value] ? { justifyItems: map[value] } : null;
1498
+ },
1499
+ // place-self is shorthand for align-self + justify-self on a grid item
1500
+ "place-self": ({ value }) => {
1501
+ if (!getEffectiveIsWeb()) return null;
1502
+ const map = {
1503
+ auto: "auto",
1504
+ start: "start",
1505
+ end: "end",
1506
+ center: "center",
1507
+ stretch: "stretch"
1508
+ };
1509
+ return map[value] ? { placeSelf: map[value] } : null;
1510
+ },
1511
+ // justify-self controls inline-axis self-alignment of a grid item
1512
+ "justify-self": ({ value }) => {
1513
+ if (!getEffectiveIsWeb()) return null;
1514
+ const map = {
1515
+ auto: "auto",
1516
+ start: "start",
1517
+ end: "end",
1518
+ center: "center",
1519
+ stretch: "stretch"
1520
+ };
1521
+ return map[value] ? { justifySelf: map[value] } : null;
1522
+ },
1523
+ // ── Flex ───────────────────────────────────────────────────────────────────
1524
+ // flex-1/flex-auto/flex-none/grow/shrink are item-level sizing properties —
1525
+ // deliberately NOT implying display:flex here. See the "Flex grow / shrink"
1526
+ // comment in the standalone table above for why: these already work via the
1527
+ // PARENT's flex context regardless of this element's own display, so
1528
+ // forcing display:flex here would only be a surprising, unrelated side
1529
+ // effect on this element's OWN children.
1530
+ flex: ({ value, isArbitrary }, { flex }) => {
1531
+ if (!value) return { display: "flex" };
1532
+ if (isArbitrary) {
1533
+ const v = parseFloat(value);
1534
+ return isNaN(v) ? null : { flex: v };
1535
+ }
1536
+ if (value in flex) {
1537
+ const v = flex[value];
1538
+ if (typeof v === "string" && !getEffectiveIsWeb()) {
1539
+ if (v === "auto" || v === "initial") return { flex: 1 };
1540
+ if (v === "none") return { flex: 0 };
1541
+ return null;
1542
+ }
1543
+ return { flex: v };
1544
+ }
1545
+ const n = parseFloat(value);
1546
+ return isNaN(n) ? null : { flex: n };
1547
+ },
1548
+ grow: ({ value }, _) => {
1549
+ if (!value) return { flexGrow: 1 };
1550
+ const n = parseFloat(value);
1551
+ return { flexGrow: isNaN(n) ? 1 : n };
1552
+ },
1553
+ shrink: ({ value }, _) => {
1554
+ if (!value) return { flexShrink: 1 };
1555
+ const n = parseFloat(value);
1556
+ return { flexShrink: isNaN(n) ? 1 : n };
1557
+ },
1558
+ order: ({ value, negative }, _) => {
1559
+ const n = parseInt(value, 10);
1560
+ return isNaN(n) ? null : { order: negative ? -n : n };
1561
+ },
1562
+ // ── Z-index ────────────────────────────────────────────────────────────────
1563
+ z: ({ value, isArbitrary }, { zIndex }) => {
1564
+ if (isArbitrary) {
1565
+ const n2 = parseInt(value);
1566
+ return isNaN(n2) ? null : { zIndex: n2 };
1567
+ }
1568
+ const v = zIndex[value];
1569
+ if (v !== void 0) {
1570
+ if (v === "auto") return getEffectiveIsWeb() ? { zIndex: "auto" } : null;
1571
+ return { zIndex: v };
1572
+ }
1573
+ const n = parseInt(value);
1574
+ return isNaN(n) ? null : { zIndex: n };
1575
+ },
1576
+ // ── Aspect ratio ───────────────────────────────────────────────────────────
1577
+ aspect: ({ value, isArbitrary }) => {
1578
+ if (isArbitrary) return { aspectRatio: value };
1579
+ const presets = { auto: "auto", square: 1, video: 16 / 9 };
1580
+ if (!(value in presets)) return null;
1581
+ const v = presets[value];
1582
+ if (v === "auto") return getEffectiveIsWeb() ? { aspectRatio: "auto" } : null;
1583
+ return { aspectRatio: v };
1584
+ },
1585
+ // ── Columns (web-only) ─────────────────────────────────────────────────────
1586
+ columns: ({ value, isArbitrary }) => {
1587
+ if (!getEffectiveIsWeb()) return null;
1588
+ if (isArbitrary) return { columnCount: value };
1589
+ if (value === "auto") return { columnCount: "auto" };
1590
+ const n = parseInt(value, 10);
1591
+ if (!isNaN(n)) return { columnCount: n };
1592
+ const widths = {
1593
+ "3xs": "16rem",
1594
+ "2xs": "18rem",
1595
+ xs: "20rem",
1596
+ sm: "24rem",
1597
+ md: "28rem",
1598
+ lg: "32rem",
1599
+ xl: "36rem",
1600
+ "2xl": "42rem",
1601
+ "3xl": "48rem",
1602
+ "4xl": "56rem",
1603
+ "5xl": "64rem",
1604
+ "6xl": "72rem",
1605
+ "7xl": "80rem"
1606
+ };
1607
+ return widths[value] ? { columnWidth: widths[value] } : null;
1608
+ }
1609
+ };
1610
+
1611
+ // src/core/resolvers/typography.ts
1612
+ function resolveFontSize(value, fontSizes, isArbitrary) {
1613
+ if (isArbitrary) return getEffectiveIsWeb() ? value : toNativeValue(value);
1614
+ return fontSizes[value] ?? null;
1615
+ }
1616
+ var typographyResolvers = {
1617
+ // ── Text ───────────────────────────────────────────────────────────────────
1618
+ text: ({ value, isArbitrary }, { colors, fontSize }) => {
1619
+ const size = resolveFontSize(value, fontSize, false);
1620
+ if (!isArbitrary && size !== null) return { fontSize: size };
1621
+ if (isArbitrary) {
1622
+ if (/^\d/.test(value) || /^(calc|min|max|clamp)/.test(value)) {
1623
+ return { fontSize: getEffectiveIsWeb() ? value : toNativeValue(value) };
1624
+ }
1625
+ return { color: getEffectiveIsWeb() ? withOpacityVar(value, "--text-opacity") : value };
1626
+ }
1627
+ const color = resolveColor(value, colors, false);
1628
+ if (!color) return null;
1629
+ return { color: getEffectiveIsWeb() ? withOpacityVar(color, "--text-opacity") : color };
1630
+ },
1631
+ "text-opacity": ({ value, isArbitrary }, _) => {
1632
+ if (!getEffectiveIsWeb()) return null;
1633
+ const n = parseFloat(value);
1634
+ if (isNaN(n)) return null;
1635
+ const v = isArbitrary ? n > 1 ? n / 100 : n : n / 100;
1636
+ return { "--text-opacity": v };
1637
+ },
1638
+ // ── Text decoration advanced (web-only) ───────────────────────────────────
1639
+ decoration: ({ value, isArbitrary }, { colors }) => {
1640
+ if (!getEffectiveIsWeb()) return null;
1641
+ if (isArbitrary) {
1642
+ if (/^\d/.test(value) || value.startsWith("calc(")) return { textDecorationThickness: value };
1643
+ return { textDecorationColor: value };
1644
+ }
1645
+ const thickTokens = {
1646
+ auto: "auto",
1647
+ "from-font": "from-font",
1648
+ "0": "0px",
1649
+ "1": "1px",
1650
+ "2": "2px",
1651
+ "4": "4px",
1652
+ "8": "8px"
1653
+ };
1654
+ if (value in thickTokens) return { textDecorationThickness: thickTokens[value] };
1655
+ const styleTokens = {
1656
+ solid: "solid",
1657
+ dashed: "dashed",
1658
+ dotted: "dotted",
1659
+ double: "double",
1660
+ wavy: "wavy"
1661
+ };
1662
+ if (value in styleTokens) return { textDecorationStyle: styleTokens[value] };
1663
+ const color = resolveColor(value, colors, false);
1664
+ return color ? { textDecorationColor: color } : null;
1665
+ },
1666
+ "underline-offset": ({ value, isArbitrary }) => {
1667
+ if (!getEffectiveIsWeb()) return null;
1668
+ if (isArbitrary) return { textUnderlineOffset: value };
1669
+ const offsets = {
1670
+ auto: "auto",
1671
+ "0": "0px",
1672
+ "1": "1px",
1673
+ "2": "2px",
1674
+ "4": "4px",
1675
+ "8": "8px"
1676
+ };
1677
+ return offsets[value] ? { textUnderlineOffset: offsets[value] } : null;
1678
+ },
1679
+ // ── Content (web-only — for before:/after: pseudo-elements, which don't exist on native) ──
1680
+ // Arbitrary value carries its own quotes from the bracket syntax (content-['*'] parses to
1681
+ // the literal string 'you can see it' quotes included), so it passes straight through as a
1682
+ // valid `content: '*'` CSS value with no extra wrapping needed.
1683
+ content: ({ value, isArbitrary }) => {
1684
+ if (!getEffectiveIsWeb()) return null;
1685
+ if (isArbitrary) return { content: value };
1686
+ const presets = { none: "none" };
1687
+ return value in presets ? { content: presets[value] } : null;
1688
+ },
1689
+ // ── Font ───────────────────────────────────────────────────────────────────
1690
+ font: ({ value, isArbitrary }, { fontFamily, fontWeight }) => {
1691
+ if (isArbitrary) return { fontFamily: value };
1692
+ if (value in fontFamily) {
1693
+ const ff = fontFamily[value];
1694
+ return { fontFamily: Array.isArray(ff) ? ff.join(", ") : ff };
1695
+ }
1696
+ if (value in fontWeight) return { fontWeight: String(fontWeight[value]) };
1697
+ return null;
1698
+ },
1699
+ // ── Line height ────────────────────────────────────────────────────────────
1700
+ leading: ({ value, isArbitrary }, { lineHeight }) => {
1701
+ if (isArbitrary) return { lineHeight: getEffectiveIsWeb() ? value : toNativeValue(value) };
1702
+ const v = lineHeight[value];
1703
+ if (v === void 0) return null;
1704
+ if (typeof v === "string" && !getEffectiveIsWeb()) return { lineHeight: toNativeValue(v) };
1705
+ return { lineHeight: v };
1706
+ },
1707
+ // ── Letter spacing ─────────────────────────────────────────────────────────
1708
+ tracking: ({ value, isArbitrary }, { letterSpacing }) => {
1709
+ if (isArbitrary) return { letterSpacing: getEffectiveIsWeb() ? value : toNativeValue(value) };
1710
+ const v = letterSpacing[value];
1711
+ return v !== void 0 ? { letterSpacing: v } : null;
1712
+ },
1713
+ // ── Line-clamp (web-only) ─────────────────────────────────────────────────
1714
+ "line-clamp": ({ value }) => {
1715
+ if (!getEffectiveIsWeb()) return null;
1716
+ if (value === "none") return { overflow: "visible", display: "block", WebkitLineClamp: "unset" };
1717
+ const n = parseInt(value, 10);
1718
+ if (isNaN(n) || n < 1) return null;
1719
+ return {
1720
+ overflow: "hidden",
1721
+ display: "-webkit-box",
1722
+ WebkitBoxOrient: "vertical",
1723
+ WebkitLineClamp: n
1724
+ };
1725
+ }
1726
+ };
1727
+
1728
+ // src/core/resolvers/transform.ts
1729
+ var transformResolvers = {
1730
+ // ── Transform extras (web-only) ───────────────────────────────────────────
1731
+ "skew-x": ({ value, negative, isArbitrary }) => {
1732
+ const deg = isArbitrary ? value : `${(negative ? -1 : 1) * parseFloat(value)}deg`;
1733
+ if (!isArbitrary && isNaN(parseFloat(value))) return null;
1734
+ return getEffectiveIsWeb() ? { transform: `skewX(${deg})` } : { transform: [{ skewX: deg }] };
1735
+ },
1736
+ "skew-y": ({ value, negative, isArbitrary }) => {
1737
+ const deg = isArbitrary ? value : `${(negative ? -1 : 1) * parseFloat(value)}deg`;
1738
+ if (!isArbitrary && isNaN(parseFloat(value))) return null;
1739
+ return getEffectiveIsWeb() ? { transform: `skewY(${deg})` } : { transform: [{ skewY: deg }] };
1740
+ },
1741
+ // Arbitrary full transform string: transform-[rotate(45deg)_scale(1.5)]
1742
+ transform: ({ value, isArbitrary }) => {
1743
+ if (!getEffectiveIsWeb()) return null;
1744
+ if (isArbitrary) return { transform: value.replace(/_/g, " ") };
1745
+ if (value === "none") return { transform: "none" };
1746
+ return null;
1747
+ },
1748
+ // Transform origin
1749
+ origin: ({ value, isArbitrary }) => {
1750
+ if (!getEffectiveIsWeb()) return null;
1751
+ if (isArbitrary) return { transformOrigin: value.replace(/_/g, " ") };
1752
+ const origins = {
1753
+ center: "center",
1754
+ top: "top",
1755
+ "top-right": "top right",
1756
+ right: "right",
1757
+ "bottom-right": "bottom right",
1758
+ bottom: "bottom",
1759
+ "bottom-left": "bottom left",
1760
+ left: "left",
1761
+ "top-left": "top left"
1762
+ };
1763
+ return origins[value] ? { transformOrigin: origins[value] } : null;
1764
+ },
1765
+ // ── Scale ──────────────────────────────────────────────────────────────────
1766
+ // Non-arbitrary: scale-150 → value='150' → 150/100 = 1.5
1767
+ // Arbitrary: scale-[1.5] → value='1.5' → used as-is (already the factor)
1768
+ scale: ({ value, isArbitrary }) => {
1769
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1770
+ if (isNaN(n)) return null;
1771
+ return getEffectiveIsWeb() ? { transform: `scale(${n})` } : { transform: [{ scale: n }] };
1772
+ },
1773
+ "scale-x": ({ value, isArbitrary }) => {
1774
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1775
+ if (isNaN(n)) return null;
1776
+ return getEffectiveIsWeb() ? { transform: `scaleX(${n})` } : { transform: [{ scaleX: n }] };
1777
+ },
1778
+ "scale-y": ({ value, isArbitrary }) => {
1779
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1780
+ if (isNaN(n)) return null;
1781
+ return getEffectiveIsWeb() ? { transform: `scaleY(${n})` } : { transform: [{ scaleY: n }] };
1782
+ },
1783
+ // ── Rotate ─────────────────────────────────────────────────────────────────
1784
+ rotate: ({ value, negative, isArbitrary }) => {
1785
+ if (isArbitrary) {
1786
+ const finalValue = negative ? `-${value}` : value;
1787
+ return getEffectiveIsWeb() ? { transform: `rotate(${finalValue})` } : { transform: [{ rotate: finalValue }] };
1788
+ }
1789
+ const deg = parseFloat(value);
1790
+ if (isNaN(deg)) return null;
1791
+ const finalDeg = negative ? -deg : deg;
1792
+ return getEffectiveIsWeb() ? { transform: `rotate(${finalDeg}deg)` } : { transform: [{ rotate: `${finalDeg}deg` }] };
1793
+ },
1794
+ // ── Perspective transform (cross-platform: iOS, Android, web) ────────────
1795
+ perspective: ({ value, isArbitrary }) => {
1796
+ if (isArbitrary) {
1797
+ const numPx = parseFloat(value);
1798
+ if (getEffectiveIsWeb()) return { transform: `perspective(${value})` };
1799
+ return isNaN(numPx) ? null : { transform: [{ perspective: numPx }] };
1800
+ }
1801
+ if (value === "none") return getEffectiveIsWeb() ? { transform: "none" } : null;
1802
+ const n = parseFloat(value);
1803
+ if (isNaN(n)) return null;
1804
+ return getEffectiveIsWeb() ? { transform: `perspective(${n}px)` } : { transform: [{ perspective: n }] };
1805
+ }
1806
+ };
1807
+
1808
+ // src/core/resolvers/effects.ts
1809
+ function keyframeDeclToCSS(decl) {
1810
+ return Object.entries(decl).map(([prop, val]) => `${prop.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${val}`).join("; ");
1811
+ }
1812
+ function buildKeyframeCSS(name, steps) {
1813
+ const body = Object.entries(steps).map(([selector, decl]) => `${selector} { ${keyframeDeclToCSS(decl)} }`).join(" ");
1814
+ return `@keyframes ${name} { ${body} }`;
1815
+ }
1816
+ var BUILTIN_ANIMATIONS = {
1817
+ none: { animation: "none" },
1818
+ spin: {
1819
+ animation: "kb-spin 1s linear infinite",
1820
+ __keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
1821
+ },
1822
+ ping: {
1823
+ animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
1824
+ __keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
1825
+ },
1826
+ pulse: {
1827
+ animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
1828
+ __keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
1829
+ },
1830
+ bounce: {
1831
+ animation: "kb-bounce 1s infinite",
1832
+ __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) } }"
1833
+ }
1834
+ };
1835
+ function buildAnimationValue(animation, keyframes) {
1836
+ const words = animation.trim().split(/\s+/);
1837
+ const name = words[0];
1838
+ if (!name) return { animation };
1839
+ const builtin = BUILTIN_ANIMATIONS[name];
1840
+ if (builtin?.__keyframe) {
1841
+ const kbName = builtin.animation.split(/\s+/, 1)[0];
1842
+ return { animation: [kbName, ...words.slice(1)].join(" "), __keyframe: builtin.__keyframe };
1843
+ }
1844
+ const steps = keyframes?.[name];
1845
+ return steps ? { animation, __keyframe: buildKeyframeCSS(name, steps) } : { animation };
1846
+ }
1847
+ var effectResolvers = {
1848
+ // ── Opacity ────────────────────────────────────────────────────────────────
1849
+ opacity: ({ value, isArbitrary }, { opacity }) => {
1850
+ if (isArbitrary) {
1851
+ const v2 = parseFloat(value);
1852
+ return isNaN(v2) ? null : { opacity: v2 > 1 ? v2 / 100 : v2 };
1853
+ }
1854
+ const v = opacity[value];
1855
+ if (v !== void 0) return { opacity: v };
1856
+ const n = parseFloat(value);
1857
+ return isNaN(n) ? null : { opacity: n > 1 ? n / 100 : n };
1858
+ },
1859
+ // ── Shadow ─────────────────────────────────────────────────────────────────
1860
+ shadow: ({ value }, { shadow }) => {
1861
+ const key = value === "" ? "DEFAULT" : value;
1862
+ return shadow[key] ?? null;
1863
+ },
1864
+ // ── Animations (CSS keyframe animations, web-only) ────────────────────────
1865
+ // Each variant injects a @keyframes rule once via the __keyframe marker.
1866
+ // resolver.ts detects __keyframe, injects the rule, then strips the marker
1867
+ // so it never reaches element inline styles.
1868
+ animate: ({ value, isArbitrary }, theme) => {
1869
+ if (!getEffectiveIsWeb()) return null;
1870
+ if (isArbitrary) return buildAnimationValue(value.replace(/_/g, " "), theme.keyframes);
1871
+ if (value in BUILTIN_ANIMATIONS) return BUILTIN_ANIMATIONS[value];
1872
+ const custom = theme.animation?.[value];
1873
+ return custom ? buildAnimationValue(custom, theme.keyframes) : null;
1874
+ },
1875
+ // ── Transition (web-only; use Animated API on native) ─────────────────────
1876
+ transition: ({ value }) => {
1877
+ if (!getEffectiveIsWeb()) return null;
1878
+ const presets = {
1879
+ "": "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",
1880
+ all: "all 150ms",
1881
+ none: "none",
1882
+ colors: "color 150ms, background-color 150ms, border-color 150ms",
1883
+ opacity: "opacity 150ms",
1884
+ shadow: "box-shadow 150ms",
1885
+ transform: "transform 150ms"
1886
+ };
1887
+ const v = presets[value];
1888
+ return v !== void 0 ? { transition: v } : null;
1889
+ },
1890
+ duration: ({ value, isArbitrary }) => {
1891
+ if (!getEffectiveIsWeb()) return null;
1892
+ if (!isArbitrary && isNaN(parseFloat(value))) return null;
1893
+ const ms = isArbitrary ? value : `${value}ms`;
1894
+ return { transitionDuration: ms };
1895
+ },
1896
+ delay: ({ value, isArbitrary }) => {
1897
+ if (!getEffectiveIsWeb()) return null;
1898
+ if (!isArbitrary && isNaN(parseFloat(value))) return null;
1899
+ const ms = isArbitrary ? value : `${value}ms`;
1900
+ return { transitionDelay: ms };
1901
+ },
1902
+ ease: ({ value, isArbitrary }) => {
1903
+ if (!getEffectiveIsWeb()) return null;
1904
+ if (isArbitrary) return { transitionTimingFunction: value };
1905
+ const presets = {
1906
+ linear: "linear",
1907
+ in: "cubic-bezier(0.4, 0, 1, 1)",
1908
+ out: "cubic-bezier(0, 0, 0.2, 1)",
1909
+ "in-out": "cubic-bezier(0.4, 0, 0.2, 1)"
1910
+ };
1911
+ const v = presets[value];
1912
+ return v !== void 0 ? { transitionTimingFunction: v } : null;
1913
+ }
1914
+ };
1915
+
1916
+ // src/core/resolvers/misc.ts
1917
+ var miscResolvers = {
1918
+ // ── Arbitrary CSS property (web-only): [property:value] with no utility prefix ──
1919
+ // e.g. [mask-type:luminance], [--my-var:10px]. parser.ts already isolates this case
1920
+ // as utility: '' — it's ALSO how a stray, prefix-less negative bracket ("-[10px]",
1921
+ // presumably a typo) parses, which is why this only fires when the arbitrary value
1922
+ // itself contains a "property:value" pair; anything else (no colon) still resolves
1923
+ // to null exactly as before this resolver existed, rather than guessing a property.
1924
+ //
1925
+ // Only the FIRST colon splits property from value — the value itself may contain
1926
+ // more colons (a URL's "http://…") and must stay intact: [background:url(http://x/a.png)].
1927
+ // camelToKebab() in resolver.ts is a no-op on an already-kebab-case (or "--"-prefixed
1928
+ // custom property) key, so it can be used as the style object key as-written with no
1929
+ // extra case conversion — it only needs to look like a plausible CSS property name.
1930
+ "": ({ value, isArbitrary, negative }) => {
1931
+ if (!getEffectiveIsWeb() || !isArbitrary || !value) return null;
1932
+ if (negative) return null;
1933
+ const colonIdx = value.indexOf(":");
1934
+ if (colonIdx <= 0) return null;
1935
+ const prop = value.slice(0, colonIdx).trim();
1936
+ const cssValue = value.slice(colonIdx + 1).trim();
1937
+ if (!cssValue || !/^(--[\w-]+|[a-zA-Z-]+)$/.test(prop)) return null;
1938
+ return { [prop]: cssValue };
1939
+ },
1940
+ // ── SVG stroke/fill (web-only) ────────────────────────────────────────────
1941
+ // Native is intentionally excluded: react-native-svg's <Path>/<Circle>/etc.
1942
+ // take stroke/fill as component PROPS, not style entries, so there's no
1943
+ // reliable way to apply a resolved color through the `style` prop there.
1944
+ // `stroke-{n}` sets strokeWidth (a plain number, unlike a color) — same
1945
+ // color-first-then-numeric-fallback disambiguation `border` uses above.
1946
+ stroke: ({ value, isArbitrary }, { colors }) => {
1947
+ if (!getEffectiveIsWeb()) return null;
1948
+ if (!value) return null;
1949
+ if (value === "none") return { stroke: "none" };
1950
+ if (isArbitrary) {
1951
+ const w = toNativeValue(value);
1952
+ return typeof w === "number" ? { strokeWidth: w } : { stroke: value };
1953
+ }
1954
+ const color = resolveColor(value, colors, false);
1955
+ if (color) return { stroke: color };
1956
+ const n = parseFloat(value);
1957
+ return isNaN(n) ? null : { strokeWidth: n };
1958
+ },
1959
+ fill: ({ value, isArbitrary }, { colors }) => {
1960
+ if (!getEffectiveIsWeb()) return null;
1961
+ if (!value) return null;
1962
+ if (value === "none") return { fill: "none" };
1963
+ if (isArbitrary) return { fill: value };
1964
+ const color = resolveColor(value, colors, false);
1965
+ return color ? { fill: color } : null;
1966
+ },
1967
+ // ── Text shadow (cross-platform: iOS, Android, web) ─────────────────────
1968
+ // Named presets give cross-platform shadow; arbitrary is web-only.
1969
+ "text-shadow": ({ value, isArbitrary }, { colors }) => {
1970
+ if (value === "none") {
1971
+ return getEffectiveIsWeb() ? { textShadow: "none" } : { textShadowColor: "transparent", textShadowOffset: { width: 0, height: 0 }, textShadowRadius: 0 };
1972
+ }
1973
+ const webPresets = {
1974
+ "": "0 2px 4px rgba(0,0,0,0.3)",
1975
+ sm: "0 1px 2px rgba(0,0,0,0.3)",
1976
+ md: "0 4px 6px rgba(0,0,0,0.3)",
1977
+ lg: "0 8px 16px rgba(0,0,0,0.5)",
1978
+ xl: "0 16px 32px rgba(0,0,0,0.5)"
1979
+ };
1980
+ const nativePresets = {
1981
+ "": { textShadowColor: "rgba(0,0,0,0.3)", textShadowOffset: { width: 0, height: 2 }, textShadowRadius: 4 },
1982
+ sm: { textShadowColor: "rgba(0,0,0,0.3)", textShadowOffset: { width: 0, height: 1 }, textShadowRadius: 2 },
1983
+ md: { textShadowColor: "rgba(0,0,0,0.3)", textShadowOffset: { width: 0, height: 4 }, textShadowRadius: 6 },
1984
+ lg: { textShadowColor: "rgba(0,0,0,0.5)", textShadowOffset: { width: 0, height: 8 }, textShadowRadius: 16 },
1985
+ xl: { textShadowColor: "rgba(0,0,0,0.5)", textShadowOffset: { width: 0, height: 16 }, textShadowRadius: 32 }
1986
+ };
1987
+ if (value in webPresets) {
1988
+ if (getEffectiveIsWeb()) return { textShadow: webPresets[value] };
1989
+ return nativePresets[value] ?? null;
1990
+ }
1991
+ if (isArbitrary) {
1992
+ return getEffectiveIsWeb() ? { textShadow: value.replace(/_/g, " ") } : null;
1993
+ }
1994
+ const color = resolveColor(value, colors, false);
1995
+ if (color) {
1996
+ return getEffectiveIsWeb() ? { textShadow: `0 2px 4px ${color}` } : { textShadowColor: color };
1997
+ }
1998
+ return null;
1999
+ }
2000
+ };
2001
+
2002
+ // src/core/utilities.ts
2003
+ var RESOLVERS = {
2004
+ ...colorResolvers,
2005
+ ...spacingResolvers,
2006
+ ...borderResolvers,
2007
+ ...layoutResolvers,
2008
+ ...typographyResolvers,
2009
+ ...filterResolvers,
2010
+ ...transformResolvers,
2011
+ ...effectResolvers,
2012
+ ...miscResolvers
2013
+ };
2014
+ var NAMED_GROUP_PEER_MARKER_RE = /^(group|peer)\/.+$/;
2015
+ function resolveUtility(parsed, theme) {
2016
+ if (!parsed.value) {
2017
+ if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
2018
+ if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
2019
+ if (NAMED_GROUP_PEER_MARKER_RE.test(parsed.utility)) return getEffectiveIsWeb() ? {} : null;
2020
+ }
2021
+ const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
2022
+ if (resolver) return resolver(parsed, theme);
2023
+ return null;
2024
+ }
2025
+ var PLUGIN_STANDALONE = {};
2026
+ var PLUGIN_RESOLVERS = {};
2027
+ function clearPluginUtilities() {
2028
+ for (const key of Object.keys(PLUGIN_STANDALONE)) delete PLUGIN_STANDALONE[key];
2029
+ for (const key of Object.keys(PLUGIN_RESOLVERS)) delete PLUGIN_RESOLVERS[key];
2030
+ _sortedPrefixes = null;
2031
+ _standaloneNames = null;
2032
+ }
2033
+ function getPluginStandaloneMap() {
2034
+ return PLUGIN_STANDALONE;
2035
+ }
2036
+ function isKnownUtility(utility) {
2037
+ return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS || NAMED_GROUP_PEER_MARKER_RE.test(utility);
2038
+ }
2039
+ var _sortedPrefixes = null;
2040
+ var _standaloneNames = null;
2041
+ function getBuiltinUtilityPrefixes() {
2042
+ if (!_sortedPrefixes) {
2043
+ const all = [.../* @__PURE__ */ new Set([...Object.keys(RESOLVERS), ...Object.keys(PLUGIN_RESOLVERS)])];
2044
+ _sortedPrefixes = all.sort((a, b) => b.length - a.length);
2045
+ }
2046
+ return _sortedPrefixes;
2047
+ }
2048
+ function getBuiltinStandaloneNames() {
2049
+ if (!_standaloneNames) {
2050
+ _standaloneNames = /* @__PURE__ */ new Set([...Object.keys(getStandalone()), ...Object.keys(PLUGIN_STANDALONE)]);
2051
+ }
2052
+ return _standaloneNames;
2053
+ }
2054
+
2055
+ // src/core/parser.ts
2056
+ var UNSAFE_ARBITRARY_VALUE = /[{};\\]|\/\*|\*\//;
2057
+ function isSafeArbitraryValue(value) {
2058
+ if (!UNSAFE_ARBITRARY_VALUE.test(value)) return true;
2059
+ if (process.env.NODE_ENV !== "production") {
2060
+ kbachWarn(`Unsafe arbitrary value rejected: "${value}"`);
2061
+ }
2062
+ return false;
2063
+ }
2064
+ function parseClass(className) {
2065
+ const trimmed = className.trim();
2066
+ if (!trimmed) return null;
2067
+ const modifiers = [];
2068
+ let remaining = trimmed;
2069
+ while (true) {
2070
+ const colonIdx = findOuterColon(remaining);
2071
+ if (colonIdx === -1) break;
2072
+ const candidate = remaining.slice(0, colonIdx);
2073
+ if (!isKnownModifier(candidate)) break;
2074
+ modifiers.push(candidate);
2075
+ remaining = remaining.slice(colonIdx + 1);
2076
+ }
2077
+ let important = false;
2078
+ if (remaining.startsWith("!") && remaining.length > 1) {
2079
+ important = true;
2080
+ remaining = remaining.slice(1);
2081
+ }
2082
+ let negative = false;
2083
+ if (remaining.startsWith("-") && remaining.length > 1 && remaining[1] !== "-") {
2084
+ negative = true;
2085
+ remaining = remaining.slice(1);
2086
+ }
2087
+ let bracketDepth = 0;
2088
+ let bracketStart = -1;
2089
+ for (let i = remaining.length - 1; i >= 0; i--) {
2090
+ if (remaining[i] === "]") bracketDepth++;
2091
+ else if (remaining[i] === "[") {
2092
+ bracketDepth--;
2093
+ if (bracketDepth === 0) {
2094
+ bracketStart = i;
2095
+ break;
2096
+ }
2097
+ }
2098
+ }
2099
+ if (bracketStart > 0 && remaining[bracketStart - 1] === "-" && remaining.endsWith("]")) {
2100
+ const utility = remaining.slice(0, bracketStart - 1);
2101
+ const value = remaining.slice(bracketStart + 1, -1).replace(/_/g, " ");
2102
+ if (!isSafeArbitraryValue(value)) return null;
2103
+ return { original: trimmed, modifiers, negative, important, utility, value, isArbitrary: true };
2104
+ }
2105
+ if (remaining.endsWith("]") && bracketStart === -1) {
2106
+ if (process.env.NODE_ENV !== "production") {
2107
+ kbachWarn(`Unbalanced brackets: "${trimmed}"`);
2108
+ }
2109
+ }
2110
+ if (remaining.startsWith("[") && remaining.endsWith("]")) {
2111
+ const value = remaining.slice(1, -1).replace(/_/g, " ");
2112
+ if (!isSafeArbitraryValue(value)) return null;
2113
+ return { original: trimmed, modifiers, negative, important, utility: "", value, isArbitrary: true };
2114
+ }
2115
+ if (getBuiltinStandaloneNames().has(remaining)) {
2116
+ return { original: trimmed, modifiers, negative, important, utility: remaining, value: "", isArbitrary: false };
2117
+ }
2118
+ for (const prefix of getBuiltinUtilityPrefixes()) {
2119
+ if (remaining === prefix) {
2120
+ return { original: trimmed, modifiers, negative, important, utility: prefix, value: "", isArbitrary: false };
2121
+ }
2122
+ if (remaining.startsWith(prefix + "-")) {
2123
+ const value = remaining.slice(prefix.length + 1);
2124
+ return { original: trimmed, modifiers, negative, important, utility: prefix, value, isArbitrary: false };
2125
+ }
2126
+ }
2127
+ const firstDash = remaining.indexOf("-");
2128
+ if (firstDash > 0) {
2129
+ return {
2130
+ original: trimmed,
2131
+ modifiers,
2132
+ negative,
2133
+ important,
2134
+ utility: remaining.slice(0, firstDash),
2135
+ value: remaining.slice(firstDash + 1),
2136
+ isArbitrary: false
2137
+ };
2138
+ }
2139
+ return { original: trimmed, modifiers, negative, important, utility: remaining, value: "", isArbitrary: false };
2140
+ }
2141
+ function splitClassTokens(classString) {
2142
+ const tokens = [];
2143
+ let current = "";
2144
+ let depth = 0;
2145
+ let parenDepth = 0;
2146
+ for (let i = 0; i < classString.length; i++) {
2147
+ const ch = classString[i];
2148
+ if (ch === "[") {
2149
+ depth++;
2150
+ current += ch;
2151
+ continue;
2152
+ }
2153
+ if (ch === "]") {
2154
+ depth--;
2155
+ current += ch;
2156
+ continue;
2157
+ }
2158
+ if (ch === "(") {
2159
+ parenDepth++;
2160
+ current += ch;
2161
+ continue;
2162
+ }
2163
+ if (ch === ")") {
2164
+ parenDepth--;
2165
+ current += ch;
2166
+ continue;
2167
+ }
2168
+ if (/\s/.test(ch)) {
2169
+ if (depth === 0 && parenDepth === 0) {
2170
+ if (current) {
2171
+ tokens.push(current);
2172
+ current = "";
2173
+ }
2174
+ }
2175
+ } else {
2176
+ current += ch;
2177
+ }
2178
+ }
2179
+ if (current) tokens.push(current);
2180
+ return tokens;
2181
+ }
2182
+ function normalizeClassString(classString) {
2183
+ return splitClassTokens(classString).join(" ");
2184
+ }
2185
+ function parseClasses(classString) {
2186
+ const results = [];
2187
+ for (const token of splitClassTokens(classString)) {
2188
+ const parsed = parseClass(token);
2189
+ if (parsed) results.push(parsed);
2190
+ }
2191
+ return results;
2192
+ }
2193
+ function findOuterColon(s) {
2194
+ let depth = 0;
2195
+ for (let i = 0; i < s.length; i++) {
2196
+ const ch = s[i];
2197
+ if (ch === "[") depth++;
2198
+ else if (ch === "]") depth--;
2199
+ else if (ch === ":" && depth === 0) return i;
2200
+ }
2201
+ return -1;
2202
+ }
2203
+
2204
+ // src/core/modeAwareColors.ts
2205
+ var _modeAwareMapCache = /* @__PURE__ */ new WeakMap();
2206
+ function buildModeAwareMap(colors) {
2207
+ const map = /* @__PURE__ */ new Map();
2208
+ for (const [name, entry] of Object.entries(colors)) {
2209
+ if (isModeAwareColor(entry)) {
2210
+ map.set(name, entry);
2211
+ } else if (entry && typeof entry === "object") {
2212
+ for (const [shade, val] of Object.entries(entry)) {
2213
+ if (isModeAwareColor(val)) map.set(`${name}-${shade}`, val);
2214
+ }
2215
+ }
2216
+ }
2217
+ return map;
2218
+ }
2219
+ function getModeAwareMap(colors) {
2220
+ let map = _modeAwareMapCache.get(colors);
2221
+ if (!map) {
2222
+ map = buildModeAwareMap(colors);
2223
+ _modeAwareMapCache.set(colors, map);
2224
+ }
2225
+ return map;
2226
+ }
2227
+ function expandModeAwareColorClasses(classString, colors) {
2228
+ const map = getModeAwareMap(colors);
2229
+ if (map.size === 0) return classString;
2230
+ const tokens = splitClassTokens(classString);
2231
+ let changed = false;
2232
+ const out = [];
2233
+ for (const token of tokens) {
2234
+ const parsed = parseClass(token);
2235
+ if (!parsed || parsed.isArbitrary) {
2236
+ out.push(token);
2237
+ continue;
2238
+ }
2239
+ const slashIdx = parsed.value.indexOf("/");
2240
+ const colorPart = slashIdx > 0 ? parsed.value.slice(0, slashIdx) : parsed.value;
2241
+ const opacitySuffix = slashIdx > 0 ? parsed.value.slice(slashIdx) : "";
2242
+ const pair = map.get(colorPart);
2243
+ if (!pair) {
2244
+ out.push(token);
2245
+ continue;
2246
+ }
2247
+ changed = true;
2248
+ const bang = parsed.important ? "!" : "";
2249
+ const modPrefix = parsed.modifiers.map((m) => `${m}:`).join("");
2250
+ const explicitScheme = parsed.modifiers.map((m) => getModifier(m)?.darkScheme).find((s) => s);
2251
+ if (explicitScheme) {
2252
+ const side = explicitScheme === "dark" ? pair.dark : pair.light;
2253
+ out.push(`${bang}${modPrefix}${parsed.utility}-[${side}]${opacitySuffix}`);
2254
+ continue;
2255
+ }
2256
+ out.push(`${bang}${modPrefix}${parsed.utility}-[${pair.light}]${opacitySuffix}`);
2257
+ out.push(`${bang}dark:${modPrefix}${parsed.utility}-[${pair.dark}]${opacitySuffix}`);
2258
+ }
2259
+ return changed ? out.join(" ") : classString;
2260
+ }
2261
+
2262
+ // src/core/theme.ts
2263
+ var defaultColors = {
2264
+ transparent: "transparent",
2265
+ current: "currentColor",
2266
+ black: "#000000",
2267
+ white: "#ffffff",
2268
+ slate: {
2269
+ 1: "#f8fafc",
2270
+ 2: "#f1f5f9",
2271
+ 3: "#e2e8f0",
2272
+ 4: "#cbd5e1",
2273
+ 5: "#94a3b8",
2274
+ 6: "#64748b",
2275
+ 7: "#475569",
2276
+ 8: "#334155",
2277
+ 9: "#1e293b",
2278
+ 10: "#0f172a",
2279
+ 11: "#020617",
2280
+ 12: "#01020a"
2281
+ },
2282
+ gray: {
2283
+ 1: "#f9fafb",
2284
+ 2: "#f3f4f6",
2285
+ 3: "#e5e7eb",
2286
+ 4: "#d1d5db",
2287
+ 5: "#9ca3af",
2288
+ 6: "#6b7280",
2289
+ 7: "#4b5563",
2290
+ 8: "#374151",
2291
+ 9: "#1f2937",
2292
+ 10: "#111827",
2293
+ 11: "#030712",
2294
+ 12: "#020409"
2295
+ },
2296
+ zinc: {
2297
+ 1: "#fafafa",
2298
+ 2: "#f4f4f5",
2299
+ 3: "#e4e4e7",
2300
+ 4: "#d4d4d8",
2301
+ 5: "#a1a1aa",
2302
+ 6: "#71717a",
2303
+ 7: "#52525b",
2304
+ 8: "#3f3f46",
2305
+ 9: "#27272a",
2306
+ 10: "#18181b",
2307
+ 11: "#09090b",
2308
+ 12: "#050506"
2309
+ },
2310
+ neutral: {
2311
+ 1: "#fafafa",
2312
+ 2: "#f5f5f5",
2313
+ 3: "#e5e5e5",
2314
+ 4: "#d4d4d4",
2315
+ 5: "#a3a3a3",
2316
+ 6: "#737373",
2317
+ 7: "#525252",
2318
+ 8: "#404040",
2319
+ 9: "#262626",
2320
+ 10: "#171717",
2321
+ 11: "#0a0a0a",
2322
+ 12: "#050505"
2323
+ },
2324
+ stone: {
2325
+ 1: "#fafaf9",
2326
+ 2: "#f5f5f4",
2327
+ 3: "#e7e5e4",
2328
+ 4: "#d6d3d1",
2329
+ 5: "#a8a29e",
2330
+ 6: "#78716c",
2331
+ 7: "#57534e",
2332
+ 8: "#44403c",
2333
+ 9: "#292524",
2334
+ 10: "#1c1917",
2335
+ 11: "#0c0a09",
2336
+ 12: "#070605"
2337
+ },
2338
+ red: {
2339
+ 1: "#fef2f2",
2340
+ 2: "#fee2e2",
2341
+ 3: "#fecaca",
2342
+ 4: "#fca5a5",
2343
+ 5: "#f87171",
2344
+ 6: "#ef4444",
2345
+ 7: "#dc2626",
2346
+ 8: "#b91c1c",
2347
+ 9: "#991b1b",
2348
+ 10: "#7f1d1d",
2349
+ 11: "#450a0a",
2350
+ 12: "#280606"
2351
+ },
2352
+ orange: {
2353
+ 1: "#fff7ed",
2354
+ 2: "#ffedd5",
2355
+ 3: "#fed7aa",
2356
+ 4: "#fdba74",
2357
+ 5: "#fb923c",
2358
+ 6: "#f97316",
2359
+ 7: "#ea580c",
2360
+ 8: "#c2410c",
2361
+ 9: "#9a3412",
2362
+ 10: "#7c2d12",
2363
+ 11: "#431407",
2364
+ 12: "#270c04"
2365
+ },
2366
+ amber: {
2367
+ 1: "#fffbeb",
2368
+ 2: "#fef3c7",
2369
+ 3: "#fde68a",
2370
+ 4: "#fcd34d",
2371
+ 5: "#fbbf24",
2372
+ 6: "#f59e0b",
2373
+ 7: "#d97706",
2374
+ 8: "#b45309",
2375
+ 9: "#92400e",
2376
+ 10: "#78350f",
2377
+ 11: "#451a03",
2378
+ 12: "#291002"
2379
+ },
2380
+ yellow: {
2381
+ 1: "#fefce8",
2382
+ 2: "#fef9c3",
2383
+ 3: "#fef08a",
2384
+ 4: "#fde047",
2385
+ 5: "#facc15",
2386
+ 6: "#eab308",
2387
+ 7: "#ca8a04",
2388
+ 8: "#a16207",
2389
+ 9: "#854d0e",
2390
+ 10: "#713f12",
2391
+ 11: "#422006",
2392
+ 12: "#271304"
2393
+ },
2394
+ lime: {
2395
+ 1: "#f7fee7",
2396
+ 2: "#ecfccb",
2397
+ 3: "#d9f99d",
2398
+ 4: "#bef264",
2399
+ 5: "#a3e635",
2400
+ 6: "#84cc16",
2401
+ 7: "#65a30d",
2402
+ 8: "#4d7c0f",
2403
+ 9: "#3f6212",
2404
+ 10: "#365314",
2405
+ 11: "#1a2e05",
2406
+ 12: "#0f1b03"
2407
+ },
2408
+ green: {
2409
+ 1: "#f0fdf4",
2410
+ 2: "#dcfce7",
2411
+ 3: "#bbf7d0",
2412
+ 4: "#86efac",
2413
+ 5: "#4ade80",
2414
+ 6: "#22c55e",
2415
+ 7: "#16a34a",
2416
+ 8: "#15803d",
2417
+ 9: "#166534",
2418
+ 10: "#14532d",
2419
+ 11: "#052e16",
2420
+ 12: "#031b0d"
2421
+ },
2422
+ emerald: {
2423
+ 1: "#ecfdf5",
2424
+ 2: "#d1fae5",
2425
+ 3: "#a7f3d0",
2426
+ 4: "#6ee7b7",
2427
+ 5: "#34d399",
2428
+ 6: "#10b981",
2429
+ 7: "#059669",
2430
+ 8: "#047857",
2431
+ 9: "#065f46",
2432
+ 10: "#064e3b",
2433
+ 11: "#022c22",
2434
+ 12: "#011a14"
2435
+ },
2436
+ teal: {
2437
+ 1: "#f0fdfa",
2438
+ 2: "#ccfbf1",
2439
+ 3: "#99f6e4",
2440
+ 4: "#5eead4",
2441
+ 5: "#2dd4bf",
2442
+ 6: "#14b8a6",
2443
+ 7: "#0d9488",
2444
+ 8: "#0f766e",
2445
+ 9: "#115e59",
2446
+ 10: "#134e4a",
2447
+ 11: "#042f2e",
2448
+ 12: "#021c1b"
2449
+ },
2450
+ cyan: {
2451
+ 1: "#ecfeff",
2452
+ 2: "#cffafe",
2453
+ 3: "#a5f3fc",
2454
+ 4: "#67e8f9",
2455
+ 5: "#22d3ee",
2456
+ 6: "#06b6d4",
2457
+ 7: "#0891b2",
2458
+ 8: "#0e7490",
2459
+ 9: "#155e75",
2460
+ 10: "#164e63",
2461
+ 11: "#083344",
2462
+ 12: "#041e28"
2463
+ },
2464
+ sky: {
2465
+ 1: "#f0f9ff",
2466
+ 2: "#e0f2fe",
2467
+ 3: "#bae6fd",
2468
+ 4: "#7dd3fc",
2469
+ 5: "#38bdf8",
2470
+ 6: "#0ea5e9",
2471
+ 7: "#0284c7",
2472
+ 8: "#0369a1",
2473
+ 9: "#075985",
2474
+ 10: "#0c4a6e",
2475
+ 11: "#082f49",
2476
+ 12: "#041b2b"
2477
+ },
2478
+ blue: {
2479
+ 1: "#eff6ff",
2480
+ 2: "#dbeafe",
2481
+ 3: "#bfdbfe",
2482
+ 4: "#93c5fd",
2483
+ 5: "#60a5fa",
2484
+ 6: "#3b82f6",
2485
+ 7: "#2563eb",
2486
+ 8: "#1d4ed8",
2487
+ 9: "#1e40af",
2488
+ 10: "#1e3a8a",
2489
+ 11: "#172554",
2490
+ 12: "#0d1633"
2491
+ },
2492
+ indigo: {
2493
+ 1: "#eef2ff",
2494
+ 2: "#e0e7ff",
2495
+ 3: "#c7d2fe",
2496
+ 4: "#a5b4fc",
2497
+ 5: "#818cf8",
2498
+ 6: "#6366f1",
2499
+ 7: "#4f46e5",
2500
+ 8: "#4338ca",
2501
+ 9: "#3730a3",
2502
+ 10: "#312e81",
2503
+ 11: "#1e1b4b",
2504
+ 12: "#12102d"
2505
+ },
2506
+ violet: {
2507
+ 1: "#f5f3ff",
2508
+ 2: "#ede9fe",
2509
+ 3: "#ddd6fe",
2510
+ 4: "#c4b5fd",
2511
+ 5: "#a78bfa",
2512
+ 6: "#8b5cf6",
2513
+ 7: "#7c3aed",
2514
+ 8: "#6d28d9",
2515
+ 9: "#5b21b6",
2516
+ 10: "#4c1d95",
2517
+ 11: "#2e1065",
2518
+ 12: "#1c0a3d"
2519
+ },
2520
+ purple: {
2521
+ 1: "#faf5ff",
2522
+ 2: "#f3e8ff",
2523
+ 3: "#e9d5ff",
2524
+ 4: "#d8b4fe",
2525
+ 5: "#c084fc",
2526
+ 6: "#a855f7",
2527
+ 7: "#9333ea",
2528
+ 8: "#7e22ce",
2529
+ 9: "#6b21a8",
2530
+ 10: "#581c87",
2531
+ 11: "#3b0764",
2532
+ 12: "#23043c"
2533
+ },
2534
+ fuchsia: {
2535
+ 1: "#fdf4ff",
2536
+ 2: "#fae8ff",
2537
+ 3: "#f5d0fe",
2538
+ 4: "#f0abfc",
2539
+ 5: "#e879f9",
2540
+ 6: "#d946ef",
2541
+ 7: "#c026d3",
2542
+ 8: "#a21caf",
2543
+ 9: "#86198f",
2544
+ 10: "#701a75",
2545
+ 11: "#4a044e",
2546
+ 12: "#2d022f"
2547
+ },
2548
+ pink: {
2549
+ 1: "#fdf2f8",
2550
+ 2: "#fce7f3",
2551
+ 3: "#fbcfe8",
2552
+ 4: "#f9a8d4",
2553
+ 5: "#f472b6",
2554
+ 6: "#ec4899",
2555
+ 7: "#db2777",
2556
+ 8: "#be185d",
2557
+ 9: "#9d174d",
2558
+ 10: "#831843",
2559
+ 11: "#500724",
2560
+ 12: "#300415"
2561
+ },
2562
+ rose: {
2563
+ 1: "#fff1f2",
2564
+ 2: "#ffe4e6",
2565
+ 3: "#fecdd3",
2566
+ 4: "#fda4af",
2567
+ 5: "#fb7185",
2568
+ 6: "#f43f5e",
2569
+ 7: "#e11d48",
2570
+ 8: "#be123c",
2571
+ 9: "#9f1239",
2572
+ 10: "#881337",
2573
+ 11: "#4c0519",
2574
+ 12: "#2d030e"
2575
+ }
2576
+ };
2577
+ var defaultTheme = {
2578
+ colors: defaultColors,
2579
+ // 1 unit = 4px
2580
+ spacing: {
2581
+ px: 1,
2582
+ 0: 0,
2583
+ "0.5": 2,
2584
+ 1: 4,
2585
+ "1.5": 6,
2586
+ 2: 8,
2587
+ "2.5": 10,
2588
+ 3: 12,
2589
+ "3.5": 14,
2590
+ 4: 16,
2591
+ 5: 20,
2592
+ 6: 24,
2593
+ 7: 28,
2594
+ 8: 32,
2595
+ 9: 36,
2596
+ 10: 40,
2597
+ 11: 44,
2598
+ 12: 48,
2599
+ 14: 56,
2600
+ 16: 64,
2601
+ 20: 80,
2602
+ 24: 96,
2603
+ 28: 112,
2604
+ 32: 128,
2605
+ 36: 144,
2606
+ 40: 160,
2607
+ 44: 176,
2608
+ 48: 192,
2609
+ 52: 208,
2610
+ 56: 224,
2611
+ 60: 240,
2612
+ 64: 256,
2613
+ 72: 288,
2614
+ 80: 320,
2615
+ 96: 384,
2616
+ auto: "auto",
2617
+ full: "100%",
2618
+ "1/2": "50%",
2619
+ "1/3": "33.333333%",
2620
+ "2/3": "66.666667%",
2621
+ "1/4": "25%",
2622
+ "3/4": "75%",
2623
+ // Dynamic viewport unit — see the comment on the 'screen' standalone utilities
2624
+ // in utilities.ts for why dvh beats vh on mobile. Used by h-screen (w-screen
2625
+ // has its own standalone entry so it never reaches this spacing lookup).
2626
+ screen: "100dvh",
2627
+ min: "min-content",
2628
+ max: "max-content",
2629
+ fit: "fit-content"
2630
+ },
2631
+ fontSize: {
2632
+ xs: 12,
2633
+ sm: 14,
2634
+ base: 16,
2635
+ lg: 18,
2636
+ xl: 20,
2637
+ "2xl": 24,
2638
+ "3xl": 30,
2639
+ "4xl": 36,
2640
+ "5xl": 48,
2641
+ "6xl": 60,
2642
+ "7xl": 72,
2643
+ "8xl": 96,
2644
+ "9xl": 128
2645
+ },
2646
+ fontFamily: {
2647
+ sans: "System",
2648
+ mono: "Courier New",
2649
+ serif: "Georgia"
2650
+ },
2651
+ fontWeight: {
2652
+ thin: "100",
2653
+ extralight: "200",
2654
+ light: "300",
2655
+ normal: "400",
2656
+ medium: "500",
2657
+ semibold: "600",
2658
+ bold: "700",
2659
+ extrabold: "800",
2660
+ black: "900"
2661
+ },
2662
+ borderRadius: {
2663
+ none: 0,
2664
+ sm: 2,
2665
+ DEFAULT: 4,
2666
+ md: 6,
2667
+ lg: 8,
2668
+ xl: 12,
2669
+ "2xl": 16,
2670
+ "3xl": 24,
2671
+ full: 9999
2672
+ },
2673
+ borderWidth: {
2674
+ DEFAULT: 1,
2675
+ 0: 0,
2676
+ 2: 2,
2677
+ 4: 4,
2678
+ 8: 8
2679
+ },
2680
+ opacity: {
2681
+ 0: 0,
2682
+ 5: 0.05,
2683
+ 10: 0.1,
2684
+ 15: 0.15,
2685
+ 20: 0.2,
2686
+ 25: 0.25,
2687
+ 30: 0.3,
2688
+ 40: 0.4,
2689
+ 50: 0.5,
2690
+ 60: 0.6,
2691
+ 70: 0.7,
2692
+ 75: 0.75,
2693
+ 80: 0.8,
2694
+ 90: 0.9,
2695
+ 95: 0.95,
2696
+ 100: 1
2697
+ },
2698
+ lineHeight: {
2699
+ none: 1,
2700
+ tight: 1.25,
2701
+ snug: 1.375,
2702
+ normal: 1.5,
2703
+ relaxed: 1.625,
2704
+ loose: 2,
2705
+ // Pixel values stored as strings; styleValueToCSS passes them through, toNativeValue strips the unit
2706
+ 3: "12px",
2707
+ 4: "16px",
2708
+ 5: "20px",
2709
+ 6: "24px",
2710
+ 7: "28px",
2711
+ 8: "32px",
2712
+ 9: "36px",
2713
+ 10: "40px"
2714
+ },
2715
+ letterSpacing: {
2716
+ tighter: -0.8,
2717
+ tight: -0.4,
2718
+ normal: 0,
2719
+ wide: 0.4,
2720
+ wider: 0.8,
2721
+ widest: 1.6
2722
+ },
2723
+ zIndex: {
2724
+ auto: "auto",
2725
+ 0: 0,
2726
+ 10: 10,
2727
+ 20: 20,
2728
+ 30: 30,
2729
+ 40: 40,
2730
+ 50: 50
2731
+ },
2732
+ flex: {
2733
+ 1: 1,
2734
+ auto: "auto",
2735
+ // CSS: flex: auto = 1 1 auto; native: mapped to 1 in resolver
2736
+ initial: "initial",
2737
+ // CSS: flex: initial = 0 1 auto; native: mapped to 1
2738
+ none: "none"
2739
+ // CSS: flex: none = 0 0 auto; native: mapped to 0
2740
+ },
2741
+ shadow: {
2742
+ sm: {
2743
+ shadowColor: "#000",
2744
+ shadowOffset: { width: 0, height: 1 },
2745
+ shadowOpacity: 0.05,
2746
+ shadowRadius: 2,
2747
+ elevation: 1
2748
+ },
2749
+ DEFAULT: {
2750
+ shadowColor: "#000",
2751
+ shadowOffset: { width: 0, height: 2 },
2752
+ shadowOpacity: 0.1,
2753
+ shadowRadius: 4,
2754
+ elevation: 2
2755
+ },
2756
+ md: {
2757
+ shadowColor: "#000",
2758
+ shadowOffset: { width: 0, height: 4 },
2759
+ shadowOpacity: 0.1,
2760
+ shadowRadius: 8,
2761
+ elevation: 3
2762
+ },
2763
+ lg: {
2764
+ shadowColor: "#000",
2765
+ shadowOffset: { width: 0, height: 8 },
2766
+ shadowOpacity: 0.1,
2767
+ shadowRadius: 15,
2768
+ elevation: 4
2769
+ },
2770
+ xl: {
2771
+ shadowColor: "#000",
2772
+ shadowOffset: { width: 0, height: 16 },
2773
+ shadowOpacity: 0.1,
2774
+ shadowRadius: 24,
2775
+ elevation: 6
2776
+ },
2777
+ "2xl": {
2778
+ shadowColor: "#000",
2779
+ shadowOffset: { width: 0, height: 24 },
2780
+ shadowOpacity: 0.25,
2781
+ shadowRadius: 48,
2782
+ elevation: 8
2783
+ },
2784
+ none: {
2785
+ shadowColor: "transparent",
2786
+ shadowOffset: { width: 0, height: 0 },
2787
+ shadowOpacity: 0,
2788
+ shadowRadius: 0,
2789
+ elevation: 0
2790
+ }
2791
+ },
2792
+ screens: {
2793
+ sm: 576,
2794
+ // small tablets / large phones (landscape)
2795
+ md: 768,
2796
+ // tablets (iPad and up)
2797
+ lg: 1024,
2798
+ // large tablets (iPad Pro)
2799
+ xl: 1280,
2800
+ // desktop
2801
+ "2xl": 1536
2802
+ },
2803
+ // Empty by default — the 4 built-in presets (spin/ping/pulse/bounce) are handled
2804
+ // directly in the `animate` resolver, not through this theme table. This exists so
2805
+ // user-defined ones (kbach.config.js theme.extend.keyframes/animation) merge in
2806
+ // alongside them without needing to redeclare the built-ins.
2807
+ keyframes: {},
2808
+ animation: {}
2809
+ };
2810
+
2811
+ // src/core/generateTypesDts.ts
2812
+ var HEADER = `// AUTO-GENERATED by Kbach from your kbach.config.js \u2014 do not edit by hand.
2813
+ // Regenerated automatically every time your dev server / Metro picks up a
2814
+ // change to that file. Safe (and recommended) to add to .gitignore.
2815
+ //
2816
+ // Gives useColors()/useSpacing() full autocomplete and typo-catching for your
2817
+ // custom colors/spacing keys with zero manual setup \u2014 see KbachCustomColors'
2818
+ // doc comment in @kbach/ui if you'd rather hand-author this instead.
2819
+ import '@kbach/ui';
2820
+ `;
2821
+ var IDENT_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
2822
+ function propKey(name) {
2823
+ return IDENT_RE.test(name) ? name : JSON.stringify(name);
2824
+ }
2825
+ function generateKbachTypesDts(theme) {
2826
+ const colorLines = [];
2827
+ for (const [name, value] of Object.entries(theme.colors)) {
2828
+ if (name in defaultColors) continue;
2829
+ const isFlat = typeof value === "string" || isModeAwareColor(value);
2830
+ colorLines.push(` ${propKey(name)}: ${isFlat ? "string" : "ColorScale"};`);
2831
+ }
2832
+ const spacingLines = [];
2833
+ for (const key of Object.keys(theme.spacing)) {
2834
+ if (key in defaultTheme.spacing) continue;
2835
+ spacingLines.push(` ${propKey(key)}: true;`);
2836
+ }
2837
+ if (colorLines.length === 0 && spacingLines.length === 0) return "";
2838
+ const body = [
2839
+ colorLines.length > 0 ? ` interface KbachCustomColors {
2840
+ ${colorLines.join("\n")}
2841
+ }` : null,
2842
+ spacingLines.length > 0 ? ` interface KbachCustomSpacing {
2843
+ ${spacingLines.join("\n")}
2844
+ }` : null
2845
+ ].filter((s) => s !== null).join("\n\n");
2846
+ return `${HEADER}
2847
+ declare module '@kbach/ui' {
2848
+ ${body}
2849
+ }
2850
+ `;
2851
+ }
2852
+
2853
+ // src/core/reset.ts
2854
+ var RESET_STYLE_ID = "kbach-reset";
2855
+ var BASE_RESET = [
2856
+ // border-style: solid means border-N utilities show a visible border without an extra border-solid class.
2857
+ // border-width: 0 keeps all elements borderless by default.
2858
+ "*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor; }",
2859
+ "body { margin: 0; padding: 0; }",
2860
+ "h1, h2, h3, h4, h5, h6 { margin: 0; font-size: inherit; font-weight: inherit; }",
2861
+ "p { margin: 0; }",
2862
+ "a { color: inherit; text-decoration: none; }",
2863
+ "ul, ol { margin: 0; padding: 0; list-style: none; }",
2864
+ "img, video, svg { display: block; max-width: 100%; }",
2865
+ // appearance: none is deliberately NOT applied to checkbox/radio/select below —
2866
+ // stripping it hides their native checkmark/arrow with nothing rendered in its
2867
+ // place, leaving an invisible checkbox or an arrow-less <select> that looks like
2868
+ // plain text. Text-like inputs, textarea, and button don't have that problem
2869
+ // (their native chrome is just a skin around content utilities can fully
2870
+ // restyle), so they keep the blank-canvas treatment.
2871
+ // :where() wraps the :not() exclusions so they contribute ZERO specificity
2872
+ // (unlike a bare `input:not([type='checkbox']):not([type='radio'])`, whose
2873
+ // two :not([attr]) clauses each add a class-level specificity point — (0,2,1)
2874
+ // total, MORE than any single utility class (0,1,0)). Without :where(), this
2875
+ // reset's `color: inherit` always won the cascade over a text-* color
2876
+ // utility applied directly to a <input>/<textarea> regardless of source
2877
+ // order, since author rules only override on a tie or higher specificity —
2878
+ // confirmed on a real app: a TextInput's own text color utility resolved
2879
+ // and injected correctly, class and all, but silently never painted.
2880
+ "input:where(:not([type='checkbox']):not([type='radio'])), textarea { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
2881
+ // Native checkbox/radio/range still get typography + spacing normalized, and
2882
+ // accent-color re-themes their native indicator to the current text color
2883
+ // instead of the browser/OS default blue, so they stay on-brand without
2884
+ // needing to be rebuilt from scratch.
2885
+ "input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
2886
+ // select keeps its native chrome (border/background/arrow are all part of the
2887
+ // same OS-drawn widget that appearance: none would blank out) — only typography
2888
+ // and spacing are normalized so it still matches surrounding text.
2889
+ "select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
2890
+ "button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
2891
+ "button, [role='button'] { cursor: pointer; }",
2892
+ ":disabled { cursor: default; }",
2893
+ "textarea { resize: vertical; }",
2894
+ // Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
2895
+ // normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
2896
+ "::placeholder { opacity: 1; }",
2897
+ "input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
2898
+ "input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
2899
+ "fieldset { padding: 0; margin: 0; }",
2900
+ "table { border-collapse: collapse; border-spacing: 0; }"
2901
+ ].join("\n");
2902
+
2903
+ // src/core/responsiveStore.ts
2904
+ var store = { width: 0, notifiedWidth: 0, screens: {}, listeners: /* @__PURE__ */ new Set() };
2905
+ function syncGlobalWidth(width) {
2906
+ store.width = width;
2907
+ }
2908
+ function syncGlobalScreens(screens) {
2909
+ store.screens = screens;
2910
+ }
2911
+ function getGlobalScreens() {
2912
+ return store.screens;
2913
+ }
2914
+ function setGlobalWidth(width) {
2915
+ store.width = width;
2916
+ if (store.notifiedWidth === width) return;
2917
+ store.notifiedWidth = width;
2918
+ for (const l of store.listeners) l();
2919
+ }
2920
+ function getGlobalWidth() {
2921
+ return store.width;
2922
+ }
2923
+ function subscribeGlobalWidth(listener) {
2924
+ store.listeners.add(listener);
2925
+ return () => store.listeners.delete(listener);
2926
+ }
2927
+ function getActiveBreakpoints(width, screens) {
2928
+ const w = width ?? store.width;
2929
+ const s = screens ?? store.screens;
2930
+ const active = /* @__PURE__ */ new Set();
2931
+ for (const [name, minW] of Object.entries(s)) {
2932
+ if (typeof minW === "number" && w >= minW) active.add(name);
2933
+ }
2934
+ return active;
2935
+ }
2936
+
2937
+ // src/core/resolver.ts
2938
+ var _themeCache = /* @__PURE__ */ new WeakMap();
2939
+ function getThemeCache(theme) {
2940
+ let cache = _themeCache.get(theme);
2941
+ if (!cache) {
2942
+ cache = new LRUCache(1e4);
2943
+ _themeCache.set(theme, cache);
2944
+ }
2945
+ return cache;
2946
+ }
2947
+ var _sortCache = /* @__PURE__ */ new WeakMap();
2948
+ function getSortedEntries(resolved) {
2949
+ let sorted = _sortCache.get(resolved);
2950
+ if (!sorted) {
2951
+ const entries = Object.entries(resolved);
2952
+ entries.sort((a, b) => {
2953
+ const pa = a[0] === "base" ? -1 : a[0].split(":").length;
2954
+ const pb = b[0] === "base" ? -1 : b[0].split(":").length;
2955
+ return pa - pb;
2956
+ });
2957
+ sorted = entries;
2958
+ _sortCache.set(resolved, sorted);
2959
+ }
2960
+ return sorted;
2961
+ }
2962
+ var _defaultFontFamily;
2963
+ function setDefaultFontFamily(font) {
2964
+ _defaultFontFamily = font;
2965
+ }
2966
+ function getDefaultFontFamily() {
2967
+ return _defaultFontFamily;
2968
+ }
2969
+ var _styleEl = null;
2970
+ var _ruleIndexByKey = /* @__PURE__ */ new Map();
2971
+ var _sheetKeys = [];
2972
+ var _ruleOrderByKey = /* @__PURE__ */ new Map();
2973
+ function findInsertionIndex(order) {
2974
+ let lo = 0, hi = _sheetKeys.length;
2975
+ while (lo < hi) {
2976
+ const mid = lo + hi >>> 1;
2977
+ if (_ruleOrderByKey.get(_sheetKeys[mid]) > order) hi = mid;
2978
+ else lo = mid + 1;
2979
+ }
2980
+ return lo;
2981
+ }
2982
+ function evictInjectedRule(rule) {
2983
+ const idx = _ruleIndexByKey.get(rule);
2984
+ _ruleIndexByKey.delete(rule);
2985
+ _ruleOrderByKey.delete(rule);
2986
+ if (idx === void 0) return;
2987
+ const sheet = _styleEl?.sheet;
2988
+ if (!sheet) return;
2989
+ try {
2990
+ sheet.deleteRule(idx);
2991
+ } catch {
2992
+ return;
2993
+ }
2994
+ _sheetKeys.splice(idx, 1);
2995
+ for (const [key, i] of _ruleIndexByKey) {
2996
+ if (i > idx) _ruleIndexByKey.set(key, i - 1);
2997
+ }
2998
+ }
2999
+ var _injectedRules = new LRUCache(5e4, evictInjectedRule);
3000
+ var _runtimeCSSDisabled = false;
3001
+ function disableRuntimeCSS() {
3002
+ _runtimeCSSDisabled = true;
3003
+ }
3004
+ function isRuntimeCSSDisabled() {
3005
+ return _runtimeCSSDisabled;
3006
+ }
3007
+ function getStyleEl() {
3008
+ if (_styleEl) return _styleEl;
3009
+ _styleEl = document.createElement("style");
3010
+ _styleEl.setAttribute("data-kbach", "");
3011
+ document.head.appendChild(_styleEl);
3012
+ return _styleEl;
3013
+ }
3014
+ function injectRule(rule, order) {
3015
+ if (isRuntimeCSSDisabled()) return;
3016
+ if (_injectedRules.get(rule)) return;
3017
+ try {
3018
+ const sheet = getStyleEl().sheet;
3019
+ if (sheet) {
3020
+ const idx = findInsertionIndex(order);
3021
+ sheet.insertRule(rule, idx);
3022
+ _sheetKeys.splice(idx, 0, rule);
3023
+ for (const [key, i] of _ruleIndexByKey) {
3024
+ if (i >= idx) _ruleIndexByKey.set(key, i + 1);
3025
+ }
3026
+ _ruleIndexByKey.set(rule, idx);
3027
+ _ruleOrderByKey.set(rule, order);
3028
+ _injectedRules.set(rule, true);
3029
+ }
3030
+ } catch {
3031
+ }
3032
+ }
3033
+ var _globalStyleEl = null;
3034
+ function getGlobalStyleEl() {
3035
+ if (_globalStyleEl) return _globalStyleEl;
3036
+ _globalStyleEl = document.createElement("style");
3037
+ _globalStyleEl.setAttribute("data-kbach-global", "");
3038
+ document.head.insertBefore(_globalStyleEl, getStyleEl());
3039
+ return _globalStyleEl;
3040
+ }
3041
+ function injectGlobalStyles(theme) {
3042
+ if (typeof document === "undefined" || isRuntimeCSSDisabled()) return;
3043
+ const rules = document.getElementById(RESET_STYLE_ID) ? [] : [BASE_RESET];
3044
+ const ff = theme.fontFamily;
3045
+ if (ff?.sans && ff.sans !== "System") {
3046
+ const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
3047
+ rules.push(`body { font-family: ${family}; }`);
3048
+ }
3049
+ getGlobalStyleEl().textContent = rules.join("\n");
3050
+ }
3051
+ function buildDivideDecls(styles, forceImportant) {
3052
+ const imp = forceImportant ? " !important" : "";
3053
+ const parts = [];
3054
+ if ("__divideX" in styles) {
3055
+ const w = Number(styles.__divideX);
3056
+ parts.push(`border-left-width: ${w}px${imp}`, `border-right-width: 0px${imp}`);
3057
+ }
3058
+ if ("__divideY" in styles) {
3059
+ const w = Number(styles.__divideY);
3060
+ parts.push(`border-top-width: ${w}px${imp}`, `border-bottom-width: 0px${imp}`);
3061
+ }
3062
+ if ("__divideColor" in styles) parts.push(`border-color: ${String(styles.__divideColor)}${imp}`);
3063
+ if ("__divideStyle" in styles) parts.push(`border-style: ${String(styles.__divideStyle)}${imp}`);
3064
+ return parts.join("; ");
3065
+ }
3066
+ function buildSpaceDecls(styles, forceImportant) {
3067
+ const imp = forceImportant ? " !important" : "";
3068
+ const parts = [];
3069
+ if ("__spaceX" in styles) {
3070
+ const v = styles.__spaceX;
3071
+ const val = typeof v === "number" ? `${v}px` : String(v);
3072
+ parts.push(`margin-left: ${val}${imp}`);
3073
+ }
3074
+ if ("__spaceY" in styles) {
3075
+ const v = styles.__spaceY;
3076
+ const val = typeof v === "number" ? `${v}px` : String(v);
3077
+ parts.push(`margin-top: ${val}${imp}`);
3078
+ }
3079
+ return parts.join("; ");
3080
+ }
3081
+ var RN_ONLY_PROPS = /* @__PURE__ */ new Set([
3082
+ "shadowColor",
3083
+ "shadowOffset",
3084
+ "shadowOpacity",
3085
+ "shadowRadius",
3086
+ "elevation",
3087
+ "includeFontPadding",
3088
+ "textAlignVertical",
3089
+ "writingDirection",
3090
+ "textShadowColor",
3091
+ "textShadowOffset",
3092
+ "textShadowRadius",
3093
+ "tintColor",
3094
+ "__divideX",
3095
+ "__divideY",
3096
+ "__divideColor",
3097
+ "__divideStyle",
3098
+ "__keyframe",
3099
+ "__spaceX",
3100
+ "__spaceY"
3101
+ ]);
3102
+ var CSS_UNITLESS = /* @__PURE__ */ new Set([
3103
+ "opacity",
3104
+ "fontWeight",
3105
+ "flex",
3106
+ "flexGrow",
3107
+ "flexShrink",
3108
+ "order",
3109
+ "zIndex",
3110
+ "aspectRatio",
3111
+ "columnCount",
3112
+ "lineHeight",
3113
+ "gridColumnStart",
3114
+ "gridColumnEnd",
3115
+ "gridRowStart",
3116
+ "gridRowEnd"
3117
+ ]);
3118
+ var TEXT_ONLY_STYLE_KEYS = [
3119
+ "color",
3120
+ "fontSize",
3121
+ "fontWeight",
3122
+ "fontStyle",
3123
+ "fontVariant",
3124
+ "letterSpacing",
3125
+ "lineHeight",
3126
+ "textAlign",
3127
+ "textAlignVertical",
3128
+ "textDecorationLine",
3129
+ "textDecorationColor",
3130
+ "textDecorationStyle",
3131
+ "textShadowColor",
3132
+ "textShadowOffset",
3133
+ "textShadowRadius",
3134
+ "textTransform",
3135
+ "writingDirection",
3136
+ "includeFontPadding",
3137
+ "verticalAlign"
3138
+ ];
3139
+ var RN_SHORTHAND_EXPAND = {
3140
+ marginHorizontal: ["margin-left", "margin-right"],
3141
+ marginVertical: ["margin-top", "margin-bottom"],
3142
+ paddingHorizontal: ["padding-left", "padding-right"],
3143
+ paddingVertical: ["padding-top", "padding-bottom"]
3144
+ };
3145
+ function styleValueToCSS(styles, forceImportant = false) {
3146
+ const parts = [];
3147
+ for (const [prop, val] of Object.entries(styles)) {
3148
+ if (val === void 0 || val === null || typeof val === "object" || RN_ONLY_PROPS.has(prop)) continue;
3149
+ let cssVal;
3150
+ if (typeof val === "number") {
3151
+ const isCustomProp = prop.startsWith("--");
3152
+ cssVal = val === 0 || isCustomProp || CSS_UNITLESS.has(prop) ? String(val) : `${val}px`;
3153
+ } else {
3154
+ cssVal = String(val);
3155
+ }
3156
+ const imp = forceImportant || prop === "display" && (cssVal === "grid" || cssVal === "inline-grid") || prop === "position" && (cssVal === "sticky" || cssVal === "fixed" || cssVal === "static") ? " !important" : "";
3157
+ if (prop in RN_SHORTHAND_EXPAND) {
3158
+ const [p1, p2] = RN_SHORTHAND_EXPAND[prop];
3159
+ parts.push(`${p1}: ${cssVal}${imp}`, `${p2}: ${cssVal}${imp}`);
3160
+ continue;
3161
+ }
3162
+ parts.push(`${camelToKebab(prop)}: ${cssVal}${imp}`);
3163
+ }
3164
+ return parts.join("; ");
3165
+ }
3166
+ function camelToKebab(str) {
3167
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase();
3168
+ }
3169
+ function buildClassCSSRules(cls, bucketKey, styles, darkMode, important, screens) {
3170
+ const rules = [];
3171
+ if ("__keyframe" in styles && typeof styles.__keyframe === "string") {
3172
+ rules.push(styles.__keyframe);
3173
+ }
3174
+ const isDivide = "__divideX" in styles || "__divideY" in styles || "__divideColor" in styles || "__divideStyle" in styles;
3175
+ const isSpace = "__spaceX" in styles || "__spaceY" in styles;
3176
+ const isChildCombinator = isDivide || isSpace;
3177
+ const escaped = escapeCSSSelector(cls);
3178
+ const childSuffix = isChildCombinator ? " > * + *" : "";
3179
+ if (bucketKey === "base") {
3180
+ const decls2 = isDivide ? buildDivideDecls(styles, important) : isSpace ? buildSpaceDecls(styles, important) : styleValueToCSS(styles, important);
3181
+ if (!decls2) return rules;
3182
+ rules.push(`.${escaped}${childSuffix} { ${decls2} }`);
3183
+ return rules;
3184
+ }
3185
+ const mods = bucketKey.split(":");
3186
+ let darkScheme;
3187
+ let needsImportant = important;
3188
+ const pseudoParts = [];
3189
+ const ancestorParts = [];
3190
+ const dirParts = [];
3191
+ const mediaWrappers = [];
3192
+ let minWidth = 0;
3193
+ for (const mod of mods) {
3194
+ const def = getModifier(mod);
3195
+ if (!def) continue;
3196
+ if (def.darkScheme) darkScheme = def.darkScheme;
3197
+ if (def.pseudo) pseudoParts.push(def.pseudo);
3198
+ if (def.ancestorSelector) {
3199
+ ancestorParts.push(def.ancestorSelector);
3200
+ needsImportant = true;
3201
+ }
3202
+ if (def.dirSelector) {
3203
+ dirParts.push(def.dirSelector);
3204
+ needsImportant = true;
3205
+ }
3206
+ if (def.mediaQuery) {
3207
+ mediaWrappers.push(`@media ${def.mediaQuery}`);
3208
+ needsImportant = true;
3209
+ }
3210
+ if (def.isResponsive) {
3211
+ const w = screens[mod] ?? 0;
3212
+ if (w > minWidth) minWidth = w;
3213
+ }
3214
+ if (def.forcesImportant) needsImportant = true;
3215
+ }
3216
+ const pseudoSuffix = pseudoParts.join("");
3217
+ const elementSelector = `.${escaped}${pseudoSuffix}${childSuffix}`;
3218
+ const ancestorPrefix = ancestorParts.join("");
3219
+ const dirPrefix = dirParts.join("");
3220
+ const selector = `${dirPrefix}${ancestorPrefix}${elementSelector}`;
3221
+ const decls = isDivide ? buildDivideDecls(styles, needsImportant) : isSpace ? buildSpaceDecls(styles, needsImportant) : styleValueToCSS(styles, needsImportant);
3222
+ if (!decls) return rules;
3223
+ let rule;
3224
+ if (darkScheme === "dark") {
3225
+ if (darkMode === "media") rule = `@media (prefers-color-scheme: dark) { ${selector} { ${decls} } }`;
3226
+ else if (darkMode === "class") rule = `.dark ${selector} { ${decls} }`;
3227
+ else rule = `[data-theme="dark"] ${selector} { ${decls} }`;
3228
+ } else if (darkScheme === "light") {
3229
+ if (darkMode === "media") rule = `@media (prefers-color-scheme: light) { ${selector} { ${decls} } }`;
3230
+ else if (darkMode === "class") rule = `.light ${selector} { ${decls} }`;
3231
+ else rule = `[data-theme="light"] ${selector} { ${decls} }`;
3232
+ } else {
3233
+ rule = `${selector} { ${decls} }`;
3234
+ }
3235
+ for (const mw of mediaWrappers) rule = `${mw} { ${rule} }`;
3236
+ if (minWidth > 0) rule = `@media (min-width: ${minWidth}px) { ${rule} }`;
3237
+ rules.push(rule);
3238
+ return rules;
3239
+ }
3240
+ function injectClassRule(cls, bucketKey, styles, darkMode, important, order) {
3241
+ const cssRules = buildClassCSSRules(cls, bucketKey, styles, darkMode, important, getGlobalScreens());
3242
+ for (const r of cssRules) injectRule(r, order);
3243
+ return cssRules;
3244
+ }
3245
+ function isPureDisplayStyle(styles) {
3246
+ const keys = Object.keys(styles);
3247
+ return keys.length === 1 && keys[0] === "display";
3248
+ }
3249
+ function resolve(classString, theme, darkMode = "attribute") {
3250
+ const cache = getThemeCache(theme);
3251
+ const cacheKey = `${classString}::${darkMode}::${getEffectiveIsWeb() ? "web" : "native"}`;
3252
+ const cached = cache.get(cacheKey);
3253
+ if (cached) {
3254
+ if (isWeb) {
3255
+ for (const { rule, order } of cached.rules) injectRule(rule, order);
3256
+ }
3257
+ return cached.result;
3258
+ }
3259
+ const result = {};
3260
+ const rules = [];
3261
+ const onWeb = isWeb;
3262
+ for (const parsed of parseClasses(expandModeAwareColorClasses(classString, theme.colors))) {
3263
+ const styles = resolveUtility(parsed, theme);
3264
+ if (!styles) {
3265
+ if (process.env.NODE_ENV !== "production" && !parsed.isArbitrary && !isKnownUtility(parsed.utility) && !parsed.original.startsWith("__")) {
3266
+ kbachWarn(`Unknown class "${parsed.original}"`);
3267
+ }
3268
+ continue;
3269
+ }
3270
+ const bucketKey = parsed.modifiers.length === 0 ? "base" : parsed.modifiers.join(":");
3271
+ if (!result[bucketKey]) result[bucketKey] = {};
3272
+ Object.assign(result[bucketKey], styles);
3273
+ if (onWeb) {
3274
+ const order = getModifierOrder(bucketKey) + (isPureDisplayStyle(styles) ? 0.5 : 0);
3275
+ for (const r of injectClassRule(parsed.original, bucketKey, styles, darkMode, parsed.important, order)) {
3276
+ rules.push({ rule: r, order });
3277
+ }
3278
+ }
3279
+ }
3280
+ cache.set(cacheKey, { result, rules });
3281
+ return result;
3282
+ }
3283
+ function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
3284
+ const result = {};
3285
+ for (const [key, styles] of getSortedEntries(resolved)) {
3286
+ if (!styles) continue;
3287
+ if (key === "base") {
3288
+ Object.assign(result, styles);
3289
+ continue;
3290
+ }
3291
+ const mods = key.split(":");
3292
+ if (mods.every((mod) => matchModifier(mod, isDark, state, breakpoints))) {
3293
+ Object.assign(result, styles);
3294
+ }
3295
+ }
3296
+ if (isNative) {
3297
+ const defaultFont = getDefaultFontFamily();
3298
+ const r = result;
3299
+ const looksLikeText = TEXT_ONLY_STYLE_KEYS.some((k) => k in r);
3300
+ if (defaultFont && looksLikeText && !("fontFamily" in r)) {
3301
+ r.fontFamily = defaultFont;
3302
+ }
3303
+ }
3304
+ if (!isNative) {
3305
+ const r = result;
3306
+ const ph = r.paddingHorizontal;
3307
+ if (ph !== void 0) {
3308
+ r.paddingLeft = ph;
3309
+ r.paddingRight = ph;
3310
+ delete r.paddingHorizontal;
3311
+ }
3312
+ const pv = r.paddingVertical;
3313
+ if (pv !== void 0) {
3314
+ r.paddingTop = pv;
3315
+ r.paddingBottom = pv;
3316
+ delete r.paddingVertical;
3317
+ }
3318
+ const mh = r.marginHorizontal;
3319
+ if (mh !== void 0) {
3320
+ r.marginLeft = mh;
3321
+ r.marginRight = mh;
3322
+ delete r.marginHorizontal;
3323
+ }
3324
+ const mv = r.marginVertical;
3325
+ if (mv !== void 0) {
3326
+ r.marginTop = mv;
3327
+ r.marginBottom = mv;
3328
+ delete r.marginVertical;
3329
+ }
3330
+ }
3331
+ return result;
3332
+ }
3333
+ function clearCache() {
3334
+ _injectedRules.clear();
3335
+ _ruleIndexByKey.clear();
3336
+ _ruleOrderByKey.clear();
3337
+ _sheetKeys.length = 0;
3338
+ if (_styleEl) {
3339
+ _styleEl.remove();
3340
+ _styleEl = null;
3341
+ }
3342
+ if (_globalStyleEl) {
3343
+ _globalStyleEl.remove();
3344
+ _globalStyleEl = null;
3345
+ }
3346
+ }
3347
+
3348
+ // src/core/config.ts
3349
+ function deepMerge(base, override) {
3350
+ const result = { ...base };
3351
+ for (const key of Object.keys(override)) {
3352
+ const baseVal = base[key];
3353
+ const overVal = override[key];
3354
+ if (baseVal !== null && typeof baseVal === "object" && !Array.isArray(baseVal) && overVal !== null && typeof overVal === "object" && !Array.isArray(overVal)) {
3355
+ result[key] = deepMerge(
3356
+ baseVal,
3357
+ overVal
3358
+ );
3359
+ } else if (overVal !== void 0) {
3360
+ result[key] = overVal;
3361
+ }
3362
+ }
3363
+ return result;
3364
+ }
3365
+ var configStore = { resolved: null, listeners: /* @__PURE__ */ new Set() };
3366
+ function getConfig() {
3367
+ if (configStore.resolved) return configStore.resolved;
3368
+ configStore.resolved = buildConfig({});
3369
+ return configStore.resolved;
3370
+ }
3371
+ function resetConfig() {
3372
+ configStore.resolved = null;
3373
+ }
3374
+ function resolveColorRefs(colors) {
3375
+ const MAX_DEPTH = 5;
3376
+ function resolveChain(raw, side) {
3377
+ let current = raw;
3378
+ for (let i = 0; i < MAX_DEPTH; i++) {
3379
+ const next = resolveOneRef(current, side);
3380
+ if (!next || next === current) break;
3381
+ current = next;
3382
+ }
3383
+ return current;
3384
+ }
3385
+ function resolveOneRef(ref, side) {
3386
+ const slashIdx = ref.indexOf("/");
3387
+ if (slashIdx > 0) {
3388
+ const baseName = ref.slice(0, slashIdx);
3389
+ const opacity = Number(ref.slice(slashIdx + 1));
3390
+ if (!Number.isFinite(opacity)) return null;
3391
+ let baseRef;
3392
+ const entry2 = colors[baseName];
3393
+ if (typeof entry2 === "string") {
3394
+ baseRef = entry2;
3395
+ } else if (isModeAwareColor(entry2)) {
3396
+ if (!side) return null;
3397
+ baseRef = entry2[side];
3398
+ } else if (entry2 && typeof entry2 === "object" && "6" in entry2) {
3399
+ const v = entry2["6"];
3400
+ baseRef = typeof v === "string" ? v : void 0;
3401
+ }
3402
+ if (baseRef === void 0) return null;
3403
+ const baseHex = resolveChain(baseRef, side);
3404
+ const a = opacity > 1 ? opacity / 100 : opacity;
3405
+ return hexToRgba(baseHex, a);
3406
+ }
3407
+ const split = splitColorShadeRef(ref);
3408
+ if (!split) return null;
3409
+ const { name, shade } = split;
3410
+ if (!/^\d+$/.test(shade)) return null;
3411
+ const entry = colors[name];
3412
+ if (!entry || typeof entry !== "object" || isModeAwareColor(entry)) return null;
3413
+ const target = entry[shade];
3414
+ return typeof target === "string" ? target : null;
3415
+ }
3416
+ function resolveValue(val) {
3417
+ return typeof val === "string" ? resolveChain(val, null) : { light: resolveChain(val.light, "light"), dark: resolveChain(val.dark, "dark") };
3418
+ }
3419
+ const out = {};
3420
+ for (const [key, val] of Object.entries(colors)) {
3421
+ if (typeof val === "string" || isModeAwareColor(val)) {
3422
+ out[key] = resolveValue(val);
3423
+ } else {
3424
+ const shades = {};
3425
+ for (const [shade, v] of Object.entries(val)) {
3426
+ shades[shade] = resolveValue(v);
3427
+ }
3428
+ out[key] = shades;
3429
+ }
3430
+ }
3431
+ return out;
3432
+ }
3433
+ function buildConfig(userConfig) {
3434
+ let theme = { ...defaultTheme };
3435
+ if (userConfig.theme) {
3436
+ theme = { ...theme, ...userConfig.theme };
3437
+ }
3438
+ if (userConfig.extend) {
3439
+ const extendConfig = userConfig.extend;
3440
+ const { theme: nestedTheme, ...directKeys } = extendConfig;
3441
+ const extSources = [];
3442
+ if (nestedTheme) extSources.push(nestedTheme);
3443
+ if (Object.keys(directKeys).length) extSources.push(directKeys);
3444
+ for (const ext of extSources) {
3445
+ for (const [key, value] of Object.entries(ext)) {
3446
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
3447
+ if (process.env.NODE_ENV !== "production") {
3448
+ kbachWarn(`extend.${key} should be an object, got ${typeof value} \u2014 skipped`);
3449
+ }
3450
+ continue;
3451
+ }
3452
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
3453
+ theme[key] = deepMerge(
3454
+ theme[key] ?? {},
3455
+ value
3456
+ );
3457
+ }
3458
+ }
3459
+ }
3460
+ }
3461
+ theme = { ...theme, colors: resolveColorRefs(theme.colors) };
3462
+ const resolved = {
3463
+ darkMode: userConfig.darkMode ?? "attribute",
3464
+ theme,
3465
+ plugins: userConfig.plugins ?? []
3466
+ };
3467
+ if (userConfig.plugins !== void 0) {
3468
+ clearPluginUtilities();
3469
+ clearPluginModifiers();
3470
+ }
3471
+ const pluginAPI = makePluginAPI(resolved.theme);
3472
+ for (const plugin of resolved.plugins) {
3473
+ plugin(pluginAPI);
3474
+ }
3475
+ const rawSans = theme.fontFamily?.sans;
3476
+ const sansFontValue = Array.isArray(rawSans) ? rawSans[0] : rawSans;
3477
+ const nativeSansFont = sansFontValue?.split(",")[0]?.trim().replace(/^['"]|['"]$/g, "");
3478
+ setDefaultFontFamily(nativeSansFont && nativeSansFont !== "System" ? nativeSansFont : void 0);
3479
+ injectGlobalStyles(resolved.theme);
3480
+ return resolved;
3481
+ }
3482
+ function _selectorToModifierDef(selector) {
3483
+ const s = selector.trim();
3484
+ if (s.startsWith("@media ")) {
3485
+ return { mediaQuery: s.slice(7), jsBehavior: "css-only", forcesImportant: true };
3486
+ }
3487
+ if (s.startsWith("::") || s.startsWith(":") && !s.includes(" ") && !s.includes("&") && !s.includes("(")) {
3488
+ return { pseudo: s, jsBehavior: "css-only", forcesImportant: true };
3489
+ }
3490
+ return { ancestorSelector: s.endsWith(" ") ? s : `${s} `, jsBehavior: "css-only", forcesImportant: true };
3491
+ }
3492
+ function makePluginAPI(theme) {
3493
+ const standalone = getPluginStandaloneMap();
3494
+ return {
3495
+ addUtility(name, styles) {
3496
+ standalone[name] = styles;
3497
+ },
3498
+ addVariant(name, selectorOrDef) {
3499
+ const def = typeof selectorOrDef === "string" ? _selectorToModifierDef(selectorOrDef) : selectorOrDef;
3500
+ registerModifier(name, def);
3501
+ },
3502
+ theme(path, defaultValue) {
3503
+ const parts = path.replace(/\[([^\]]+)\]/g, ".$1").split(".");
3504
+ let current = theme;
3505
+ for (const part of parts) {
3506
+ if (current === null || typeof current !== "object") return defaultValue;
3507
+ current = current[part];
3508
+ }
3509
+ return current ?? defaultValue;
3510
+ },
3511
+ e(className) {
3512
+ return className.replace(/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
3513
+ }
3514
+ };
3515
+ }
3516
+ function onConfigChange(listener) {
3517
+ configStore.listeners.add(listener);
3518
+ return () => configStore.listeners.delete(listener);
3519
+ }
3520
+ function updateConfig(userConfig) {
3521
+ clearCache();
3522
+ configStore.resolved = buildConfig(userConfig);
3523
+ configStore._src = userConfig;
3524
+ for (const listener of configStore.listeners) {
3525
+ listener(configStore.resolved);
3526
+ }
3527
+ }
3528
+ function initConfig(userConfig) {
3529
+ if (configStore._src === userConfig) return;
3530
+ updateConfig(userConfig);
3531
+ }
3532
+
3533
+ // src/core/darkModeStore.ts
3534
+ var store2 = { isDark: false, notifiedIsDark: false, subscribers: /* @__PURE__ */ new Set() };
3535
+ function syncGlobalDarkMode(isDark) {
3536
+ store2.isDark = isDark;
3537
+ }
3538
+ function setGlobalDarkMode(isDark) {
3539
+ store2.isDark = isDark;
3540
+ if (store2.notifiedIsDark === isDark) return;
3541
+ store2.notifiedIsDark = isDark;
3542
+ for (const sub of store2.subscribers) sub();
3543
+ }
3544
+ function getGlobalDarkMode() {
3545
+ return store2.isDark;
3546
+ }
3547
+ function subscribeGlobalDarkMode(callback) {
3548
+ store2.subscribers.add(callback);
3549
+ return () => store2.subscribers.delete(callback);
3550
+ }
3551
+
3552
+ // src/useSyncExternalStoreShim.ts
3553
+ import React from "react";
3554
+ var useSyncExternalStore = React.useSyncExternalStore ?? function useSyncExternalStoreFallback(subscribe, getSnapshot, getServerSnapshot) {
3555
+ const isServer = typeof window === "undefined";
3556
+ const [, forceUpdate] = React.useReducer((n) => n + 1, 0);
3557
+ const value = isServer && getServerSnapshot ? getServerSnapshot() : getSnapshot();
3558
+ React.useEffect(() => {
3559
+ if (getSnapshot() !== value) forceUpdate();
3560
+ return subscribe(forceUpdate);
3561
+ }, [subscribe]);
3562
+ return value;
3563
+ };
3564
+
3565
+ // src/useGlobalDarkMode.ts
3566
+ var NOOP_SUB = (_) => () => {
3567
+ };
3568
+ var FALSE_SNAP = () => false;
3569
+ function useGlobalDarkMode() {
3570
+ return useSyncExternalStore(
3571
+ subscribeGlobalDarkMode,
3572
+ getGlobalDarkMode,
3573
+ () => false
3574
+ // SSR: default light
3575
+ );
3576
+ }
3577
+ function useConditionalGlobalDarkMode(active) {
3578
+ return useSyncExternalStore(
3579
+ active ? subscribeGlobalDarkMode : NOOP_SUB,
3580
+ active ? getGlobalDarkMode : FALSE_SNAP,
3581
+ FALSE_SNAP
3582
+ );
3583
+ }
3584
+
3585
+ // src/shared-utils.ts
3586
+ function hasResponsiveBuckets(resolved) {
3587
+ const responsiveMods = getResponsiveModifiers();
3588
+ for (const key of Object.keys(resolved)) {
3589
+ if (key === "base") continue;
3590
+ for (const mod of key.split(":")) {
3591
+ if (responsiveMods.has(mod)) return true;
3592
+ }
3593
+ }
3594
+ return false;
3595
+ }
3596
+ function hasInteractiveBuckets(resolved) {
3597
+ const interactiveMods = getInteractiveModifiers();
3598
+ for (const key of Object.keys(resolved)) {
3599
+ if (key === "base") continue;
3600
+ for (const mod of key.split(":")) {
3601
+ if (interactiveMods.has(mod)) return true;
3602
+ }
3603
+ }
3604
+ return false;
3605
+ }
3606
+ function chain(original, extra) {
3607
+ return (...args) => {
3608
+ original?.(...args);
3609
+ extra();
3610
+ };
3611
+ }
3612
+ function stripInternalMarkers(s) {
3613
+ delete s.__divideX;
3614
+ delete s.__divideY;
3615
+ delete s.__divideColor;
3616
+ delete s.__divideStyle;
3617
+ delete s.__keyframe;
3618
+ }
3619
+ function stripWebOnlyProps(s) {
3620
+ if (s.display === "grid" || s.display === "inline-grid") delete s.display;
3621
+ delete s.gridTemplateColumns;
3622
+ delete s.gridTemplateRows;
3623
+ delete s.gridColumn;
3624
+ delete s.gridRow;
3625
+ delete s.gridArea;
3626
+ delete s.gridColumnStart;
3627
+ delete s.gridColumnEnd;
3628
+ delete s.gridRowStart;
3629
+ delete s.gridRowEnd;
3630
+ delete s.gridAutoFlow;
3631
+ delete s.gridAutoColumns;
3632
+ delete s.gridAutoRows;
3633
+ delete s.placeItems;
3634
+ delete s.placeContent;
3635
+ delete s.justifyItems;
3636
+ delete s.placeSelf;
3637
+ delete s.justifySelf;
3638
+ if (s.position === "sticky" || s.position === "fixed" || s.position === "static") {
3639
+ delete s.position;
3640
+ }
3641
+ }
3642
+ function composeNativeStyle(computed, userStyle) {
3643
+ if (!userStyle) return computed;
3644
+ return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
3645
+ }
3646
+
3647
+ // src/web-substitute.ts
3648
+ var _rnToHtml = {
3649
+ View: "div",
3650
+ SafeAreaView: "div",
3651
+ KeyboardAvoidingView: "div",
3652
+ ScrollView: "div",
3653
+ VirtualizedList: "div",
3654
+ FlatList: "div",
3655
+ SectionList: "div",
3656
+ Text: "span",
3657
+ TextInput: "input",
3658
+ Image: "img",
3659
+ ImageBackground: "div",
3660
+ Pressable: "div",
3661
+ TouchableOpacity: "div",
3662
+ TouchableHighlight: "div",
3663
+ TouchableWithoutFeedback: "div",
3664
+ TouchableNativeFeedback: "div"
3665
+ };
3666
+ var _userMap = /* @__PURE__ */ new Map();
3667
+ var _cache = /* @__PURE__ */ new WeakMap();
3668
+ function registerWebElement(rnComponent, htmlTag) {
3669
+ if (typeof rnComponent !== "object" && typeof rnComponent !== "function") return;
3670
+ _userMap.set(rnComponent, htmlTag);
3671
+ _cache.delete(rnComponent);
3672
+ }
3673
+ var _forwardRefOrMemoType = /* @__PURE__ */ new Set([
3674
+ /* @__PURE__ */ Symbol.for("react.forward_ref"),
3675
+ /* @__PURE__ */ Symbol.for("react.memo")
3676
+ ]);
3677
+ function looksLikeRealRNPrimitive(type) {
3678
+ const t = type.$$typeof;
3679
+ if (t !== void 0) return _forwardRefOrMemoType.has(t);
3680
+ return !!type.prototype?.isReactComponent;
3681
+ }
3682
+ function getWebTag(type, props) {
3683
+ if (typeof type === "string") return null;
3684
+ if (type === null || typeof type !== "function" && typeof type !== "object") return null;
3685
+ const obj = type;
3686
+ if (_userMap.has(obj)) return _userMap.get(obj);
3687
+ if (!looksLikeRealRNPrimitive(obj)) return null;
3688
+ const name = obj.displayName ?? obj.name;
3689
+ if (!name) {
3690
+ _cache.set(obj, null);
3691
+ return null;
3692
+ }
3693
+ if (name === "TextInput") {
3694
+ const multiline = !!props && (props.multiline === true || Number(props.numberOfLines) > 1);
3695
+ return multiline ? "textarea" : "input";
3696
+ }
3697
+ if (_cache.has(obj)) return _cache.get(obj);
3698
+ const tag = _rnToHtml[name] ?? null;
3699
+ _cache.set(obj, tag);
3700
+ return tag;
3701
+ }
3702
+ function getImpliedRNClasses(webTag, resolvedBase) {
3703
+ if (!webTag || !resolvedBase) return void 0;
3704
+ const classes = [];
3705
+ if (resolvedBase.position === void 0) classes.push("relative");
3706
+ const explicitDisplay = resolvedBase.display;
3707
+ const hasFlexItemProps = "flexGrow" in resolvedBase || "flexShrink" in resolvedBase || "flex" in resolvedBase || "gap" in resolvedBase || "columnGap" in resolvedBase || "rowGap" in resolvedBase;
3708
+ const willBeFlex = explicitDisplay === "flex" || explicitDisplay === void 0 && hasFlexItemProps;
3709
+ if (willBeFlex && (explicitDisplay === void 0 || resolvedBase.flexDirection === void 0)) {
3710
+ classes.push("flex-col");
3711
+ }
3712
+ return classes.length > 0 ? classes.join(" ") : void 0;
3713
+ }
3714
+ var _rnOnlyProps = /* @__PURE__ */ new Set([
3715
+ // Interaction
3716
+ "onLongPress",
3717
+ "delayLongPress",
3718
+ "activeOpacity",
3719
+ "underlayColor",
3720
+ "hitSlop",
3721
+ "pressRetentionOffset",
3722
+ "android_ripple",
3723
+ "android_disableSound",
3724
+ "onHoverIn",
3725
+ "onHoverOut",
3726
+ "onHoverStart",
3727
+ "onHoverEnd",
3728
+ // Layout event
3729
+ "onLayout",
3730
+ // Accessibility
3731
+ "accessible",
3732
+ "accessibilityState",
3733
+ "accessibilityLiveRegion",
3734
+ "importantForAccessibility",
3735
+ // Platform
3736
+ "nativeID",
3737
+ "collapsable",
3738
+ "needsOffscreenAlphaCompositing",
3739
+ "renderToHardwareTextureAndroid",
3740
+ "shouldRasterizeIOS",
3741
+ "focusable",
3742
+ "hasTVPreferredFocus",
3743
+ // 'pointerEvents' is handled in transformToWebProps (mapped to CSS style)
3744
+ // Text
3745
+ "selectable",
3746
+ "allowFontScaling",
3747
+ "adjustsFontSizeToFit",
3748
+ "minimumFontScale",
3749
+ "ellipsizeMode",
3750
+ "numberOfLines",
3751
+ "onTextLayout",
3752
+ "textBreakStrategy",
3753
+ "lineBreakStrategyIOS",
3754
+ // TextInput
3755
+ "multiline",
3756
+ // NOTE: 'resizeMode' (Image's prop, not TextInput's) deliberately does NOT
3757
+ // go here — it's handled below in the isImage branch (mapped to CSS
3758
+ // object-fit). Blacklisting it here would make that branch unreachable.
3759
+ "blurOnSubmit",
3760
+ "clearButtonMode",
3761
+ "clearTextOnFocus",
3762
+ "enablesReturnKeyAutomatically",
3763
+ "returnKeyType",
3764
+ "spellCheck",
3765
+ // ScrollView
3766
+ "scrollEnabled",
3767
+ "showsVerticalScrollIndicator",
3768
+ "showsHorizontalScrollIndicator",
3769
+ "contentContainerStyle",
3770
+ // 'horizontal' is handled in transformToWebProps (converted to CSS overflow-x).
3771
+ "keyboardShouldPersistTaps",
3772
+ "keyboardDismissMode",
3773
+ "pagingEnabled",
3774
+ "scrollEventThrottle",
3775
+ "decelerationRate",
3776
+ "bounces",
3777
+ "alwaysBounceHorizontal",
3778
+ "alwaysBounceVertical",
3779
+ "snapToAlignment",
3780
+ "snapToInterval",
3781
+ "snapToOffsets",
3782
+ "removeClippedSubviews",
3783
+ "overScrollMode",
3784
+ "stickyHeaderIndices",
3785
+ "invertStickyHeaders",
3786
+ "onScrollBeginDrag",
3787
+ "onScrollEndDrag",
3788
+ "onMomentumScrollBegin",
3789
+ "onMomentumScrollEnd",
3790
+ "contentInset",
3791
+ "contentInsetAdjustmentBehavior",
3792
+ "automaticallyAdjustContentInsets",
3793
+ "automaticallyAdjustsScrollIndicatorInsets",
3794
+ // expo-image
3795
+ "contentPosition",
3796
+ "cachePolicy",
3797
+ "recyclingKey",
3798
+ "blurRadius",
3799
+ "fadeDuration",
3800
+ "responsivePolicy",
3801
+ "tintColor",
3802
+ "allowDownscaling",
3803
+ "placeholderContentFit",
3804
+ // FlatList / SectionList
3805
+ "data",
3806
+ "renderItem",
3807
+ "keyExtractor",
3808
+ "getItemLayout",
3809
+ "initialScrollIndex",
3810
+ "initialNumToRender",
3811
+ "maxToRenderPerBatch",
3812
+ "windowSize",
3813
+ "updateCellsBatchingPeriod",
3814
+ "onEndReached",
3815
+ "onEndReachedThreshold",
3816
+ "ListHeaderComponent",
3817
+ "ListFooterComponent",
3818
+ "ListEmptyComponent",
3819
+ "ListHeaderComponentStyle",
3820
+ "ListFooterComponentStyle",
3821
+ "ItemSeparatorComponent",
3822
+ "SectionSeparatorComponent",
3823
+ "inverted",
3824
+ "getItem",
3825
+ "getItemCount"
3826
+ ]);
3827
+ var _pressableNames = /* @__PURE__ */ new Set([
3828
+ "Pressable",
3829
+ "TouchableOpacity",
3830
+ "TouchableHighlight",
3831
+ "TouchableWithoutFeedback",
3832
+ "TouchableNativeFeedback"
3833
+ ]);
3834
+ var _keyboardTypeMap = {
3835
+ "numeric": "number",
3836
+ "number-pad": "number",
3837
+ "decimal-pad": "decimal",
3838
+ "email-address": "email",
3839
+ "phone-pad": "tel",
3840
+ "url": "url"
3841
+ };
3842
+ var _resizeModeMap = {
3843
+ "contain": "contain",
3844
+ "cover": "cover",
3845
+ "stretch": "fill",
3846
+ "center": "none",
3847
+ "repeat": "none"
3848
+ };
3849
+ var PLACEHOLDER_RULE_ATTR = "data-kbach-ph";
3850
+ var _placeholderRuleInjected = false;
3851
+ function ensurePlaceholderColorRuleInjected() {
3852
+ if (_placeholderRuleInjected || typeof document === "undefined") return;
3853
+ _placeholderRuleInjected = true;
3854
+ const style = document.createElement("style");
3855
+ style.setAttribute("data-kbach-placeholder", "");
3856
+ style.textContent = `[${PLACEHOLDER_RULE_ATTR}]::placeholder{color:var(--kbach-ph-color)}`;
3857
+ document.head.appendChild(style);
3858
+ }
3859
+ function transformToWebProps(originalName, tag, props) {
3860
+ const out = {};
3861
+ const isPressable = _pressableNames.has(originalName);
3862
+ const isTextInput = originalName === "TextInput";
3863
+ const isImage = originalName === "Image" || originalName === "ImageBackground";
3864
+ const isScrollable = originalName === "ScrollView" || originalName === "FlatList" || originalName === "SectionList";
3865
+ let pendingStyle = null;
3866
+ for (const [k, v] of Object.entries(props)) {
3867
+ if (_rnOnlyProps.has(k)) continue;
3868
+ if (k === "onPress") {
3869
+ if (!("onClick" in props)) out.onClick = v;
3870
+ continue;
3871
+ }
3872
+ if (k === "accessibilityLabel") {
3873
+ if (out["aria-label"] == null) out["aria-label"] = v;
3874
+ continue;
3875
+ }
3876
+ if (k === "accessibilityRole") {
3877
+ if (out.role == null) out.role = v;
3878
+ continue;
3879
+ }
3880
+ if (k === "testID") {
3881
+ if (out["data-testid"] == null) out["data-testid"] = v;
3882
+ continue;
3883
+ }
3884
+ if (isTextInput) {
3885
+ if (k === "onChangeText") {
3886
+ if (!("onChange" in props)) out.onChange = (e) => v(e.target.value);
3887
+ continue;
3888
+ }
3889
+ if (k === "onSubmitEditing") {
3890
+ if (!("onKeyDown" in props)) {
3891
+ out.onKeyDown = (e) => {
3892
+ if (e.key === "Enter") v();
3893
+ };
3894
+ }
3895
+ continue;
3896
+ }
3897
+ if (k === "placeholderTextColor") {
3898
+ if (v) {
3899
+ ensurePlaceholderColorRuleInjected();
3900
+ out[PLACEHOLDER_RULE_ATTR] = "";
3901
+ pendingStyle = { ...pendingStyle ?? {}, "--kbach-ph-color": v };
3902
+ }
3903
+ continue;
3904
+ }
3905
+ if (k === "secureTextEntry") {
3906
+ if (v && !("type" in props)) out.type = "password";
3907
+ continue;
3908
+ }
3909
+ if (k === "keyboardType") {
3910
+ if (!("type" in props) && !props.secureTextEntry) {
3911
+ const mapped = _keyboardTypeMap[v];
3912
+ if (mapped) out.type = mapped;
3913
+ }
3914
+ continue;
3915
+ }
3916
+ if (k === "editable") {
3917
+ if (v === false) out.readOnly = true;
3918
+ continue;
3919
+ }
3920
+ if (k === "maxLength") {
3921
+ out.maxLength = v;
3922
+ continue;
3923
+ }
3924
+ }
3925
+ if (isScrollable && k === "horizontal") {
3926
+ if (v) pendingStyle = { ...pendingStyle ?? {}, display: "flex", flexDirection: "row", overflowX: "auto" };
3927
+ continue;
3928
+ }
3929
+ if (k === "pointerEvents") {
3930
+ if (v === "none" || v === "auto") pendingStyle = { ...pendingStyle ?? {}, pointerEvents: v };
3931
+ continue;
3932
+ }
3933
+ if (isImage) {
3934
+ if (k === "source") {
3935
+ if (typeof v === "string") {
3936
+ out.src = v;
3937
+ } else if (v && typeof v === "object" && "uri" in v) {
3938
+ out.src = v.uri;
3939
+ if (v.headers) out["crossOrigin"] = "anonymous";
3940
+ }
3941
+ continue;
3942
+ }
3943
+ if (k === "resizeMode") {
3944
+ pendingStyle = { ...pendingStyle ?? {}, objectFit: _resizeModeMap[v] ?? "cover" };
3945
+ continue;
3946
+ }
3947
+ if (k === "contentFit") {
3948
+ pendingStyle = { ...pendingStyle ?? {}, objectFit: v };
3949
+ continue;
3950
+ }
3951
+ if (k === "defaultSource") continue;
3952
+ }
3953
+ if (k === "style") {
3954
+ if (Array.isArray(v)) {
3955
+ out.style = Object.assign({}, ...v.filter(Boolean));
3956
+ } else if (v != null) {
3957
+ out.style = v;
3958
+ }
3959
+ continue;
3960
+ }
3961
+ out[k] = v;
3962
+ }
3963
+ if (pendingStyle) {
3964
+ out.style = out.style ? { ...pendingStyle, ...out.style } : pendingStyle;
3965
+ }
3966
+ if (isPressable && !out.role) out.role = "button";
3967
+ if (isImage && tag === "img" && out.alt == null) out.alt = "";
3968
+ return out;
3969
+ }
3970
+
3971
+ // src/InteractiveWrapper.tsx
3972
+ import React2, { forwardRef, useState, useCallback, useMemo } from "react";
3973
+
3974
+ // src/useGlobalWidth.ts
3975
+ var NOOP_SUB2 = (_) => () => {
3976
+ };
3977
+ var ZERO_SNAP = () => 0;
3978
+ var EMPTY_BREAKPOINTS = /* @__PURE__ */ new Set();
3979
+ var _webListeners = null;
3980
+ var _webRaf = 0;
3981
+ function _webResizeHandler() {
3982
+ cancelAnimationFrame(_webRaf);
3983
+ _webRaf = requestAnimationFrame(() => {
3984
+ if (_webListeners) for (const cb of _webListeners) cb();
3985
+ });
3986
+ }
3987
+ function subscribeWebWidth(cb) {
3988
+ if (typeof window === "undefined") return () => {
3989
+ };
3990
+ if (!_webListeners) {
3991
+ _webListeners = /* @__PURE__ */ new Set();
3992
+ window.addEventListener("resize", _webResizeHandler);
3993
+ }
3994
+ _webListeners.add(cb);
3995
+ return () => {
3996
+ _webListeners.delete(cb);
3997
+ if (_webListeners.size === 0) {
3998
+ window.removeEventListener("resize", _webResizeHandler);
3999
+ cancelAnimationFrame(_webRaf);
4000
+ _webListeners = null;
4001
+ }
4002
+ };
4003
+ }
4004
+ function getWebWidth() {
4005
+ return typeof window !== "undefined" ? window.innerWidth : 0;
4006
+ }
4007
+ function useGlobalWidth() {
4008
+ return useSyncExternalStore(
4009
+ isWeb ? subscribeWebWidth : subscribeGlobalWidth,
4010
+ isWeb ? getWebWidth : getGlobalWidth,
4011
+ ZERO_SNAP
4012
+ );
4013
+ }
4014
+ function useConditionalWidth(active) {
4015
+ return useSyncExternalStore(
4016
+ active ? isWeb ? subscribeWebWidth : subscribeGlobalWidth : NOOP_SUB2,
4017
+ active ? isWeb ? getWebWidth : getGlobalWidth : ZERO_SNAP,
4018
+ ZERO_SNAP
4019
+ );
4020
+ }
4021
+
4022
+ // src/InteractiveWrapper.tsx
4023
+ var InteractiveWrapper = forwardRef(
4024
+ function InteractiveWrapper2({
4025
+ Component,
4026
+ resolvedStyle,
4027
+ className,
4028
+ style: styleProp,
4029
+ onPressIn,
4030
+ onPressOut,
4031
+ onPointerDown,
4032
+ onPointerUp,
4033
+ onPointerLeave,
4034
+ onPointerCancel,
4035
+ onMouseEnter,
4036
+ onMouseLeave,
4037
+ onFocus,
4038
+ onBlur,
4039
+ ...rest
4040
+ }, ref) {
4041
+ const isWebPlatform = getEffectiveIsWeb();
4042
+ const isDark = useConditionalGlobalDarkMode(!isWebPlatform);
4043
+ const needsWidth = hasResponsiveBuckets(resolvedStyle);
4044
+ const width = useConditionalWidth(needsWidth && !isWebPlatform);
4045
+ const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
4046
+ const screens = getGlobalScreens();
4047
+ const [pressed, setPressed] = useState(false);
4048
+ const [hovered, setHovered] = useState(false);
4049
+ const [focused, setFocused] = useState(false);
4050
+ const handlePressIn = useCallback(chain(onPressIn, () => {
4051
+ if (!isWebPlatform) setPressed(true);
4052
+ }), [onPressIn, isWebPlatform]);
4053
+ const handlePressOut = useCallback(chain(onPressOut, () => {
4054
+ if (!isWebPlatform) setPressed(false);
4055
+ }), [onPressOut, isWebPlatform]);
4056
+ const handlePointerDown = useCallback(chain(onPointerDown, () => {
4057
+ if (!isWebPlatform) setPressed(true);
4058
+ }), [onPointerDown, isWebPlatform]);
4059
+ const handlePointerUp = useCallback(chain(onPointerUp, () => {
4060
+ if (!isWebPlatform) setPressed(false);
4061
+ }), [onPointerUp, isWebPlatform]);
4062
+ const handlePointerLeave = useCallback(chain(onPointerLeave, () => {
4063
+ if (!isWebPlatform) setPressed(false);
4064
+ }), [onPointerLeave, isWebPlatform]);
4065
+ const handlePointerCancel = useCallback(chain(onPointerCancel, () => {
4066
+ if (!isWebPlatform) setPressed(false);
4067
+ }), [onPointerCancel, isWebPlatform]);
4068
+ const handleMouseEnter = useCallback(chain(onMouseEnter, () => {
4069
+ if (!isWebPlatform) setHovered(true);
4070
+ }), [onMouseEnter, isWebPlatform]);
4071
+ const handleMouseLeave = useCallback(chain(onMouseLeave, () => {
4072
+ if (!isWebPlatform) setHovered(false);
4073
+ }), [onMouseLeave, isWebPlatform]);
4074
+ const handleFocus = useCallback(chain(onFocus, () => {
4075
+ if (!isWebPlatform) setFocused(true);
4076
+ }), [onFocus, isWebPlatform]);
4077
+ const handleBlur = useCallback(chain(onBlur, () => {
4078
+ if (!isWebPlatform) setFocused(false);
4079
+ }), [onBlur, isWebPlatform]);
4080
+ const { children, disabled, checked, ...restForComponent } = rest;
4081
+ const isNonStringComponent = typeof Component !== "string";
4082
+ const computedStyle = useMemo(
4083
+ () => {
4084
+ if (isWebPlatform) return {};
4085
+ const s = flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused, disabled: !!disabled, checked: !!checked }, breakpoints);
4086
+ stripInternalMarkers(s);
4087
+ if (isNonStringComponent) stripWebOnlyProps(s);
4088
+ return s;
4089
+ },
4090
+ [resolvedStyle, isDark, pressed, hovered, focused, width, screens, disabled, checked, isNonStringComponent]
4091
+ );
4092
+ const skipComputedInline = isWebPlatform;
4093
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : composeNativeStyle(computedStyle, styleProp);
4094
+ const componentProps = {
4095
+ ref,
4096
+ ...restForComponent,
4097
+ ...disabled !== void 0 ? { disabled } : {},
4098
+ ...checked !== void 0 ? { checked } : {},
4099
+ style: finalStyle,
4100
+ ...!isNative && className ? { className } : {},
4101
+ // On web, onPointerDown/Up drive the wrapper's own pressed state (covers mouse + touch) —
4102
+ // onPressIn/onPressOut are RN-only prop names and are never forwarded here, even when
4103
+ // Component isn't a literal HTML tag string. A non-string Component on web is just as
4104
+ // likely to be an ordinary web component (React Router's <Link>, Next.js's <Link>, any
4105
+ // custom wrapper) as an actual react-native-web primitive — "not a string" alone was never
4106
+ // a reliable signal that onPressIn/onPressOut are wanted, and forwarding them
4107
+ // unconditionally made React DOM warn "Unknown event handler property" the moment any web
4108
+ // component got an interactive modifier (hover:, active:, …), which is the common case,
4109
+ // not the exception. (A real react-native-web primitive would additionally have this pair
4110
+ // of props silently vanish after hydration anyway, once the browser-only web-substitution
4111
+ // in jsx-runtime.tsx swaps it for a plain host tag — so keeping them pre-hydration would
4112
+ // only have traded one prop-mismatch warning for another.) Genuinely native code paths
4113
+ // still get them via the isNative branch below.
4114
+ ...!isNative ? {
4115
+ onPointerDown: handlePointerDown,
4116
+ onPointerUp: handlePointerUp,
4117
+ onPointerLeave: handlePointerLeave,
4118
+ onPointerCancel: handlePointerCancel
4119
+ } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
4120
+ onMouseEnter: handleMouseEnter,
4121
+ onMouseLeave: handleMouseLeave,
4122
+ onFocus: handleFocus,
4123
+ onBlur: handleBlur
4124
+ };
4125
+ return Array.isArray(children) ? React2.createElement(Component, componentProps, ...children) : React2.createElement(Component, componentProps, children);
4126
+ }
4127
+ );
4128
+ InteractiveWrapper.displayName = "Kbach.InteractiveWrapper";
4129
+
4130
+ export {
4131
+ isModeAwareColor,
4132
+ isWeb,
4133
+ isNative,
4134
+ setResolveTarget,
4135
+ getEffectiveIsWeb,
4136
+ kbachWarn,
4137
+ getInteractiveModifiers,
4138
+ getModeModifiers,
4139
+ getResponsiveModifiers,
4140
+ parseHexRgb,
4141
+ parseClass,
4142
+ splitClassTokens,
4143
+ normalizeClassString,
4144
+ parseClasses,
4145
+ expandModeAwareColorClasses,
4146
+ defaultColors,
4147
+ defaultTheme,
4148
+ generateKbachTypesDts,
4149
+ RESET_STYLE_ID,
4150
+ BASE_RESET,
4151
+ syncGlobalWidth,
4152
+ syncGlobalScreens,
4153
+ getGlobalScreens,
4154
+ setGlobalWidth,
4155
+ getActiveBreakpoints,
4156
+ getDefaultFontFamily,
4157
+ disableRuntimeCSS,
4158
+ isRuntimeCSSDisabled,
4159
+ resolve,
4160
+ flatten,
4161
+ clearCache,
4162
+ getConfig,
4163
+ resetConfig,
4164
+ buildConfig,
4165
+ onConfigChange,
4166
+ updateConfig,
4167
+ initConfig,
4168
+ syncGlobalDarkMode,
4169
+ setGlobalDarkMode,
4170
+ useSyncExternalStore,
4171
+ useGlobalDarkMode,
4172
+ useConditionalGlobalDarkMode,
4173
+ EMPTY_BREAKPOINTS,
4174
+ useGlobalWidth,
4175
+ useConditionalWidth,
4176
+ hasResponsiveBuckets,
4177
+ hasInteractiveBuckets,
4178
+ chain,
4179
+ stripInternalMarkers,
4180
+ stripWebOnlyProps,
4181
+ composeNativeStyle,
4182
+ registerWebElement,
4183
+ getWebTag,
4184
+ getImpliedRNClasses,
4185
+ transformToWebProps,
4186
+ InteractiveWrapper
4187
+ };