@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.js CHANGED
@@ -40,16 +40,19 @@ __export(index_exports, {
40
40
  defaultTheme: () => defaultTheme,
41
41
  flatten: () => flatten,
42
42
  getConfig: () => getConfig,
43
+ kb: () => kb,
43
44
  parseClass: () => parseClass,
44
45
  parseClasses: () => parseClasses,
45
46
  resolve: () => resolve,
46
47
  styled: () => styled,
47
- tw: () => tw,
48
48
  updateConfig: () => updateConfig,
49
+ useColors: () => useColors,
49
50
  useGlobalDarkMode: () => useGlobalDarkMode,
51
+ useIsDark: () => useIsDark,
50
52
  useResolvedStyle: () => useResolvedStyle,
51
53
  useStyles: () => useStyles,
52
- useTheme: () => useTheme
54
+ useTheme: () => useTheme,
55
+ wrapColors: () => wrapColors
53
56
  });
54
57
  module.exports = __toCommonJS(index_exports);
55
58
 
@@ -65,9 +68,69 @@ function useTheme() {
65
68
  }
66
69
  return ctx;
67
70
  }
71
+ function useIsDark() {
72
+ return useTheme().isDark;
73
+ }
68
74
 
69
- // src/ThemeProvider.tsx
75
+ // src/useColors.ts
70
76
  var import_react2 = require("react");
77
+ function applyOpacity(color, opacity) {
78
+ const a = Math.max(0, Math.min(1, opacity / 100));
79
+ if (color.startsWith("#")) {
80
+ const h = color.slice(1);
81
+ 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)];
82
+ return `rgba(${parseInt(rs, 16)},${parseInt(gs, 16)},${parseInt(bs, 16)},${a})`;
83
+ }
84
+ if (color.startsWith("rgb(")) return color.replace("rgb(", "rgba(").replace(")", `,${a})`);
85
+ if (color.startsWith("rgba(")) return color.replace(/,\s*[\d.]+\)$/, `,${a})`);
86
+ return color;
87
+ }
88
+ function makeShadeProxy(shades) {
89
+ return new Proxy(shades, {
90
+ get(target, prop) {
91
+ const key = String(prop);
92
+ if (key === "then") return void 0;
93
+ if (key.includes("/")) {
94
+ const slash = key.indexOf("/");
95
+ const shade = key.slice(0, slash);
96
+ const op = Number(key.slice(slash + 1));
97
+ const color = target[shade];
98
+ return typeof color === "string" ? applyOpacity(color, op) : void 0;
99
+ }
100
+ return target[key];
101
+ }
102
+ });
103
+ }
104
+ function wrapColors(rawColors) {
105
+ const cache = /* @__PURE__ */ new Map();
106
+ const alpha = (color, opacity) => applyOpacity(color, opacity);
107
+ return new Proxy({ alpha }, {
108
+ get(_, prop) {
109
+ const key = String(prop);
110
+ if (key === "then") return void 0;
111
+ if (key === "alpha") return alpha;
112
+ if (key.includes("/")) {
113
+ const slash = key.indexOf("/");
114
+ const name = key.slice(0, slash);
115
+ const op = Number(key.slice(slash + 1));
116
+ const entry2 = rawColors[name];
117
+ return typeof entry2 === "string" ? applyOpacity(entry2, op) : void 0;
118
+ }
119
+ const entry = rawColors[key];
120
+ if (entry === void 0) return void 0;
121
+ if (typeof entry === "string") return entry;
122
+ if (!cache.has(key)) cache.set(key, makeShadeProxy(entry));
123
+ return cache.get(key);
124
+ }
125
+ });
126
+ }
127
+ function useColors() {
128
+ const { config } = useTheme();
129
+ return (0, import_react2.useMemo)(() => wrapColors(config.theme.colors), [config.theme.colors]);
130
+ }
131
+
132
+ // src/ThemeProvider.tsx
133
+ var import_react3 = require("react");
71
134
 
72
135
  // src/core/theme.ts
