@kbach/react 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  syncGlobalDarkMode,
16
16
  updateConfig,
17
17
  useGlobalDarkMode
18
- } from "./chunk-LYGD2GSW.mjs";
18
+ } from "./chunk-V5KHX4CZ.mjs";
19
19
 
20
20
  // src/context.tsx
21
21
  import { createContext, useContext } from "react";
@@ -29,12 +29,72 @@ function useTheme() {
29
29
  }
30
30
  return ctx;
31
31
  }
32
+ function useIsDark() {
33
+ return useTheme().isDark;
34
+ }
35
+
36
+ // src/useColors.ts
37
+ import { useMemo } from "react";
38
+ function applyOpacity(color, opacity) {
39
+ const a = Math.max(0, Math.min(1, opacity / 100));
40
+ if (color.startsWith("#")) {
41
+ const h = color.slice(1);
42
+ const [rs, gs, bs] = h.length === 3 ? [h[0] + h[0], h[1] + h[1], h[2] + h[2]] : [h.slice(0, 2), h.slice(2, 4), h.slice(4, 6)];
43
+ return `rgba(${parseInt(rs, 16)},${parseInt(gs, 16)},${parseInt(bs, 16)},${a})`;
44
+ }
45
+ if (color.startsWith("rgb(")) return color.replace("rgb(", "rgba(").replace(")", `,${a})`);
46
+ if (color.startsWith("rgba(")) return color.replace(/,\s*[\d.]+\)$/, `,${a})`);
47
+ return color;
48
+ }
49
+ function makeShadeProxy(shades) {
50
+ return new Proxy(shades, {
51
+ get(target, prop) {
52
+ const key = String(prop);
53
+ if (key === "then") return void 0;
54
+ if (key.includes("/")) {
55
+ const slash = key.indexOf("/");
56
+ const shade = key.slice(0, slash);
57
+ const op = Number(key.slice(slash + 1));
58
+ const color = target[shade];
59
+ return typeof color === "string" ? applyOpacity(color, op) : void 0;
60
+ }
61
+ return target[key];
62
+ }
63
+ });
64
+ }
65
+ function wrapColors(rawColors) {
66
+ const cache = /* @__PURE__ */ new Map();
67
+ const alpha = (color, opacity) => applyOpacity(color, opacity);
68
+ return new Proxy({ alpha }, {
69
+ get(_, prop) {
70
+ const key = String(prop);
71
+ if (key === "then") return void 0;
72
+ if (key === "alpha") return alpha;
73
+ if (key.includes("/")) {
74
+ const slash = key.indexOf("/");
75
+ const name = key.slice(0, slash);
76
+ const op = Number(key.slice(slash + 1));
77
+ const entry2 = rawColors[name];
78
+ return typeof entry2 === "string" ? applyOpacity(entry2, op) : void 0;
79
+ }
80
+ const entry = rawColors[key];
81
+ if (entry === void 0) return void 0;
82
+ if (typeof entry === "string") return entry;
83
+ if (!cache.has(key)) cache.set(key, makeShadeProxy(entry));
84
+ return cache.get(key);
85
+ }
86
+ });
87
+ }
88
+ function useColors() {
89
+ const { config } = useTheme();
90
+ return useMemo(() => wrapColors(config.theme.colors), [config.theme.colors]);
91
+ }
32
92
 
33
93
  // src/ThemeProvider.tsx
34
94
  import {
35
95
  useCallback,
36
96
  useEffect,
37
- useMemo,
97
+ useMemo as useMemo2,
38
98
  useRef,
39
99
  useState
40
100
  } from "react";
@@ -114,12 +174,16 @@ function ThemeProvider({
114
174
  mq.addEventListener("change", handler);
115
175
  return () => mq.removeEventListener("change", handler);
116
176
  }, []);
177
+ useEffect(() => {
178
+ if (!configOverride) return;
179
+ setResolvedConfig(buildConfig(configOverride));
180
+ }, [configOverride]);
117
181
  useEffect(() => {
118
182
  if (configOverride) return;
119
183
  const unsub = onConfigChange(setResolvedConfig);
120
184
  return unsub;
121
185
  }, [configOverride]);
