@kbach/react 0.2.8 → 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];
1845
1963
  }
1846
- function getResolverMap() {
1847
- return RESOLVERS;
1964
+ function getPluginStandaloneMap() {
1965
+ return PLUGIN_STANDALONE;
1966
+ }
1967
+ function getPluginResolverMap() {
1968
+ return PLUGIN_RESOLVERS;
1848
1969
  }
1849
1970
 
1850
1971
  // src/core/resolver.ts
@@ -1866,6 +1987,37 @@ function injectRule(rule) {
1866
1987
  } catch {
1867
1988
  }
1868
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"]);
1869
2021
  function injectClassRule(cls, bucketKey, styles, darkMode) {
1870
2022
  const cssDecls = styleValueToCSS(styles);
1871
2023
  if (!cssDecls) return;
@@ -1875,10 +2027,14 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1875
2027
  return;
1876
2028
  }
1877
2029
  const mods = bucketKey.split(":");
1878
- const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1879
- const hasDark = mods.includes("dark");
1880
- const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1881
- 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}`;
1882
2038
  let rule;
1883
2039
  if (hasDark) {
1884
2040
  if (darkMode === "media") {
@@ -1888,6 +2044,14 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1888
2044
  } else {
1889
2045
  rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1890
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
+ }
1891
2055
  } else {
1892
2056
  rule = `${selector} { ${cssDecls} }`;
1893
2057
  }
@@ -2013,6 +2177,9 @@ function matchesMods(mods, isDark, state) {
2013
2177
  }
2014
2178
  });
2015
2179
  }
2180
+ function clearCache() {
2181
+ styleCache.clear();
2182
+ }
2016
2183
 
2017
2184
  // src/core/config.ts
2018
2185
  function deepMerge(base, override) {
@@ -2068,20 +2235,24 @@ function buildConfig(userConfig) {
2068
2235
  theme,
2069
2236
  plugins: userConfig.plugins ?? []
2070
2237
  };
2238
+ clearPluginUtilities();
2239
+ clearPluginModifiers();
2240
+ const pluginAPI = makePluginAPI(resolved.theme);
2071
2241
  for (const plugin of resolved.plugins) {
2072
- plugin(makePluginAPI(resolved.theme));
2242
+ plugin(pluginAPI);
2073
2243
  }
2074
2244
  return resolved;
2075
2245
  }
2076
2246
  function makePluginAPI(theme) {
2077
- const standalone = getStandaloneMap();
2078
- const resolvers = getResolverMap();
2247
+ const standalone = getPluginStandaloneMap();
2248
+ const resolvers = getPluginResolverMap();
2079
2249
  return {
2080
2250
  addUtility(name, styles) {
2081
2251
  standalone[name] = styles;
2082
2252
  },
2083
- addVariant(name, _selector) {
2084
- customVariants[name] = _selector;
2253
+ addVariant(name, selector) {
2254
+ customVariants[name] = selector;
2255
+ registerModifier(name);
2085
2256
  },
2086
2257
  theme(path, defaultValue) {
2087
2258
  const parts = path.replace(/\[([^\]]+)\]/g, ".$1").split(".");
@@ -2105,6 +2276,7 @@ function onConfigChange(listener) {
2105
2276
  function updateConfig(userConfig) {
2106
2277
  const store = getConfigStore();
2107
2278
  store.resolved = buildConfig(userConfig);
2279
+ clearCache();
2108
2280
  for (const listener of store.listeners) {
2109
2281
  listener(store.resolved);
2110
2282
  }
@@ -2192,40 +2364,40 @@ function ThemeProvider({
2192
2364
  config: configOverride,
2193
2365
  disablePersistence = false
2194
2366
  }) {
2195
- const [resolvedConfig, setResolvedConfig] = (0, import_react2.useState)(
2367
+ const [resolvedConfig, setResolvedConfig] = (0, import_react3.useState)(
2196
2368
  () => configOverride ? buildConfig(configOverride) : getConfig()
2197
2369
  );
2198
- const [webScheme, setWebScheme] = (0, import_react2.useState)(() => {
2370
+ const [webScheme, setWebScheme] = (0, import_react3.useState)(() => {
2199
2371
  if (isWeb && typeof window !== "undefined") {
2200
2372
  return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
2201
2373
  }
2202
2374
  return "light";
2203
2375
  });
2204
2376
  const systemScheme = isWeb ? webScheme : colorScheme === "dark" ? "dark" : "light";
2205
- const [mode, _setMode] = (0, import_react2.useState)(() => {
2377
+ const [mode, _setMode] = (0, import_react3.useState)(() => {
2206
2378
  if (!disablePersistence) {
2207
2379
  const persisted = loadPersistedMode();
2208
2380
  if (persisted) return persisted;
2209
2381
  }
2210
2382
  return defaultMode;
2211
2383
  });
2212
- const setMode = (0, import_react2.useCallback)((next) => {
2384
+ const setMode = (0, import_react3.useCallback)((next) => {
2213
2385
  _setMode(next);
2214
2386
  if (!disablePersistence) persistMode(next);
2215
2387
  }, [disablePersistence]);
2216
- const toggle = (0, import_react2.useCallback)(() => {
2388
+ const toggle = (0, import_react3.useCallback)(() => {
2217
2389
  setMode(mode === "dark" || mode === "system" && systemScheme === "dark" ? "light" : "dark");
2218
2390
  }, [mode, systemScheme, setMode]);
2219
2391
  const resolvedMode = mode === "system" ? systemScheme : mode;
2220
2392
  const isDark = resolvedMode === "dark";
2221
2393
  syncGlobalDarkMode(isDark);
2222
- (0, import_react2.useEffect)(() => {
2394
+ (0, import_react3.useEffect)(() => {
2223
2395
  applyWebTheme(resolvedMode, resolvedConfig.darkMode);
2224
2396
  setGlobalDarkMode(isDark);
2225
2397
  }, [isDark, resolvedMode, resolvedConfig.darkMode]);
2226
- const setWebSchemeRef = (0, import_react2.useRef)(setWebScheme);
2398
+ const setWebSchemeRef = (0, import_react3.useRef)(setWebScheme);
2227
2399
  setWebSchemeRef.current = setWebScheme;
2228
- (0, import_react2.useEffect)(() => {
2400
+ (0, import_react3.useEffect)(() => {
2229
2401
  if (!isWeb || typeof window === "undefined") return;
2230
2402
  const mq = window.matchMedia?.("(prefers-color-scheme: dark)");
2231
2403
  if (!mq) return;
@@ -2235,12 +2407,16 @@ function ThemeProvider({
2235
2407
  mq.addEventListener("change", handler);
2236
2408
  return () => mq.removeEventListener("change", handler);
2237
2409
  }, []);
2238
- (0, import_react2.useEffect)(() => {
2410
+ (0, import_react3.useEffect)(() => {
2411
+ if (!configOverride) return;
2412
+ setResolvedConfig(buildConfig(configOverride));
2413
+ }, [configOverride]);
2414
+ (0, import_react3.useEffect)(() => {
2239
2415
  if (configOverride) return;
2240
2416
  const unsub = onConfigChange(setResolvedConfig);
2241
2417
  return unsub;
2242
2418
  }, [configOverride]);
2243
- const contextValue = (0, import_react2.useMemo)(
2419
+ const contextValue = (0, import_react3.useMemo)(
2244
2420
  () => ({ mode, resolvedMode, isDark, setMode, toggle, config: resolvedConfig }),
2245
2421
  [mode, resolvedMode, isDark, setMode, toggle, resolvedConfig]
2246
2422
  );
@@ -2248,7 +2424,7 @@ function ThemeProvider({
2248
2424
  }
2249
2425
 
2250
2426
  // src/ThemeToggle.tsx
2251
- var import_react3 = __toESM(require("react"));
2427
+ var import_react4 = __toESM(require("react"));
2252
2428
  function getPrimitives() {
2253
2429
  try {
2254
2430
  const RN = require("react-native");
@@ -2259,14 +2435,14 @@ function getPrimitives() {
2259
2435
  Switch: RN.Switch
2260
2436
  };
2261
2437
  } catch {
2262
- const View = ({ style, ...rest }) => import_react3.default.createElement("div", { style, ...rest });
2263
- const Text = ({ style, ...rest }) => import_react3.default.createElement("span", { style, ...rest });
2264
- 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 });
2265
2441
  const Switch = ({
2266
2442
  value,
2267
2443
  onValueChange,
2268
2444
  accessibilityLabel
2269
- }) => import_react3.default.createElement("input", {
2445
+ }) => import_react4.default.createElement("input", {
2270
2446
  type: "checkbox",
2271
2447
  checked: value,
2272
2448
  onChange: (e) => onValueChange?.(e.target.checked),
@@ -2288,11 +2464,11 @@ function ThemeToggle({
2288
2464
  const { mode, isDark, setMode, toggle } = useTheme();
2289
2465
  const View = _View, Text = _Text, Touchable = _Touchable, Switch = _Switch;
2290
2466
  if (variant === "switch") {
2291
- return import_react3.default.createElement(
2467
+ return import_react4.default.createElement(
2292
2468
  View,
2293
2469
  { style: { flexDirection: "row", alignItems: "center", gap: 8, ...style } },
2294
- import_react3.default.createElement(Text, { style: { fontSize: 14, ...labelStyle } }, lightLabel),
2295
- import_react3.default.createElement(Switch, {
2470
+ import_react4.default.createElement(Text, { style: { fontSize: 14, ...labelStyle } }, lightLabel),
2471
+ import_react4.default.createElement(Switch, {
2296
2472
  value: isDark,
2297
2473
  onValueChange: (v) => setMode(v ? "dark" : "light"),
2298
2474
  trackColor: { false: "#d1d5db", true: "#6366f1" },
@@ -2300,12 +2476,12 @@ function ThemeToggle({
2300
2476
  ios_backgroundColor: "#d1d5db",
2301
2477
  accessibilityLabel: "Toggle dark mode"
2302
2478
  }),
2303
- import_react3.default.createElement(Text, { style: { fontSize: 14, ...labelStyle } }, darkLabel)
2479
+ import_react4.default.createElement(Text, { style: { fontSize: 14, ...labelStyle } }, darkLabel)
2304
2480
  );
2305
2481
  }
2306
2482
  if (variant === "icon-button") {
2307
2483
  const icon = isDark ? darkLabel : lightLabel;
2308
- return import_react3.default.createElement(
2484
+ return import_react4.default.createElement(
2309
2485
  Touchable,
2310
2486
  {
2311
2487
  onPress: toggle,
@@ -2319,7 +2495,7 @@ function ThemeToggle({
2319
2495
  accessibilityLabel: `Switch to ${isDark ? "light" : "dark"} mode`,
2320
2496
  accessibilityState: {}
2321
2497
  },
2322
- import_react3.default.createElement(Text, { style: { fontSize: 18, ...labelStyle } }, icon)
2498
+ import_react4.default.createElement(Text, { style: { fontSize: 18, ...labelStyle } }, icon)
2323
2499
  );
2324
2500
  }
2325
2501
  if (includeSystem) {
@@ -2328,11 +2504,11 @@ function ThemeToggle({
2328
2504
  { key: "dark", label: darkLabel },
2329
2505
  { key: "system", label: "System" }
2330
2506
  ];
2331
- return import_react3.default.createElement(
2507
+ return import_react4.default.createElement(
2332
2508
  View,
2333
2509
  { style: { flexDirection: "row", gap: 4, ...style } },
2334
2510
  ...modes.map(
2335
- ({ key, label: mLabel }) => import_react3.default.createElement(
2511
+ ({ key, label: mLabel }) => import_react4.default.createElement(
2336
2512
  Touchable,
2337
2513
  {
2338
2514
  key,
@@ -2347,7 +2523,7 @@ function ThemeToggle({
2347
2523
  accessibilityLabel: `Set ${key} theme`,
2348
2524
  accessibilityState: { selected: mode === key }
2349
2525
  },
2350
- import_react3.default.createElement(Text, {
2526
+ import_react4.default.createElement(Text, {
2351
2527
  style: {
2352
2528
  fontSize: 13,
2353
2529
  fontWeight: "500",
@@ -2360,7 +2536,7 @@ function ThemeToggle({
2360
2536
  );
2361
2537
  }
2362
2538
  const currentLabel = label ?? (isDark ? darkLabel : lightLabel);
2363
- return import_react3.default.createElement(
2539
+ return import_react4.default.createElement(
2364
2540
  Touchable,
2365
2541
  {
2366
2542
  onPress: toggle,
@@ -2375,7 +2551,7 @@ function ThemeToggle({
2375
2551
  accessibilityLabel: `Switch to ${isDark ? "light" : "dark"} mode`,
2376
2552
  accessibilityState: {}
2377
2553
  },
2378
- import_react3.default.createElement(Text, {
2554
+ import_react4.default.createElement(Text, {
2379
2555
  style: {
2380
2556
  fontSize: 14,
2381
2557
  fontWeight: "500",
@@ -2387,9 +2563,9 @@ function ThemeToggle({
2387
2563
  }
2388
2564
 
2389
2565
  // src/styled.tsx
2390
- var import_react4 = __toESM(require("react"));
2566
+ var import_react5 = __toESM(require("react"));
2391
2567
  function styled(Component, baseClasses = "") {
2392
- const Styled = (0, import_react4.forwardRef)(
2568
+ const Styled = (0, import_react5.forwardRef)(
2393
2569
  (props, ref) => {
2394
2570
  const {
2395
2571
  kb: extraClasses,
@@ -2403,47 +2579,48 @@ function styled(Component, baseClasses = "") {
2403
2579
  ...rest
2404
2580
  } = props;
2405
2581
  const { isDark, config } = useTheme();
2406
- const [pressed, setPressed] = (0, import_react4.useState)(false);
2407
- const [hovered, setHovered] = (0, import_react4.useState)(false);
2408
- const [focused, setFocused] = (0, import_react4.useState)(false);
2409
- 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) => {
2410
2586
  setPressed(true);
2411
2587
  onPressIn?.(e);
2412
2588
  }, [onPressIn]);
2413
- const handlePressOut = (0, import_react4.useCallback)((e) => {
2589
+ const handlePressOut = (0, import_react5.useCallback)((e) => {
2414
2590
  setPressed(false);
2415
2591
  onPressOut?.(e);
2416
2592
  }, [onPressOut]);
2417
- const handleMouseEnter = (0, import_react4.useCallback)((e) => {
2593
+ const handleMouseEnter = (0, import_react5.useCallback)((e) => {
2418
2594
  setHovered(true);
2419
2595
  onMouseEnter?.(e);
2420
2596
  }, [onMouseEnter]);
2421
- const handleMouseLeave = (0, import_react4.useCallback)((e) => {
2597
+ const handleMouseLeave = (0, import_react5.useCallback)((e) => {
2422
2598
  setHovered(false);
2423
2599
  onMouseLeave?.(e);
2424
2600
  }, [onMouseLeave]);
2425
- const handleFocus = (0, import_react4.useCallback)((e) => {
2601
+ const handleFocus = (0, import_react5.useCallback)((e) => {
2426
2602
  setFocused(true);
2427
2603
  onFocus?.(e);
2428
2604
  }, [onFocus]);
2429
- const handleBlur = (0, import_react4.useCallback)((e) => {
2605
+ const handleBlur = (0, import_react5.useCallback)((e) => {
2430
2606
  setFocused(false);
2431
2607
  onBlur?.(e);
2432
2608
  }, [onBlur]);
2433
2609
  const combined = extraClasses ? `${baseClasses} ${extraClasses}` : baseClasses;
2434
- const resolved = (0, import_react4.useMemo)(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
2435
- 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)(
2436
2612
  () => flatten(resolved, isDark, { pressed, hover: hovered, focus: focused }),
2437
2613
  [resolved, isDark, pressed, hovered, focused]
2438
2614
  );
2439
2615
  const extraStyle = Array.isArray(styleProp) ? Object.assign({}, ...styleProp) : styleProp;
2440
2616
  const finalStyle = extraStyle ? { ...computedStyle, ...extraStyle } : computedStyle;
2441
- return import_react4.default.createElement(Component, {
2617
+ return import_react5.default.createElement(Component, {
2442
2618
  ref,
2443
2619
  ...rest,
2444
2620
  style: finalStyle,
2445
- onPressIn: handlePressIn,
2446
- 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 },
2447
2624
  onMouseEnter: handleMouseEnter,
2448
2625
  onMouseLeave: handleMouseLeave,
2449
2626
  onFocus: handleFocus,
@@ -2457,11 +2634,11 @@ function styled(Component, baseClasses = "") {
2457
2634
  }
2458
2635
 
2459
2636
  // src/useStyles.ts
2460
- var import_react5 = require("react");
2637
+ var import_react6 = require("react");
2461
2638
  function useStyles(classString, state = {}) {
2462
2639
  const { isDark, config } = useTheme();
2463
2640
  const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
2464
- return (0, import_react5.useMemo)(() => {
2641
+ return (0, import_react6.useMemo)(() => {
2465
2642
  const resolved = resolve(normalised, config.theme, config.darkMode);
2466
2643
  return flatten(resolved, isDark, state);
2467
2644
  }, [normalised, isDark, config, state.hover, state.focus, state.pressed, state.active, state.disabled, state.checked, state.visited, state.placeholder]);
@@ -2469,17 +2646,17 @@ function useStyles(classString, state = {}) {
2469
2646
  function useResolvedStyle(classString) {
2470
2647
  const { config } = useTheme();
2471
2648
  const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
2472
- return (0, import_react5.useMemo)(
2649
+ return (0, import_react6.useMemo)(
2473
2650
  () => resolve(normalised, config.theme, config.darkMode),
2474
2651
  [normalised, config]
2475
2652
  );
2476
2653
  }
2477
2654
 
2478
2655
  // src/useGlobalDarkMode.ts
2479
- var import_react6 = __toESM(require("react"));
2480
- var useSyncExternalStore = import_react6.default.useSyncExternalStore ?? function useSyncExternalStoreFallback(subscribe, getSnapshot, _getServerSnapshot) {
2481
- const [, forceUpdate] = import_react6.default.useReducer((n) => n + 1, 0);
2482
- 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]);
2483
2660
  return getSnapshot();
2484
2661
  };
2485
2662
  function useGlobalDarkMode() {
@@ -2492,14 +2669,14 @@ function useGlobalDarkMode() {
2492
2669
  }
2493
2670
 
2494
2671
  // src/InteractiveWrapper.tsx
2495
- var import_react7 = __toESM(require("react"));
2672
+ var import_react8 = __toESM(require("react"));
2496
2673
  function chain(original, extra) {
2497
2674
  return (...args) => {
2498
2675
  original?.(...args);
2499
2676
  extra();
2500
2677
  };
2501
2678
  }
2502
- var InteractiveWrapper = (0, import_react7.forwardRef)(
2679
+ var InteractiveWrapper = (0, import_react8.forwardRef)(
2503
2680
  function InteractiveWrapper2({
2504
2681
  Component,
2505
2682
  resolvedStyle,
@@ -2514,16 +2691,16 @@ var InteractiveWrapper = (0, import_react7.forwardRef)(
2514
2691
  ...rest
2515
2692
  }, ref) {
2516
2693
  const isDark = useGlobalDarkMode();
2517
- const [pressed, setPressed] = (0, import_react7.useState)(false);
2518
- const [hovered, setHovered] = (0, import_react7.useState)(false);
2519
- const [focused, setFocused] = (0, import_react7.useState)(false);
2520
- const handlePressIn = (0, import_react7.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
2521
- const handlePressOut = (0, import_react7.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
2522
- const handleMouseEnter = (0, import_react7.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
2523
- const handleMouseLeave = (0, import_react7.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
2524
- const handleFocus = (0, import_react7.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
2525
- const handleBlur = (0, import_react7.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
2526
- 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)(
2527
2704
  () => flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused }),
2528
2705
  [resolvedStyle, isDark, pressed, hovered, focused]
2529
2706
  );
@@ -2534,20 +2711,20 @@ var InteractiveWrapper = (0, import_react7.forwardRef)(
2534
2711
  ...restWithoutChildren,
2535
2712
  style: finalStyle,
2536
2713
  ...isWeb && className ? { className } : {},
2537
- onPressIn: handlePressIn,
2538
- onPressOut: handlePressOut,
2714
+ // onPressIn / onPressOut are React Native-only; skip on web to avoid DOM warnings.
2715
+ ...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
2539
2716
  onMouseEnter: handleMouseEnter,
2540
2717
  onMouseLeave: handleMouseLeave,
2541
2718
  onFocus: handleFocus,
2542
2719
  onBlur: handleBlur
2543
2720
  };
2544
- 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);
2545
2722
  }
2546
2723
  );
2547
2724
  InteractiveWrapper.displayName = "Kbach.InteractiveWrapper";
2548
2725
 
2549
- // src/tw.ts
2550
- function tw(classString, isDark = false) {
2726
+ // src/kb.ts
2727
+ function kb(classString, isDark = false) {
2551
2728
  const config = getConfig();
2552
2729
  const resolved = resolve(classString, config.theme, config.darkMode);
2553
2730
  if (isWeb) {
@@ -2570,14 +2747,17 @@ function cx(...classes) {
2570
2747
  defaultTheme,
2571
2748
  flatten,
2572
2749
  getConfig,
2750
+ kb,
2573
2751
  parseClass,
2574
2752
  parseClasses,
2575
2753
  resolve,
2576
2754
  styled,
2577
- tw,
2578
2755
  updateConfig,
2756
+ useColors,
2579
2757
  useGlobalDarkMode,
2758
+ useIsDark,
2580
2759
  useResolvedStyle,
2581
2760
  useStyles,
2582
- useTheme
2761
+ useTheme,
2762
+ wrapColors
2583
2763
  });