73
136
  var defaultColors = {
@@ -629,6 +692,13 @@ function escapeCSSSelector(cls) {
629
692
  }
630
693
 
631
694
  // src/core/parser.ts
695
+ var PLUGIN_MODIFIERS = /* @__PURE__ */ new Set();
696
+ function registerModifier(name) {
697
+ PLUGIN_MODIFIERS.add(name);
698
+ }
699
+ function clearPluginModifiers() {
700
+ PLUGIN_MODIFIERS.clear();
701
+ }
632
702
  var MODIFIERS = /* @__PURE__ */ new Set([
633
703
  // Theme
634
704
  "dark",
@@ -1058,7 +1128,7 @@ function parseClass(className) {
1058
1128
  const colonIdx = findOuterColon(remaining);
1059
1129
  if (colonIdx === -1) break;
1060
1130
  const candidate = remaining.slice(0, colonIdx);
1061
- if (MODIFIERS.has(candidate)) {
1131
+ if (MODIFIERS.has(candidate) || PLUGIN_MODIFIERS.has(candidate)) {
1062
1132
  modifiers.push(candidate);
1063
1133
  remaining = remaining.slice(colonIdx + 1);
1064
1134
  } else {
@@ -1524,43 +1594,64 @@ var RESOLVERS = {
1524
1594
  // ── Border width ───────────────────────────────────────────────────────────
1525
1595
  border: ({ value, isArbitrary }, { colors, borderWidth, spacing }) => {
1526
1596
  if (!value) return { borderWidth: borderWidth["DEFAULT"] ?? 1 };
1527
- const color = resolveColor(value, colors, isArbitrary);
1528
- if (color) return { borderColor: color };
1529
1597
  if (isArbitrary) {
1530
1598
  const w2 = toNativeValue(value);
1531
- return { borderWidth: typeof w2 === "number" ? w2 : 1 };
1599
+ if (typeof w2 === "number") return { borderWidth: w2 };
1600
+ return { borderColor: value };
1532
1601
  }
1602
+ const color = resolveColor(value, colors, false);
1603
+ if (color) return { borderColor: color };
1533
1604
  const w = borderWidth[value] ?? spacing[value];
1534
1605
  if (w !== void 0) return { borderWidth: typeof w === "number" ? w : parseFloat(String(w)) };
1535
1606
  return null;
1536
1607
  },
1537
1608
  "border-t": ({ value, isArbitrary }, { colors, borderWidth }) => {
1538
1609
  if (!value) return { borderTopWidth: 1 };
1539
- const color = resolveColor(value, colors, isArbitrary);
1610
+ if (isArbitrary) {
1611
+ const w2 = toNativeValue(value);
1612
+ if (typeof w2 === "number") return { borderTopWidth: w2 };
1613
+ return { borderTopColor: value };
1614
+ }
1615
+ const color = resolveColor(value, colors, false);
1540
1616
  if (color) return { borderTopColor: color };
1541
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1542
- return w !== null ? { borderTopWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1617
+ const w = borderWidth[value];
1618
+ return w !== void 0 ? { borderTopWidth: w } : null;
1543
1619
  },
1544
1620
  "border-r": ({ value, isArbitrary }, { colors, borderWidth }) => {
1545
1621
  if (!value) return { borderRightWidth: 1 };
1546
- const color = resolveColor(value, colors, isArbitrary);
1622
+ if (isArbitrary) {
1623
+ const w2 = toNativeValue(value);
1624
+ if (typeof w2 === "number") return { borderRightWidth: w2 };
1625
+ return { borderRightColor: value };
1626
+ }
1627
+ const color = resolveColor(value, colors, false);
1547
1628
  if (color) return { borderRightColor: color };
1548
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1549
- return w !== null ? { borderRightWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1629
+ const w = borderWidth[value];
1630
+ return w !== void 0 ? { borderRightWidth: w } : null;
1550
1631
  },
1551
1632
  "border-b": ({ value, isArbitrary }, { colors, borderWidth }) => {
1552
1633
  if (!value) return { borderBottomWidth: 1 };
1553
- const color = resolveColor(value, colors, isArbitrary);
1634
+ if (isArbitrary) {
1635
+ const w2 = toNativeValue(value);
1636
+ if (typeof w2 === "number") return { borderBottomWidth: w2 };
1637
+ return { borderBottomColor: value };
1638
+ }
1639
+ const color = resolveColor(value, colors, false);
1554
1640
  if (color) return { borderBottomColor: color };
1555
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1556
- return w !== null ? { borderBottomWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1641
+ const w = borderWidth[value];
1642
+ return w !== void 0 ? { borderBottomWidth: w } : null;
1557
1643
  },
1558
1644
  "border-l": ({ value, isArbitrary }, { colors, borderWidth }) => {
1559
1645
  if (!value) return { borderLeftWidth: 1 };
1560
- const color = resolveColor(value, colors, isArbitrary);
1646
+ if (isArbitrary) {
1647
+ const w2 = toNativeValue(value);
1648
+ if (typeof w2 === "number") return { borderLeftWidth: w2 };
1649
+ return { borderLeftColor: value };
1650
+ }
1651
+ const color = resolveColor(value, colors, false);
1561
1652
  if (color) return { borderLeftColor: color };
1562
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1563
- return w !== null ? { borderLeftWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1653
+ const w = borderWidth[value];
1654
+ return w !== void 0 ? { borderLeftWidth: w } : null;
1564
1655
  },
1565
1656
  // ── Border radius ──────────────────────────────────────────────────────────
1566
1657
  rounded: ({ value, isArbitrary }, { borderRadius }) => {
@@ -1625,7 +1716,6 @@ var RESOLVERS = {
1625
1716
  return { flexShrink: isNaN(n) ? 1 : n };
1626
1717
  },
1627
1718
  order: ({ value, isArbitrary }, _) => {
1628
- if (isArbitrary) return { order: parseInt(value) };
1629
1719
  const n = parseInt(value);
1630
1720
  return isNaN(n) ? null : { order: n };
1631
1721
  },
@@ -1673,9 +1763,15 @@ var RESOLVERS = {
1673
1763
  },
1674
1764
  // ── Z-index ────────────────────────────────────────────────────────────────
1675
1765
  z: ({ value, isArbitrary }, { zIndex }) => {
1676
- if (isArbitrary) return { zIndex: parseInt(value) };
1766
+ if (isArbitrary) {
1767
+ const n2 = parseInt(value);
1768
+ return isNaN(n2) ? null : { zIndex: n2 };
1769
+ }
1677
1770
  const v = zIndex[value];
1678
- if (v !== void 0) return { zIndex: v };
1771
+ if (v !== void 0) {
1772
+ if (v === "auto") return isWeb ? { zIndex: "auto" } : null;
1773
+ return { zIndex: v };
1774
+ }
1679
1775
  const n = parseInt(value);
1680
1776
  return isNaN(n) ? null : { zIndex: n };
1681
1777
  },
@@ -1718,25 +1814,30 @@ var RESOLVERS = {
1718
1814
  return shadow[key] ?? null;
1719
1815
  },
1720
1816
  // ── Scale ──────────────────────────────────────────────────────────────────
1817
+ // Non-arbitrary: scale-150 → value='150' → 150/100 = 1.5
1818
+ // Arbitrary: scale-[1.5] → value='1.5' → used as-is (already the factor)
1721
1819
  scale: ({ value, isArbitrary }) => {
1722
- const n = isArbitrary ? parseFloat(value) / 100 : parseFloat(value) / 100;
1820
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1723
1821
  if (isNaN(n)) return null;
1724
1822
  return isWeb ? { transform: `scale(${n})` } : { transform: [{ scale: n }] };
1725
1823
  },
1726
1824
  "scale-x": ({ value, isArbitrary }) => {
1727
- const n = isArbitrary ? parseFloat(value) / 100 : parseFloat(value) / 100;
1825
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1728
1826
  if (isNaN(n)) return null;
1729
1827
  return isWeb ? { transform: `scaleX(${n})` } : { transform: [{ scaleX: n }] };
1730
1828
  },
1731
1829
  "scale-y": ({ value, isArbitrary }) => {
1732
- const n = isArbitrary ? parseFloat(value) / 100 : parseFloat(value) / 100;
1830
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1733
1831
  if (isNaN(n)) return null;
1734
1832
  return isWeb ? { transform: `scaleY(${n})` } : { transform: [{ scaleY: n }] };
1735
1833
  },
1736
1834
  // ── Rotate ─────────────────────────────────────────────────────────────────
1737
1835
  rotate: ({ value, negative, isArbitrary }) => {
1738
- const raw = isArbitrary ? value : `${value}deg`;
1739
- const deg = parseFloat(raw);
1836
+ if (isArbitrary) {
1837
+ const finalValue = negative ? `-${value}` : value;
1838
+ return isWeb ? { transform: `rotate(${finalValue})` } : { transform: [{ rotate: finalValue }] };
1839
+ }
1840
+ const deg = parseFloat(value);
1740
1841
  if (isNaN(deg)) return null;
1741
1842
  const finalDeg = negative ? -deg : deg;
1742
1843
  return isWeb ? { transform: `rotate(${finalDeg}deg)` } : { transform: [{ rotate: `${finalDeg}deg` }] };
@@ -1745,18 +1846,31 @@ var RESOLVERS = {
1745
1846
  "translate-x": ({ value, negative, isArbitrary }, { spacing }) => {
1746
1847
  const v = resolveSpacing(value, negative, spacing, isArbitrary);
1747
1848
  if (v === null) return null;
1748
- return isWeb ? { transform: `translateX(${v})` } : { transform: [{ translateX: typeof v === "string" ? parseFloat(v) : v }] };
1849
+ if (isWeb) return { transform: `translateX(${v})` };
1850
+ if (typeof v === "string") {
1851
+ const n = parseFloat(v);
1852
+ return isNaN(n) ? null : { transform: [{ translateX: n }] };
1853
+ }
1854
+ return { transform: [{ translateX: v }] };
1749
1855
  },
1750
1856
  "translate-y": ({ value, negative, isArbitrary }, { spacing }) => {
1751
1857
  const v = resolveSpacing(value, negative, spacing, isArbitrary);
1752
1858
  if (v === null) return null;
1753
- return isWeb ? { transform: `translateY(${v})` } : { transform: [{ translateY: typeof v === "string" ? parseFloat(v) : v }] };
1859
+ if (isWeb) return { transform: `translateY(${v})` };
1860
+ if (typeof v === "string") {
1861
+ const n = parseFloat(v);
1862
+ return isNaN(n) ? null : { transform: [{ translateY: n }] };
1863
+ }
1864
+ return { transform: [{ translateY: v }] };
1754
1865
  },
1755
1866
  // ── Aspect ratio ───────────────────────────────────────────────────────────
1756
1867
  aspect: ({ value, isArbitrary }) => {
1757
1868
  if (isArbitrary) return { aspectRatio: value };
1758
1869
  const presets = { auto: "auto", square: 1, video: 16 / 9 };
1759
- return value in presets ? { aspectRatio: presets[value] } : null;
1870
+ if (!(value in presets)) return null;
1871
+ const v = presets[value];
1872
+ if (v === "auto") return isWeb ? { aspectRatio: "auto" } : null;
1873
+ return { aspectRatio: v };
1760
1874
  },
1761
1875
  // ── Transition (web-only; use Animated API on native) ─────────────────────
1762
1876
  transition: ({ value }) => {
@@ -1833,18 +1947,25 @@ var RESOLVERS = {
1833
1947
  }
1834
1948
  };
1835
1949
  function resolveUtility(parsed, theme) {
1836
- if (!parsed.value && parsed.utility in STANDALONE) {
1837
- return STANDALONE[parsed.utility] ?? null;
1950
+ if (!parsed.value) {
1951
+ if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
1952
+ if (parsed.utility in STANDALONE) return STANDALONE[parsed.utility] ?? null;
1838
1953
  }
1839
- const resolver = RESOLVERS[parsed.utility];
1954
+ const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
1840
1955
  if (resolver) return resolver(parsed, theme);
1841
1956
  return null;
1842
1957
  }
1843
- function getStandaloneMap() {
1844
- return STANDALONE;
1958
+ var PLUGIN_STANDALONE = {};
1959
+ var PLUGIN_RESOLVERS = {};
1960
+ function clearPluginUtilities() {
1961
+ for (const key of Object.keys(PLUGIN_STANDALONE)) delete PLUGIN_STANDALONE[key];
1962
+ for (const key of Object.keys(PLUGIN_RESOLVERS)) delete PLUGIN_RESOLVERS[key];
1963
+ }
1964
+ function getPluginStandaloneMap() {
1965
+ return PLUGIN_STANDALONE;
1845
1966
  }
1846
- function getResolverMap() {
1847
- return RESOLVERS;
1967
+ function getPluginResolverMap() {
1968
+ return PLUGIN_RESOLVERS;
1848
1969
  }
1849
1970
 
1850
1971
  // src/core/resolver.ts
@@ -1861,8 +1982,42 @@ function getStyleEl() {
1861
1982
  function injectRule(rule) {
1862
1983
  if (_injectedRules.has(rule)) return;
1863
1984
  _injectedRules.add(rule);
1864
- getStyleEl().sheet?.insertRule(rule, getStyleEl().sheet.cssRules.length);
1985
+ try {
1986
+ getStyleEl().sheet?.insertRule(rule, getStyleEl().sheet.cssRules.length);
1987
+ } catch {
1988
+ }
1865
1989
  }
1990
+ var MODIFIER_TO_PSEUDO = {
1991
+ hover: ":hover",
1992
+ focus: ":focus",
1993
+ "focus-within": ":focus-within",
1994
+ "focus-visible": ":focus-visible",
1995
+ active: ":active",
1996
+ pressed: ":active",
1997
+ disabled: ":disabled",
1998
+ checked: ":checked",
1999
+ visited: ":visited",
2000
+ placeholder: "::placeholder",
2001
+ first: ":first-child",
2002
+ last: ":last-child",
2003
+ odd: ":nth-child(odd)",
2004
+ even: ":nth-child(even)",
2005
+ only: ":only-child",
2006
+ "not-hover": ":not(:hover)",
2007
+ "not-focus": ":not(:focus)",
2008
+ "not-active": ":not(:active)",
2009
+ "not-pressed": ":not(:active)",
2010
+ "not-disabled": ":not(:disabled)",
2011
+ "not-checked": ":not(:checked)",
2012
+ "not-visited": ":not(:visited)"
2013
+ };
2014
+ var GROUP_PEER_PREFIX = {
2015
+ "group-hover": ".group:hover",
2016
+ "group-focus": ".group:focus",
2017
+ "peer-hover": ".peer:hover ~",
2018
+ "peer-focus": ".peer:focus ~"
2019
+ };
2020
+ var MODE_MODS = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
1866
2021
  function injectClassRule(cls, bucketKey, styles, darkMode) {
1867
2022
  const cssDecls = styleValueToCSS(styles);
1868
2023
  if (!cssDecls) return;
@@ -1872,10 +2027,14 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1872
2027
  return;
1873
2028
  }
1874
2029
  const mods = bucketKey.split(":");
1875
- const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1876
- const hasDark = mods.includes("dark");
1877
- const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1878
- const selector = `.${escaped}${pseudoSuffix}`;
2030
+ const hasDark = mods.includes("dark") || mods.includes("not-light");
2031
+ const hasLight = mods.includes("light") || mods.includes("not-dark");
2032
+ const ancestorMods = mods.filter((m) => m in GROUP_PEER_PREFIX);
2033
+ const pseudoMods = mods.filter((m) => !MODE_MODS.has(m) && !(m in GROUP_PEER_PREFIX));
2034
+ const pseudoSuffix = pseudoMods.map((p) => MODIFIER_TO_PSEUDO[p] ?? `:${p}`).join("");
2035
+ const elementSelector = `.${escaped}${pseudoSuffix}`;
2036
+ const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
2037
+ const selector = `${ancestorPrefix}${elementSelector}`;
1879
2038
  let rule;
1880
2039
  if (hasDark) {
1881
2040
  if (darkMode === "media") {
@@ -1885,13 +2044,54 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1885
2044
  } else {
1886
2045
  rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1887
2046
  }
2047
+ } else if (hasLight) {
2048
+ if (darkMode === "media") {
2049
+ rule = `@media (prefers-color-scheme: light) { ${selector} { ${cssDecls} } }`;
2050
+ } else if (darkMode === "class") {
2051
+ rule = `:not(.dark) ${selector} { ${cssDecls} }`;
2052
+ } else {
2053
+ rule = `[data-theme="light"] ${selector} { ${cssDecls} }`;
2054
+ }
1888
2055
  } else {
1889
2056
  rule = `${selector} { ${cssDecls} }`;
1890
2057
  }
1891
2058
  injectRule(rule);
1892
2059
  }
2060
+ var RN_ONLY_PROPS = /* @__PURE__ */ new Set([
2061
+ "shadowColor",
2062
+ "shadowOffset",
2063
+ "shadowOpacity",
2064
+ "shadowRadius",
2065
+ "elevation",
2066
+ "includeFontPadding",
2067
+ "textAlignVertical",
2068
+ "writingDirection"
2069
+ ]);
2070
+ var CSS_UNITLESS = /* @__PURE__ */ new Set([
2071
+ "opacity",
2072
+ "fontWeight",
2073
+ "flex",
2074
+ "flexGrow",
2075
+ "flexShrink",
2076
+ "order",
2077
+ "zIndex",
2078
+ "aspectRatio",
2079
+ "columnCount",
2080
+ "lineHeight"
2081
+ ]);
1893
2082
  function styleValueToCSS(styles) {
1894
- return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== null && typeof v !== "object").map(([prop, val]) => `${camelToKebab(prop)}: ${val}`).join("; ");
2083
+ return Object.entries(styles).filter(
2084
+ ([prop, v]) => v !== void 0 && v !== null && typeof v !== "object" && !RN_ONLY_PROPS.has(prop)
2085
+ ).map(([prop, val]) => {
2086
+ const cssProp = camelToKebab(prop);
2087
+ let cssVal;
2088
+ if (typeof val === "number") {
2089
+ cssVal = val === 0 || CSS_UNITLESS.has(prop) ? String(val) : `${val}px`;
2090
+ } else {
2091
+ cssVal = String(val);
2092
+ }
2093
+ return `${cssProp}: ${cssVal}`;
2094
+ }).join("; ");
1895
2095
  }
1896
2096
  function camelToKebab(str) {
1897
2097
  return str.replace(/([A-Z])/g, "-$1").toLowerCase();
@@ -1977,6 +2177,9 @@ function matchesMods(mods, isDark, state) {
1977
2177
  }
1978
2178
  });
1979
2179
  }
2180
+ function clearCache() {
2181
+ styleCache.clear();
2182
+ }
1980
2183
 
1981
2184
  // src/core/config.ts
1982
2185
  function deepMerge(base, override) {
@@ -2032,20 +2235,24 @@ function buildConfig(userConfig) {
2032
2235
  theme,
2033
2236
  plugins: userConfig.plugins ?? []
2034
2237
  };
2238
+ clearPluginUtilities();
2239
+ clearPluginModifiers();
2240
+ const pluginAPI = makePluginAPI(resolved.theme);
2035
2241
  for (const plugin of resolved.plugins) {
2036
- plugin(makePluginAPI(resolved.theme));
2242
+ plugin(pluginAPI);
2037
2243
  }
2038
2244
  return resolved;
2039
2245
  }
2040
2246
  function makePluginAPI(theme) {
2041
- const standalone = getStandaloneMap();
2042
- const resolvers = getResolverMap();
2247
+ const standalone = getPluginStandaloneMap();
2248
+ const resolvers = getPluginResolverMap();
2043
2249
  return {
2044
2250
  addUtility(name, styles) {
2045
2251
  standalone[name] = styles;
2046
2252
  },
2047
- addVariant(name, _selector) {
2048
- customVariants[name] = _selector;
2253
+ addVariant(name, selector) {
2254
+ customVariants[name] = selector;
2255
+ registerModifier(name);
2049
2256
  },
2050
2257
  theme(path, defaultValue) {
2051
2258
  const parts = path.replace(/\[([^\]]+)\]/g, ".$1").split(".");
@@ -2069,6 +2276,7 @@ function onConfigChange(listener) {
2069
2276
  function updateConfig(userConfig) {
2070
2277
  const store = getConfigStore();
2071
2278
  store.resolved = buildConfig(userConfig);
2279
+ clearCache();
2072
2280
  for (const listener of store.listeners) {
2073
2281
  listener(store.resolved);
2074
2282
  }
@@ -2156,40 +2364,40 @@ function ThemeProvider({
2156
2364
  config: configOverride,
2157
2365
  disablePersistence = false
2158
2366
  }) {
2159
- const [resolvedConfig, setResolvedConfig] = (0, import_react2.useState)(
2367
+ const [resolvedConfig, setResolvedConfig] = (0, import_react3.useState)(
2160
2368
  () => configOverride ? buildConfig(configOverride) : getConfig()
2161
2369
  );
2162
- const [webScheme, setWebScheme] = (0, import_react2.useState)(() => {
2370
+ const [webScheme, setWebScheme] = (0, import_react3.useState)(() => {
2163
2371
  if (isWeb && typeof window !== "undefined") {
2164
2372
  return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
2165
2373
  }
2166
2374
  return "light";
2167
2375
  });
2168
2376
  const systemScheme = isWeb ? webScheme : colorScheme === "dark" ? "dark" : "light";
2169
- const [mode, _setMode] = (0, import_react2.useState)(() => {
2377
+ const [mode, _setMode] = (0, import_react3.useState)(() => {
2170
2378
  if (!disablePersistence) {
2171
2379
  const persisted = loadPersistedMode();
2172
2380
  if (persisted) return persisted;
2173
2381
  }
2174
2382
  return defaultMode;
2175
2383
  });
2176
- const setMode = (0, import_react2.useCallback)((next) => {
2384
+ const setMode = (0, import_react3.useCallback)((next) => {
2177
2385
  _setMode(next);
2178
2386
  if (!disablePersistence) persistMode(next);
2179
2387
  }, [disablePersistence]);
2180
- const toggle = (0, import_react2.useCallback)(() => {
2388
+ const toggle = (0, import_react3.useCallback)(() => {
2181
2389
  setMode(mode === "dark" || mode === "system" && systemScheme === "dark" ? "light" : "dark");
2182
2390
  }, [mode, systemScheme, setMode]);
2183
2391
  const resolvedMode = mode === "system" ? systemScheme : mode;
2184
2392
  const isDark = resolvedMode === "dark";
2185
2393
  syncGlobalDarkMode(isDark);
2186
- (0, import_react2.useEffect)(() => {
2394
+ (0, import_react3.useEffect)(() => {
2187
2395
  applyWebTheme(resolvedMode, resolvedConfig.darkMode);
2188
2396
  setGlobalDarkMode(isDark);
2189
2397
  }, [isDark, resolvedMode, resolvedConfig.darkMode]);
2190
- const setWebSchemeRef = (0, import_react2.useRef)(setWebScheme);
2398
+ const setWebSchemeRef = (0, import_react3.useRef)(setWebScheme);
2191
2399
  setWebSchemeRef.current = setWebScheme;
2192
- (0, import_react2.useEffect)(() => {
2400
+ (0, import_react3.useEffect)(() => {
2193
2401
  if (!isWeb || typeof window === "undefined") return;
2194
2402
  const mq = window.matchMedia?.("(prefers-color-scheme: dark)");
2195
2403
  if (!mq) return;
@@ -2199,12 +2407,16 @@ function ThemeProvider({
2199
2407
  mq.addEventListener("change", handler);
2200
2408
  return () => mq.removeEventListener("change", handler);
2201
2409
  }, []);
2202
- (0, import_react2.useEffect)(() => {
2410
+ (0, import_react3.useEffect)(() => {
2411
+ if (!configOverride) return;
2412
+ setResolvedConfig(buildConfig(configOverride));
2413
+ }, [configOverride]);
2414
+ (0, import_react3.useEffect)(() => {
2203
2415
  if (configOverride) return;
2204
2416
  const unsub = onConfigChange(setResolvedConfig);
2205
2417
  return unsub;
2206
2418
  }, [configOverride]);
2207
- const contextValue = (0, import_react2.useMemo)(
2419
+ const contextValue = (0, import_react3.useMemo)(
2208
2420
  () => ({ mode, resolvedMode, isDark, setMode, toggle, config: resolvedConfig }),
2209
2421
  [mode, resolvedMode, isDark, setMode, toggle, resolvedConfig]
2210
2422
  );
@@ -2212,7 +2424,7 @@ function ThemeProvider({
2212
2424
  }
2213
2425
 
2214
2426
  // src/ThemeToggle.tsx
2215
- var import_react3 = __toESM(require("react"));
2427
+ var import_react4 = __toESM(require("react"));
2216
2428
  function getPrimitives() {
2217
2429
  try {
2218
2430
  const RN = require("react-native");
@@ -2223,14 +2435,14 @@ function getPrimitives() {
2223
2435
  Switch: RN.Switch
2224
2436
  };
2225
2437
  } catch {
2226
- const View = ({ style, ...rest }) => import_react3.default.createElement("div", { style, ...rest });
2227
- const Text = ({ style, ...rest }) => import_react3.default.createElement("span", { style, ...rest });
2228
- const Touchable = ({ onPress, style, ...rest }) => import_react3.default.createElement("button", { onClick: onPress, style, ...rest });
2438
+ const View = ({ style, ...rest }) => import_react4.default.createElement("div", { style, ...rest });
2439
+ const Text = ({ style, ...rest }) => import_react4.default.createElement("span", { style, ...rest });
2440
+ const Touchable = ({ onPress, style, ...rest }) => import_react4.default.createElement("button", { onClick: onPress, style, ...rest });
2229
2441
  const Switch = ({
2230
2442
  value,
2231
2443
  onValueChange,
2232
2444
  accessibilityLabel
2233
- }) => import_react3.default.createElement("input", {
2445
+ }) => import_react4.default.createElement("input", {
2234
2446
  type: "checkbox",
2235
2447
  checked: value,
2236
2448
  onChange: (e) => onValueChange?.(e.target.checked),
@@ -2252,11 +2464,11 @@ function ThemeToggle({
2252
2464
  const { mode, isDark, setMode, toggle } = useTheme();
2253
2465
  const View = _View, Text = _Text, Touchable = _Touchable, Switch = _Switch;
2254
2466
  if (variant === "switch") {
2255
- return import_react3.default.createElement(
2467
+ return import_react4.default.createElement(
2256
2468
  View,
2257
2469
  { style: { flexDirection: "row", alignItems: "center", gap: 8, ...style } },
2258
- import_react3.default.createElement(Text, { style: { fontSize: 14, ...labelStyle } }, lightLabel),
2259
- import_react3.default.createElement(Switch, {
2470
+ import_react4.default.createElement(Text, { style: { fontSize: 14, ...labelStyle } }, lightLabel),
2471
+ import_react4.default.createElement(Switch, {
2260
2472
  value: isDark,
2261
2473
  onValueChange: (v) => setMode(v ? "dark" : "light"),
2262
2474
  trackColor: { false: "#d1d5db", true: "#6366f1" },
@@ -2264,12 +2476,12 @@ function ThemeToggle({
2264
2476
  ios_backgroundColor: "#d1d5db",
2265
2477
  accessibilityLabel: "Toggle dark mode"
2266
2478
  }),
2267
- import_react3.default.createElement(Text, { style: { fontSize: 14, ...labelStyle } }, darkLabel)
2479
+ import_react4.default.createElement(Text, { style: { fontSize: 14, ...labelStyle } }, darkLabel)
2268
2480
  );
2269
2481
  }
2270
2482
  if (variant === "icon-button") {
2271
2483
  const icon = isDark ? darkLabel : lightLabel;
2272
- return import_react3.default.createElement(
2484
+ return import_react4.default.createElement(
2273
2485
  Touchable,
2274
2486
  {
2275
2487
  onPress: toggle,
@@ -2283,7 +2495,7 @@ function ThemeToggle({
2283
2495
  accessibilityLabel: `Switch to ${isDark ? "light" : "dark"} mode`,
2284
2496
  accessibilityState: {}
2285
2497
  },
2286
- import_react3.default.createElement(Text, { style: { fontSize: 18, ...labelStyle } }, icon)
2498
+ import_react4.default.createElement(Text, { style: { fontSize: 18, ...labelStyle } }, icon)
2287
2499
  );
2288
2500
  }
2289
2501
  if (includeSystem) {
@@ -2292,11 +2504,11 @@ function ThemeToggle({
2292
2504
  { key: "dark", label: darkLabel },
2293
2505
  { key: "system", label: "System" }
2294
2506
  ];
2295
- return import_react3.default.createElement(
2507
+ return import_react4.default.createElement(
2296
2508
  View,
2297
2509
  { style: { flexDirection: "row", gap: 4, ...style } },
2298
2510
  ...modes.map(
2299
- ({ key, label: mLabel }) => import_react3.default.createElement(
2511
+ ({ key, label: mLabel }) => import_react4.default.createElement(
2300
2512
  Touchable,
2301
2513
  {
2302
2514
  key,
@@ -2311,7 +2523,7 @@ function ThemeToggle({
2311
2523
  accessibilityLabel: `Set ${key} theme`,
2312
2524
  accessibilityState: { selected: mode === key }
2313
2525
  },
2314
- import_react3.default.createElement(Text, {
2526
+ import_react4.default.createElement(Text, {
2315
2527
  style: {
2316
2528
  fontSize: 13,
2317
2529
  fontWeight: "500",
@@ -2324,7 +2536,7 @@ function ThemeToggle({
2324
2536
  );
2325
2537
  }
2326
2538
  const currentLabel = label ?? (isDark ? darkLabel : lightLabel);
2327
- return import_react3.default.createElement(
2539
+ return import_react4.default.createElement(
2328
2540
  Touchable,
2329
2541
  {
2330
2542
  onPress: toggle,
@@ -2339,7 +2551,7 @@ function ThemeToggle({
2339
2551
  accessibilityLabel: `Switch to ${isDark ? "light" : "dark"} mode`,
2340
2552
  accessibilityState: {}
2341
2553
  },
2342
- import_react3.default.createElement(Text, {
2554
+ import_react4.default.createElement(Text, {
2343
2555
  style: {
2344
2556
  fontSize: 14,
2345
2557
  fontWeight: "500",
@@ -2351,9 +2563,9 @@ function ThemeToggle({
2351
2563
  }
2352
2564
 
2353
2565
  // src/styled.tsx
2354
- var import_react4 = __toESM(require("react"));
2566
+ var import_react5 = __toESM(require("react"));
2355
2567
  function styled(Component, baseClasses = "") {
2356
- const Styled = (0, import_react4.forwardRef)(
2568
+ const Styled = (0, import_react5.forwardRef)(
2357
2569
  (props, ref) => {
2358
2570
  const {
2359
2571
  kb: extraClasses,
@@ -2367,47 +2579,48 @@ function styled(Component, baseClasses = "") {
2367
2579
  ...rest
2368
2580
  } = props;
2369
2581
  const { isDark, config } = useTheme();
2370
- const [pressed, setPressed] = (0, import_react4.useState)(false);
2371
- const [hovered, setHovered] = (0, import_react4.useState)(false);
2372
- const [focused, setFocused] = (0, import_react4.useState)(false);
2373
- const handlePressIn = (0, import_react4.useCallback)((e) => {
2582
+ const [pressed, setPressed] = (0, import_react5.useState)(false);
2583
+ const [hovered, setHovered] = (0, import_react5.useState)(false);
2584
+ const [focused, setFocused] = (0, import_react5.useState)(false);
2585
+ const handlePressIn = (0, import_react5.useCallback)((e) => {
2374
2586
  setPressed(true);
2375
2587
  onPressIn?.(e);
2376
2588
  }, [onPressIn]);
2377
- const handlePressOut = (0, import_react4.useCallback)((e) => {
2589
+ const handlePressOut = (0, import_react5.useCallback)((e) => {
2378
2590
  setPressed(false);
2379
2591
  onPressOut?.(e);
2380
2592
  }, [onPressOut]);
2381
- const handleMouseEnter = (0, import_react4.useCallback)((e) => {
2593
+ const handleMouseEnter = (0, import_react5.useCallback)((e) => {
2382
2594
  setHovered(true);
2383
2595
  onMouseEnter?.(e);
2384
2596
  }, [onMouseEnter]);
2385
- const handleMouseLeave = (0, import_react4.useCallback)((e) => {
2597
+ const handleMouseLeave = (0, import_react5.useCallback)((e) => {
2386
2598
  setHovered(false);
2387
2599
  onMouseLeave?.(e);
2388
2600
  }, [onMouseLeave]);
2389
- const handleFocus = (0, import_react4.useCallback)((e) => {
2601
+ const handleFocus = (0, import_react5.useCallback)((e) => {
2390
2602
  setFocused(true);
2391
2603
  onFocus?.(e);
2392
2604
  }, [onFocus]);
2393
- const handleBlur = (0, import_react4.useCallback)((e) => {
2605
+ const handleBlur = (0, import_react5.useCallback)((e) => {
2394
2606
  setFocused(false);
2395
2607
  onBlur?.(e);
2396
2608
  }, [onBlur]);
2397
2609
  const combined = extraClasses ? `${baseClasses} ${extraClasses}` : baseClasses;
2398
- const resolved = (0, import_react4.useMemo)(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
2399
- const computedStyle = (0, import_react4.useMemo)(
2610
+ const resolved = (0, import_react5.useMemo)(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
2611
+ const computedStyle = (0, import_react5.useMemo)(
2400
2612
  () => flatten(resolved, isDark, { pressed, hover: hovered, focus: focused }),
2401
2613
  [resolved, isDark, pressed, hovered, focused]
2402
2614
  );
2403
2615
  const extraStyle = Array.isArray(styleProp) ? Object.assign({}, ...styleProp) : styleProp;
2404
2616
  const finalStyle = extraStyle ? { ...computedStyle, ...extraStyle } : computedStyle;
2405
- return import_react4.default.createElement(Component, {
2617
+ return import_react5.default.createElement(Component, {
2406
2618
  ref,
2407
2619
  ...rest,
2408
2620
  style: finalStyle,
2409
- onPressIn: handlePressIn,
2410
- onPressOut: handlePressOut,
2621
+ // onPressIn / onPressOut are React Native-only events; forwarding them to
2622
+ // a DOM element on web triggers React's unknown-prop warning.
2623
+ ...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
2411
2624
  onMouseEnter: handleMouseEnter,
2412
2625
  onMouseLeave: handleMouseLeave,
2413
2626
  onFocus: handleFocus,
@@ -2421,11 +2634,11 @@ function styled(Component, baseClasses = "") {
2421
2634
  }
2422
2635
 
2423
2636
  // src/useStyles.ts
2424
- var import_react5 = require("react");
2637
+ var import_react6 = require("react");
2425
2638
  function useStyles(classString, state = {}) {
2426
2639
  const { isDark, config } = useTheme();
2427
2640
  const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
2428
- return (0, import_react5.useMemo)(() => {
2641
+ return (0, import_react6.useMemo)(() => {
2429
2642
  const resolved = resolve(normalised, config.theme, config.darkMode);
2430
2643
  return flatten(resolved, isDark, state);
2431
2644
  }, [normalised, isDark, config, state.hover, state.focus, state.pressed, state.active, state.disabled, state.checked, state.visited, state.placeholder]);
@@ -2433,17 +2646,17 @@ function useStyles(classString, state = {}) {
2433
2646
  function useResolvedStyle(classString) {
2434
2647
  const { config } = useTheme();
2435
2648
  const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
2436
- return (0, import_react5.useMemo)(
2649
+ return (0, import_react6.useMemo)(
2437
2650
  () => resolve(normalised, config.theme, config.darkMode),
2438
2651
  [normalised, config]
2439
2652
  );
2440
2653
  }
2441
2654
 
2442
2655
  // src/useGlobalDarkMode.ts
2443
- var import_react6 = __toESM(require("react"));
2444
- var useSyncExternalStore = import_react6.default.useSyncExternalStore ?? function useSyncExternalStoreFallback(subscribe, getSnapshot, _getServerSnapshot) {
2445
- const [, forceUpdate] = import_react6.default.useReducer((n) => n + 1, 0);
2446
- import_react6.default.useEffect(() => subscribe(forceUpdate), [subscribe]);
2656
+ var import_react7 = __toESM(require("react"));
2657
+ var useSyncExternalStore = import_react7.default.useSyncExternalStore ?? function useSyncExternalStoreFallback(subscribe, getSnapshot, _getServerSnapshot) {
2658
+ const [, forceUpdate] = import_react7.default.useReducer((n) => n + 1, 0);
2659
+ import_react7.default.useEffect(() => subscribe(forceUpdate), [subscribe]);
2447
2660
  return getSnapshot();
2448
2661
  };
2449
2662
  function useGlobalDarkMode() {
@@ -2456,14 +2669,14 @@ function useGlobalDarkMode() {
2456
2669
  }
2457
2670
 
2458
2671
  // src/InteractiveWrapper.tsx
2459
- var import_react7 = __toESM(require("react"));
2672
+ var import_react8 = __toESM(require("react"));
2460
2673
  function chain(original, extra) {
2461
2674
  return (...args) => {
2462
2675
  original?.(...args);
2463
2676
  extra();
2464
2677
  };
2465
2678
  }
2466
- var InteractiveWrapper = (0, import_react7.forwardRef)(
2679
+ var InteractiveWrapper = (0, import_react8.forwardRef)(
2467
2680
  function InteractiveWrapper2({
2468
2681
  Component,
2469
2682
  resolvedStyle,
@@ -2478,16 +2691,16 @@ var InteractiveWrapper = (0, import_react7.forwardRef)(
2478
2691
  ...rest
2479
2692
  }, ref) {
2480
2693
  const isDark = useGlobalDarkMode();
2481
- const [pressed, setPressed] = (0, import_react7.useState)(false);
2482
- const [hovered, setHovered] = (0, import_react7.useState)(false);
2483
- const [focused, setFocused] = (0, import_react7.useState)(false);
2484
- const handlePressIn = (0, import_react7.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
2485
- const handlePressOut = (0, import_react7.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
2486
- const handleMouseEnter = (0, import_react7.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
2487
- const handleMouseLeave = (0, import_react7.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
2488
- const handleFocus = (0, import_react7.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
2489
- const handleBlur = (0, import_react7.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
2490
- const computedStyle = (0, import_react7.useMemo)(
2694
+ const [pressed, setPressed] = (0, import_react8.useState)(false);
2695
+ const [hovered, setHovered] = (0, import_react8.useState)(false);
2696
+ const [focused, setFocused] = (0, import_react8.useState)(false);
2697
+ const handlePressIn = (0, import_react8.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
2698
+ const handlePressOut = (0, import_react8.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
2699
+ const handleMouseEnter = (0, import_react8.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
2700
+ const handleMouseLeave = (0, import_react8.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
2701
+ const handleFocus = (0, import_react8.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
2702
+ const handleBlur = (0, import_react8.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
2703
+ const computedStyle = (0, import_react8.useMemo)(
2491
2704
  () => flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused }),
2492
2705
  [resolvedStyle, isDark, pressed, hovered, focused]
2493
2706
  );
@@ -2498,20 +2711,20 @@ var InteractiveWrapper = (0, import_react7.forwardRef)(
2498
2711
  ...restWithoutChildren,
2499
2712
  style: finalStyle,
2500
2713
  ...isWeb && className ? { className } : {},
2501
- onPressIn: handlePressIn,
2502
- onPressOut: handlePressOut,
2714
+ // onPressIn / onPressOut are React Native-only; skip on web to avoid DOM warnings.
2715
+ ...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
2503
2716
  onMouseEnter: handleMouseEnter,
2504
2717
  onMouseLeave: handleMouseLeave,
2505
2718
  onFocus: handleFocus,
2506
2719
  onBlur: handleBlur
2507
2720
  };
2508
- return Array.isArray(children) ? import_react7.default.createElement(Component, componentProps, ...children) : import_react7.default.createElement(Component, componentProps, children);
2721
+ return Array.isArray(children) ? import_react8.default.createElement(Component, componentProps, ...children) : import_react8.default.createElement(Component, componentProps, children);
2509
2722
  }
2510
2723
  );
2511
2724
  InteractiveWrapper.displayName = "Kbach.InteractiveWrapper";
2512
2725
 
2513
- // src/tw.ts
2514
- function tw(classString, isDark = false) {
2726
+ // src/kb.ts
2727
+ function kb(classString, isDark = false) {
2515
2728
  const config = getConfig();
2516
2729
  const resolved = resolve(classString, config.theme, config.darkMode);
2517
2730
  if (isWeb) {
@@ -2534,14 +2747,17 @@ function cx(...classes) {
2534
2747
  defaultTheme,
2535
2748
  flatten,
2536
2749
  getConfig,
2750
+ kb,
2537
2751
  parseClass,
2538
2752
  parseClasses,
2539
2753
  resolve,
2540
2754
  styled,
2541
- tw,
2542
2755
  updateConfig,
2756
+ useColors,
2543
2757
  useGlobalDarkMode,
2758
+ useIsDark,
2544
2759
  useResolvedStyle,
2545
2760
  useStyles,
2546
- useTheme
2761
+ useTheme,
2762
+ wrapColors
2547
2763
  });