122
- const contextValue = useMemo(
186
+ const contextValue = useMemo2(
123
187
  () => ({ mode, resolvedMode, isDark, setMode, toggle, config: resolvedConfig }),
124
188
  [mode, resolvedMode, isDark, setMode, toggle, resolvedConfig]
125
189
  );
@@ -270,7 +334,7 @@ import React3, {
270
334
  forwardRef,
271
335
  useState as useState2,
272
336
  useCallback as useCallback3,
273
- useMemo as useMemo2
337
+ useMemo as useMemo3
274
338
  } from "react";
275
339
  function styled(Component, baseClasses = "") {
276
340
  const Styled = forwardRef(
@@ -315,8 +379,8 @@ function styled(Component, baseClasses = "") {
315
379
  onBlur?.(e);
316
380
  }, [onBlur]);
317
381
  const combined = extraClasses ? `${baseClasses} ${extraClasses}` : baseClasses;
318
- const resolved = useMemo2(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
319
- const computedStyle = useMemo2(
382
+ const resolved = useMemo3(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
383
+ const computedStyle = useMemo3(
320
384
  () => flatten(resolved, isDark, { pressed, hover: hovered, focus: focused }),
321
385
  [resolved, isDark, pressed, hovered, focused]
322
386
  );
@@ -326,8 +390,9 @@ function styled(Component, baseClasses = "") {
326
390
  ref,
327
391
  ...rest,
328
392
  style: finalStyle,
329
- onPressIn: handlePressIn,
330
- onPressOut: handlePressOut,
393
+ // onPressIn / onPressOut are React Native-only events; forwarding them to
394
+ // a DOM element on web triggers React's unknown-prop warning.
395
+ ...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
331
396
  onMouseEnter: handleMouseEnter,
332
397
  onMouseLeave: handleMouseLeave,
333
398
  onFocus: handleFocus,
@@ -341,11 +406,11 @@ function styled(Component, baseClasses = "") {
341
406
  }
342
407
 
343
408
  // src/useStyles.ts
344
- import { useMemo as useMemo3 } from "react";
409
+ import { useMemo as useMemo4 } from "react";
345
410
  function useStyles(classString, state = {}) {
346
411
  const { isDark, config } = useTheme();
347
412
  const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
348
- return useMemo3(() => {
413
+ return useMemo4(() => {
349
414
  const resolved = resolve(normalised, config.theme, config.darkMode);
350
415
  return flatten(resolved, isDark, state);
351
416
  }, [normalised, isDark, config, state.hover, state.focus, state.pressed, state.active, state.disabled, state.checked, state.visited, state.placeholder]);
@@ -353,14 +418,14 @@ function useStyles(classString, state = {}) {
353
418
  function useResolvedStyle(classString) {
354
419
  const { config } = useTheme();
355
420
  const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
356
- return useMemo3(
421
+ return useMemo4(
357
422
  () => resolve(normalised, config.theme, config.darkMode),
358
423
  [normalised, config]
359
424
  );
360
425
  }
361
426
 
362
- // src/tw.ts
363
- function tw(classString, isDark = false) {
427
+ // src/kb.ts
428
+ function kb(classString, isDark = false) {
364
429
  const config = getConfig();
365
430
  const resolved = resolve(classString, config.theme, config.darkMode);
366
431
  if (isWeb) {
@@ -382,14 +447,17 @@ export {
382
447
  defaultTheme,
383
448
  flatten,
384
449
  getConfig,
450
+ kb,
385
451
  parseClass,
386
452
  parseClasses,
387
453
  resolve,
388
454
  styled,
389
- tw,
390
455
  updateConfig,
456
+ useColors,
391
457
  useGlobalDarkMode,
458
+ useIsDark,
392
459
  useResolvedStyle,
393
460
  useStyles,
394
- useTheme
461
+ useTheme,
462
+ wrapColors
395
463
  };
@@ -598,6 +598,13 @@ function escapeCSSSelector(cls) {
598
598
  }
599
599
 
600
600
  // src/core/parser.ts
601
+ var PLUGIN_MODIFIERS = /* @__PURE__ */ new Set();
602
+ function registerModifier(name) {
603
+ PLUGIN_MODIFIERS.add(name);
604
+ }
605
+ function clearPluginModifiers() {
606
+ PLUGIN_MODIFIERS.clear();
607
+ }
601
608
  var MODIFIERS = /* @__PURE__ */ new Set([
602
609
  // Theme
603
610
  "dark",
@@ -1027,7 +1034,7 @@ function parseClass(className) {
1027
1034
  const colonIdx = findOuterColon(remaining);
1028
1035
  if (colonIdx === -1) break;
1029
1036
  const candidate = remaining.slice(0, colonIdx);
1030
- if (MODIFIERS.has(candidate)) {
1037
+ if (MODIFIERS.has(candidate) || PLUGIN_MODIFIERS.has(candidate)) {
1031
1038
  modifiers.push(candidate);
1032
1039
  remaining = remaining.slice(colonIdx + 1);
1033
1040
  } else {
@@ -1493,43 +1500,64 @@ var RESOLVERS = {
1493
1500
  // ── Border width ───────────────────────────────────────────────────────────
1494
1501
  border: ({ value, isArbitrary }, { colors, borderWidth, spacing }) => {
1495
1502
  if (!value) return { borderWidth: borderWidth["DEFAULT"] ?? 1 };
1496
- const color = resolveColor(value, colors, isArbitrary);
1497
- if (color) return { borderColor: color };
1498
1503
  if (isArbitrary) {
1499
1504
  const w2 = toNativeValue(value);
1500
- return { borderWidth: typeof w2 === "number" ? w2 : 1 };
1505
+ if (typeof w2 === "number") return { borderWidth: w2 };
1506
+ return { borderColor: value };
1501
1507
  }
1508
+ const color = resolveColor(value, colors, false);
1509
+ if (color) return { borderColor: color };
1502
1510
  const w = borderWidth[value] ?? spacing[value];
1503
1511
  if (w !== void 0) return { borderWidth: typeof w === "number" ? w : parseFloat(String(w)) };
1504
1512
  return null;
1505
1513
  },
1506
1514
  "border-t": ({ value, isArbitrary }, { colors, borderWidth }) => {
1507
1515
  if (!value) return { borderTopWidth: 1 };
1508
- const color = resolveColor(value, colors, isArbitrary);
1516
+ if (isArbitrary) {
1517
+ const w2 = toNativeValue(value);
1518
+ if (typeof w2 === "number") return { borderTopWidth: w2 };
1519
+ return { borderTopColor: value };
1520
+ }
1521
+ const color = resolveColor(value, colors, false);
1509
1522
  if (color) return { borderTopColor: color };
1510
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1511
- return w !== null ? { borderTopWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1523
+ const w = borderWidth[value];
1524
+ return w !== void 0 ? { borderTopWidth: w } : null;
1512
1525
  },
1513
1526
  "border-r": ({ value, isArbitrary }, { colors, borderWidth }) => {
1514
1527
  if (!value) return { borderRightWidth: 1 };
1515
- const color = resolveColor(value, colors, isArbitrary);
1528
+ if (isArbitrary) {
1529
+ const w2 = toNativeValue(value);
1530
+ if (typeof w2 === "number") return { borderRightWidth: w2 };
1531
+ return { borderRightColor: value };
1532
+ }
1533
+ const color = resolveColor(value, colors, false);
1516
1534
  if (color) return { borderRightColor: color };
1517
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1518
- return w !== null ? { borderRightWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1535
+ const w = borderWidth[value];
1536
+ return w !== void 0 ? { borderRightWidth: w } : null;
1519
1537
  },
1520
1538
  "border-b": ({ value, isArbitrary }, { colors, borderWidth }) => {
1521
1539
  if (!value) return { borderBottomWidth: 1 };
1522
- const color = resolveColor(value, colors, isArbitrary);
1540
+ if (isArbitrary) {
1541
+ const w2 = toNativeValue(value);
1542
+ if (typeof w2 === "number") return { borderBottomWidth: w2 };
1543
+ return { borderBottomColor: value };
1544
+ }
1545
+ const color = resolveColor(value, colors, false);
1523
1546
  if (color) return { borderBottomColor: color };
1524
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1525
- return w !== null ? { borderBottomWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1547
+ const w = borderWidth[value];
1548
+ return w !== void 0 ? { borderBottomWidth: w } : null;
1526
1549
  },
1527
1550
  "border-l": ({ value, isArbitrary }, { colors, borderWidth }) => {
1528
1551
  if (!value) return { borderLeftWidth: 1 };
1529
- const color = resolveColor(value, colors, isArbitrary);
1552
+ if (isArbitrary) {
1553
+ const w2 = toNativeValue(value);
1554
+ if (typeof w2 === "number") return { borderLeftWidth: w2 };
1555
+ return { borderLeftColor: value };
1556
+ }
1557
+ const color = resolveColor(value, colors, false);
1530
1558
  if (color) return { borderLeftColor: color };
1531
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1532
- return w !== null ? { borderLeftWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1559
+ const w = borderWidth[value];
1560
+ return w !== void 0 ? { borderLeftWidth: w } : null;
1533
1561
  },
1534
1562
  // ── Border radius ──────────────────────────────────────────────────────────
1535
1563
  rounded: ({ value, isArbitrary }, { borderRadius }) => {
@@ -1594,7 +1622,6 @@ var RESOLVERS = {
1594
1622
  return { flexShrink: isNaN(n) ? 1 : n };
1595
1623
  },
1596
1624
  order: ({ value, isArbitrary }, _) => {
1597
- if (isArbitrary) return { order: parseInt(value) };
1598
1625
  const n = parseInt(value);
1599
1626
  return isNaN(n) ? null : { order: n };
1600
1627
  },
@@ -1642,9 +1669,15 @@ var RESOLVERS = {
1642
1669
  },
1643
1670
  // ── Z-index ────────────────────────────────────────────────────────────────
1644
1671
  z: ({ value, isArbitrary }, { zIndex }) => {
1645
- if (isArbitrary) return { zIndex: parseInt(value) };
1672
+ if (isArbitrary) {
1673
+ const n2 = parseInt(value);
1674
+ return isNaN(n2) ? null : { zIndex: n2 };
1675
+ }
1646
1676
  const v = zIndex[value];
1647
- if (v !== void 0) return { zIndex: v };
1677
+ if (v !== void 0) {
1678
+ if (v === "auto") return isWeb ? { zIndex: "auto" } : null;
1679
+ return { zIndex: v };
1680
+ }
1648
1681
  const n = parseInt(value);
1649
1682
  return isNaN(n) ? null : { zIndex: n };
1650
1683
  },
@@ -1687,25 +1720,30 @@ var RESOLVERS = {
1687
1720
  return shadow[key] ?? null;
1688
1721
  },
1689
1722
  // ── Scale ──────────────────────────────────────────────────────────────────
1723
+ // Non-arbitrary: scale-150 → value='150' → 150/100 = 1.5
1724
+ // Arbitrary: scale-[1.5] → value='1.5' → used as-is (already the factor)
1690
1725
  scale: ({ value, isArbitrary }) => {
1691
- const n = isArbitrary ? parseFloat(value) / 100 : parseFloat(value) / 100;
1726
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1692
1727
  if (isNaN(n)) return null;
1693
1728
  return isWeb ? { transform: `scale(${n})` } : { transform: [{ scale: n }] };
1694
1729
  },
1695
1730
  "scale-x": ({ value, isArbitrary }) => {
1696
- const n = isArbitrary ? parseFloat(value) / 100 : parseFloat(value) / 100;
1731
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1697
1732
  if (isNaN(n)) return null;
1698
1733
  return isWeb ? { transform: `scaleX(${n})` } : { transform: [{ scaleX: n }] };
1699
1734
  },
1700
1735
  "scale-y": ({ value, isArbitrary }) => {
1701
- const n = isArbitrary ? parseFloat(value) / 100 : parseFloat(value) / 100;
1736
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1702
1737
  if (isNaN(n)) return null;
1703
1738
  return isWeb ? { transform: `scaleY(${n})` } : { transform: [{ scaleY: n }] };
1704
1739
  },
1705
1740
  // ── Rotate ─────────────────────────────────────────────────────────────────
1706
1741
  rotate: ({ value, negative, isArbitrary }) => {
1707
- const raw = isArbitrary ? value : `${value}deg`;
1708
- const deg = parseFloat(raw);
1742
+ if (isArbitrary) {
1743
+ const finalValue = negative ? `-${value}` : value;
1744
+ return isWeb ? { transform: `rotate(${finalValue})` } : { transform: [{ rotate: finalValue }] };
1745
+ }
1746
+ const deg = parseFloat(value);
1709
1747
  if (isNaN(deg)) return null;
1710
1748
  const finalDeg = negative ? -deg : deg;
1711
1749
  return isWeb ? { transform: `rotate(${finalDeg}deg)` } : { transform: [{ rotate: `${finalDeg}deg` }] };
@@ -1714,18 +1752,31 @@ var RESOLVERS = {
1714
1752
  "translate-x": ({ value, negative, isArbitrary }, { spacing }) => {
1715
1753
  const v = resolveSpacing(value, negative, spacing, isArbitrary);
1716
1754
  if (v === null) return null;
1717
- return isWeb ? { transform: `translateX(${v})` } : { transform: [{ translateX: typeof v === "string" ? parseFloat(v) : v }] };
1755
+ if (isWeb) return { transform: `translateX(${v})` };
1756
+ if (typeof v === "string") {
1757
+ const n = parseFloat(v);
1758
+ return isNaN(n) ? null : { transform: [{ translateX: n }] };
1759
+ }
1760
+ return { transform: [{ translateX: v }] };
1718
1761
  },
1719
1762
  "translate-y": ({ value, negative, isArbitrary }, { spacing }) => {
1720
1763
  const v = resolveSpacing(value, negative, spacing, isArbitrary);
1721
1764
  if (v === null) return null;
1722
- return isWeb ? { transform: `translateY(${v})` } : { transform: [{ translateY: typeof v === "string" ? parseFloat(v) : v }] };
1765
+ if (isWeb) return { transform: `translateY(${v})` };
1766
+ if (typeof v === "string") {
1767
+ const n = parseFloat(v);
1768
+ return isNaN(n) ? null : { transform: [{ translateY: n }] };
1769
+ }
1770
+ return { transform: [{ translateY: v }] };
1723
1771
  },
1724
1772
  // ── Aspect ratio ───────────────────────────────────────────────────────────
1725
1773
  aspect: ({ value, isArbitrary }) => {
1726
1774
  if (isArbitrary) return { aspectRatio: value };
1727
1775
  const presets = { auto: "auto", square: 1, video: 16 / 9 };
1728
- return value in presets ? { aspectRatio: presets[value] } : null;
1776
+ if (!(value in presets)) return null;
1777
+ const v = presets[value];
1778
+ if (v === "auto") return isWeb ? { aspectRatio: "auto" } : null;
1779
+ return { aspectRatio: v };
1729
1780
  },
1730
1781
  // ── Transition (web-only; use Animated API on native) ─────────────────────
1731
1782
  transition: ({ value }) => {
@@ -1802,18 +1853,25 @@ var RESOLVERS = {
1802
1853
  }
1803
1854
  };
1804
1855
  function resolveUtility(parsed, theme) {
1805
- if (!parsed.value && parsed.utility in STANDALONE) {
1806
- return STANDALONE[parsed.utility] ?? null;
1856
+ if (!parsed.value) {
1857
+ if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
1858
+ if (parsed.utility in STANDALONE) return STANDALONE[parsed.utility] ?? null;
1807
1859
  }
1808
- const resolver = RESOLVERS[parsed.utility];
1860
+ const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
1809
1861
  if (resolver) return resolver(parsed, theme);
1810
1862
  return null;
1811
1863
  }
1812
- function getStandaloneMap() {
1813
- return STANDALONE;
1864
+ var PLUGIN_STANDALONE = {};
1865
+ var PLUGIN_RESOLVERS = {};
1866
+ function clearPluginUtilities() {
1867
+ for (const key of Object.keys(PLUGIN_STANDALONE)) delete PLUGIN_STANDALONE[key];
1868
+ for (const key of Object.keys(PLUGIN_RESOLVERS)) delete PLUGIN_RESOLVERS[key];
1814
1869
  }
1815
- function getResolverMap() {
1816
- return RESOLVERS;
1870
+ function getPluginStandaloneMap() {
1871
+ return PLUGIN_STANDALONE;
1872
+ }
1873
+ function getPluginResolverMap() {
1874
+ return PLUGIN_RESOLVERS;
1817
1875
  }
1818
1876
 
1819
1877
  // src/core/resolver.ts
@@ -1830,8 +1888,42 @@ function getStyleEl() {
1830
1888
  function injectRule(rule) {
1831
1889
  if (_injectedRules.has(rule)) return;
1832
1890
  _injectedRules.add(rule);
1833
- getStyleEl().sheet?.insertRule(rule, getStyleEl().sheet.cssRules.length);
1891
+ try {
1892
+ getStyleEl().sheet?.insertRule(rule, getStyleEl().sheet.cssRules.length);
1893
+ } catch {
1894
+ }
1834
1895
  }
1896
+ var MODIFIER_TO_PSEUDO = {
1897
+ hover: ":hover",
1898
+ focus: ":focus",
1899
+ "focus-within": ":focus-within",
1900
+ "focus-visible": ":focus-visible",
1901
+ active: ":active",
1902
+ pressed: ":active",
1903
+ disabled: ":disabled",
1904
+ checked: ":checked",
1905
+ visited: ":visited",
1906
+ placeholder: "::placeholder",
1907
+ first: ":first-child",
1908
+ last: ":last-child",
1909
+ odd: ":nth-child(odd)",
1910
+ even: ":nth-child(even)",
1911
+ only: ":only-child",
1912
+ "not-hover": ":not(:hover)",
1913
+ "not-focus": ":not(:focus)",
1914
+ "not-active": ":not(:active)",
1915
+ "not-pressed": ":not(:active)",
1916
+ "not-disabled": ":not(:disabled)",
1917
+ "not-checked": ":not(:checked)",
1918
+ "not-visited": ":not(:visited)"
1919
+ };
1920
+ var GROUP_PEER_PREFIX = {
1921
+ "group-hover": ".group:hover",
1922
+ "group-focus": ".group:focus",
1923
+ "peer-hover": ".peer:hover ~",
1924
+ "peer-focus": ".peer:focus ~"
1925
+ };
1926
+ var MODE_MODS = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
1835
1927
  function injectClassRule(cls, bucketKey, styles, darkMode) {
1836
1928
  const cssDecls = styleValueToCSS(styles);
1837
1929
  if (!cssDecls) return;
@@ -1841,10 +1933,14 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1841
1933
  return;
1842
1934
  }
1843
1935
  const mods = bucketKey.split(":");
1844
- const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1845
- const hasDark = mods.includes("dark");
1846
- const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1847
- const selector = `.${escaped}${pseudoSuffix}`;
1936
+ const hasDark = mods.includes("dark") || mods.includes("not-light");
1937
+ const hasLight = mods.includes("light") || mods.includes("not-dark");
1938
+ const ancestorMods = mods.filter((m) => m in GROUP_PEER_PREFIX);
1939
+ const pseudoMods = mods.filter((m) => !MODE_MODS.has(m) && !(m in GROUP_PEER_PREFIX));
1940
+ const pseudoSuffix = pseudoMods.map((p) => MODIFIER_TO_PSEUDO[p] ?? `:${p}`).join("");
1941
+ const elementSelector = `.${escaped}${pseudoSuffix}`;
1942
+ const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
1943
+ const selector = `${ancestorPrefix}${elementSelector}`;
1848
1944
  let rule;
1849
1945
  if (hasDark) {
1850
1946
  if (darkMode === "media") {
@@ -1854,13 +1950,54 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1854
1950
  } else {
1855
1951
  rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1856
1952
  }
1953
+ } else if (hasLight) {
1954
+ if (darkMode === "media") {
1955
+ rule = `@media (prefers-color-scheme: light) { ${selector} { ${cssDecls} } }`;
1956
+ } else if (darkMode === "class") {
1957
+ rule = `:not(.dark) ${selector} { ${cssDecls} }`;
1958
+ } else {
1959
+ rule = `[data-theme="light"] ${selector} { ${cssDecls} }`;
1960
+ }
1857
1961
  } else {
1858
1962
  rule = `${selector} { ${cssDecls} }`;
1859
1963
  }
1860
1964
  injectRule(rule);
1861
1965
  }
1966
+ var RN_ONLY_PROPS = /* @__PURE__ */ new Set([
1967
+ "shadowColor",
1968
+ "shadowOffset",
1969
+ "shadowOpacity",
1970
+ "shadowRadius",
1971
+ "elevation",
1972
+ "includeFontPadding",
1973
+ "textAlignVertical",
1974
+ "writingDirection"
1975
+ ]);
1976
+ var CSS_UNITLESS = /* @__PURE__ */ new Set([
1977
+ "opacity",
1978
+ "fontWeight",
1979
+ "flex",
1980
+ "flexGrow",
1981
+ "flexShrink",
1982
+ "order",
1983
+ "zIndex",
1984
+ "aspectRatio",
1985
+ "columnCount",
1986
+ "lineHeight"
1987
+ ]);
1862
1988
  function styleValueToCSS(styles) {
1863
- return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== null && typeof v !== "object").map(([prop, val]) => `${camelToKebab(prop)}: ${val}`).join("; ");
1989
+ return Object.entries(styles).filter(
1990
+ ([prop, v]) => v !== void 0 && v !== null && typeof v !== "object" && !RN_ONLY_PROPS.has(prop)
1991
+ ).map(([prop, val]) => {
1992
+ const cssProp = camelToKebab(prop);
1993
+ let cssVal;
1994
+ if (typeof val === "number") {
1995
+ cssVal = val === 0 || CSS_UNITLESS.has(prop) ? String(val) : `${val}px`;
1996
+ } else {
1997
+ cssVal = String(val);
1998
+ }
1999
+ return `${cssProp}: ${cssVal}`;
2000
+ }).join("; ");
1864
2001
  }
1865
2002
  function camelToKebab(str) {
1866
2003
  return str.replace(/([A-Z])/g, "-$1").toLowerCase();
@@ -2001,20 +2138,24 @@ function buildConfig(userConfig) {
2001
2138
  theme,
2002
2139
  plugins: userConfig.plugins ?? []
2003
2140
  };
2141
+ clearPluginUtilities();
2142
+ clearPluginModifiers();
2143
+ const pluginAPI = makePluginAPI(resolved.theme);
2004
2144
  for (const plugin of resolved.plugins) {
2005
- plugin(makePluginAPI(resolved.theme));
2145
+ plugin(pluginAPI);
2006
2146
  }
2007
2147
  return resolved;
2008
2148
  }
2009
2149
  function makePluginAPI(theme) {
2010
- const standalone = getStandaloneMap();
2011
- const resolvers = getResolverMap();
2150
+ const standalone = getPluginStandaloneMap();
2151
+ const resolvers = getPluginResolverMap();
2012
2152
  return {
2013
2153
  addUtility(name, styles) {
2014
2154
  standalone[name] = styles;
2015
2155
  },
2016
- addVariant(name, _selector) {
2017
- customVariants[name] = _selector;
2156
+ addVariant(name, selector) {
2157
+ customVariants[name] = selector;
2158
+ registerModifier(name);
2018
2159
  },
2019
2160
  theme(path, defaultValue) {
2020
2161
  const parts = path.replace(/\[([^\]]+)\]/g, ".$1").split(".");
@@ -2130,8 +2271,8 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
2130
2271
  ...restWithoutChildren,
2131
2272
  style: finalStyle,
2132
2273
  ...isWeb && className ? { className } : {},
2133
- onPressIn: handlePressIn,
2134
- onPressOut: handlePressOut,
2274
+ // onPressIn / onPressOut are React Native-only; skip on web to avoid DOM warnings.
2275
+ ...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
2135
2276
  onMouseEnter: handleMouseEnter,
2136
2277
  onMouseLeave: handleMouseLeave,
2137
2278
  onFocus: handleFocus,
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-QMJ4XUJA.mjs";
6
- import "./chunk-LYGD2GSW.mjs";
5
+ } from "./chunk-Q5RMVTAU.mjs";
6
+ import "./chunk-V5KHX4CZ.mjs";
7
7
 
8
8
  // src/jsx-dev-runtime.tsx
9
9
  function jsxDEV(type, props, key, isStaticChildren, _source, _self) {
@@ -5,7 +5,7 @@ import { ReactElement } from 'react';
5
5
  * @kbach/react/jsx-runtime
6
6
  *
7
7
  * Drop-in replacement for react/jsx-runtime.
8
- * Intercepts `className` and `tw` props on EVERY JSX element — React Native
8
+ * Intercepts `className` and `kb` props on EVERY JSX element — React Native
9
9
  * built-ins, HTML elements on web, and any third-party library component.
10
10
  */
11
11
 
@@ -5,7 +5,7 @@ import { ReactElement } from 'react';
5
5
  * @kbach/react/jsx-runtime
6
6
  *
7
7
  * Drop-in replacement for react/jsx-runtime.
8
- * Intercepts `className` and `tw` props on EVERY JSX element — React Native
8
+ * Intercepts `className` and `kb` props on EVERY JSX element — React Native
9
9
  * built-ins, HTML elements on web, and any third-party library component.
10
10
  */
11
11