@kbach/react-native 1.0.0-beta.33 → 1.0.0-beta.35

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.
@@ -5,17 +5,20 @@ import { ReactNode } from 'react';
5
5
 
6
6
  /**
7
7
  * A `kbach.config.js`-style theme customization — same shape as
8
- * @kbach/react's `KbachConfig`, minus mode-aware `{ light, dark }` color
9
- * entries in `extend.colors` specifically: aliasing/opacity math here only
10
- * ever operates on plain hex (`brand: 'blue-6'`, `brandSoft: 'brand/30'`),
11
- * matching `jsEngine/resolvers/color.ts`'s `lookupHex`'s own Plain-only
12
- * convention — a reference to an EXISTING color that happens to be
13
- * mode-aware (`theme.colors` can hold those now, for `useColors()`; see
14
- * `theme.ts`'s own `ColorEntry` doc comment) just doesn't resolve as an
15
- * alias, falling through to "literal CSS value" the same way an unknown
16
- * name would. A config object goes in, a resolved `ThemeConfig` comes out,
17
- * via one PURE function (`resolveKbachConfig`) — no plugins, no
18
- * live-update store, nothing to keep "in sync."
8
+ * @kbach/react's `KbachConfig`, minus @kbach/react's own PER-COLOR
9
+ * `{ light, dark }` object form in `extend.colors` specifically (real
10
+ * mode-aware colors here are written via the grouped `dark` key instead —
11
+ * see `KbachConfig.extend.colors`'s own doc comment below): aliasing/
12
+ * opacity math on an individual color value here only ever operates on
13
+ * plain hex (`brand: 'blue-6'`, `brandSoft: 'brand/30'`), matching
14
+ * `jsEngine/resolvers/color.ts`'s `lookupHex`'s own Plain-only convention
15
+ * — a reference to an EXISTING color that happens to be mode-aware
16
+ * (`theme.colors` can hold those, for `useColors()`; see `theme.ts`'s own
17
+ * `ColorEntry` doc comment) just doesn't resolve as an alias, falling
18
+ * through to "literal CSS value" the same way an unknown name would. A
19
+ * config object goes in, a resolved `ThemeConfig` comes out, via one PURE
20
+ * function (`resolveKbachConfig`) — no plugins, no live-update store,
21
+ * nothing to keep "in sync."
19
22
  */
20
23
  interface KbachConfig {
21
24
  darkMode?: ThemeConfig['darkMode'];
@@ -37,8 +40,26 @@ interface KbachConfig {
37
40
  * '#ff6b35', brandSoft: 'brand/30' }` — `brandSoft` resolves to
38
41
  * `brand`'s own value at 30% opacity). A string that doesn't match any
39
42
  * known color name is treated as a literal CSS value instead.
43
+ *
44
+ * `dark` is a reserved key, not a color name: a parallel map of
45
+ * dark-mode overrides for any of the OTHER names in this same object —
46
+ * `{ surface: 'gray-2', card: 'white', dark: { surface: 'gray-11',
47
+ * card: 'gray-9' } }` makes `surface`/`card` mode-aware
48
+ * (`{ light, dark }` entries in the resolved theme), while `brand`
49
+ * (never mentioned under `dark`) stays a plain, mode-independent
50
+ * color. A name under `dark` with no matching top-level (light) entry
51
+ * is skipped — mode-aware colors need both sides written out, and
52
+ * inventing a "light" value out of nowhere would be worse than just
53
+ * not creating the entry at all. References inside `dark` resolve
54
+ * against the built-in palette and OTHER entries within `dark` itself
55
+ * (not against the light-side custom colors) — the same
56
+ * "self-contained, ordering-independent-of-the-other-side" resolution
57
+ * the light side already gets, just mirrored.
40
58
  */
41
- colors?: Record<string, string>;
59
+ colors?: {
60
+ [colorName: string]: string | Record<string, string> | undefined;
61
+ dark?: Record<string, string>;
62
+ };
42
63
  spacing?: Record<string, number>;
43
64
  screens?: Record<string, number>;
44
65
  /**
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { ColorEntry, ContainerConfig, ThemeConfig, defaultTheme, getTheme, setTheme } from './theme';
2
- export { C as ClassArray, a as ClassDictionary, b as ClassValue, c as ColorScale, d as ColorsAPI, K as KbachConfig, T as ThemeProvider, e as ThemeProviderProps, f as ThemeState, g as ThemeStorageAdapter, h as applyKbachConfig, i as clsx, r as resolveKbachConfig, u as useColors, j as useDynamicToken, k as useTheme } from './clsx-BCXALPGh.js';
2
+ export { C as ClassArray, a as ClassDictionary, b as ClassValue, c as ColorScale, d as ColorsAPI, K as KbachConfig, T as ThemeProvider, e as ThemeProviderProps, f as ThemeState, g as ThemeStorageAdapter, h as applyKbachConfig, i as clsx, r as resolveKbachConfig, u as useColors, j as useDynamicToken, k as useTheme } from './clsx-CxjWSCcU.js';
3
3
  export { ThemeMode, getGlobalDarkMode, getGlobalThemeMode, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode } from './darkModeStore';
4
4
  export { deleteDynamicToken, getDynamicToken, setDynamicToken, subscribeDynamicTokens } from './dynamicTokens';
5
5
  import 'react';
package/dist/index.js CHANGED
@@ -849,6 +849,29 @@ function lookupHex(theme, key) {
849
849
  const entry = theme.colors[key];
850
850
  return typeof entry === "string" ? entry : null;
851
851
  }
852
+ var MODE_AWARE_COLOR_PREFIXES = ["bg-", "text-", "border-"];
853
+ function findModeAwareColor(base, theme) {
854
+ const important = base.startsWith("!") ? "!" : "";
855
+ const rest = important ? base.slice(1) : base;
856
+ for (const prefix of MODE_AWARE_COLOR_PREFIXES) {
857
+ if (!rest.startsWith(prefix)) continue;
858
+ const value = rest.slice(prefix.length);
859
+ const entry = theme.colors[value];
860
+ if (typeof entry === "object" && entry !== null) {
861
+ return { prefix: `${important}${prefix}`, light: entry.light, dark: entry.dark };
862
+ }
863
+ }
864
+ return null;
865
+ }
866
+ function substituteModeAwareColorToken(token, theme, colorScheme) {
867
+ const segments = token.split(":");
868
+ const base = segments.pop() ?? token;
869
+ const modifierPrefix = segments.length > 0 ? `${segments.join(":")}:` : "";
870
+ const found = findModeAwareColor(base, theme);
871
+ if (found === null) return token;
872
+ const hex = colorScheme === "dark" ? found.dark : found.light;
873
+ return `${modifierPrefix}${found.prefix}[${hex}]`;
874
+ }
852
875
  function hexToRgb(hex) {
853
876
  const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
854
877
  if (!m) return null;
@@ -1721,7 +1744,8 @@ function resolveStyleJsWithWarnings(classString, theme, colorScheme, pressed, wi
1721
1744
  const warnings = [];
1722
1745
  let shadowOffset = null;
1723
1746
  const transformOps = /* @__PURE__ */ new Map();
1724
- for (const token of classString.split(/\s+/).filter(Boolean)) {
1747
+ for (const rawToken of classString.split(/\s+/).filter(Boolean)) {
1748
+ const token = substituteModeAwareColorToken(rawToken, theme, colorScheme);
1725
1749
  const parsed = parseClass(token);
1726
1750
  const allModifiersHold = parsed.modifiers.every(
1727
1751
  (m) => nativeModifierState(m, theme, colorScheme, pressed, width) === true
@@ -1843,15 +1867,33 @@ function resolveColorEntry(value, lookup) {
1843
1867
  return opacity === void 0 ? base : applyOpacityToHex(base, opacity);
1844
1868
  }
1845
1869
  function resolveColors(extend, existing) {
1846
- const resolved = {};
1847
- const lookup = (name) => {
1848
- const found = resolved[name] ?? existing[name];
1870
+ const { dark: darkExtend, ...lightExtend } = extend;
1871
+ const resolvedLight = {};
1872
+ const lookupLight = (name) => {
1873
+ const found = resolvedLight[name] ?? existing[name];
1849
1874
  return typeof found === "string" ? found : void 0;
1850
1875
  };
1851
- for (const [name, value] of Object.entries(extend)) {
1852
- resolved[name] = resolveColorEntry(value, lookup);
1876
+ for (const [name, value] of Object.entries(lightExtend)) {
1877
+ if (typeof value !== "string") continue;
1878
+ resolvedLight[name] = resolveColorEntry(value, lookupLight);
1879
+ }
1880
+ const result = { ...resolvedLight };
1881
+ if (darkExtend) {
1882
+ const resolvedDark = {};
1883
+ const lookupDark = (name) => {
1884
+ const found = resolvedDark[name] ?? existing[name];
1885
+ return typeof found === "string" ? found : void 0;
1886
+ };
1887
+ for (const [name, value] of Object.entries(darkExtend)) {
1888
+ resolvedDark[name] = resolveColorEntry(value, lookupDark);
1889
+ }
1890
+ for (const [name, darkValue] of Object.entries(resolvedDark)) {
1891
+ const lightValue = resolvedLight[name];
1892
+ if (lightValue === void 0) continue;
1893
+ result[name] = { light: lightValue, dark: darkValue };
1894
+ }
1853
1895
  }
1854
- return resolved;
1896
+ return result;
1855
1897
  }
1856
1898
  function resolveKbachConfig(config) {
1857
1899
  let theme = { ...import_theme2.defaultTheme, ...config.theme };
@@ -1,5 +1,5 @@
1
1
  export { ColorEntry, ContainerConfig, ThemeConfig, defaultTheme, getTheme, setTheme } from './theme';
2
- export { C as ClassArray, a as ClassDictionary, b as ClassValue, c as ColorScale, d as ColorsAPI, K as KbachConfig, T as ThemeProvider, e as ThemeProviderProps, f as ThemeState, h as applyKbachConfig, i as clsx, r as resolveKbachConfig, u as useColors, j as useDynamicToken, k as useTheme } from './clsx-BCXALPGh.js';
2
+ export { C as ClassArray, a as ClassDictionary, b as ClassValue, c as ColorScale, d as ColorsAPI, K as KbachConfig, T as ThemeProvider, e as ThemeProviderProps, f as ThemeState, h as applyKbachConfig, i as clsx, r as resolveKbachConfig, u as useColors, j as useDynamicToken, k as useTheme } from './clsx-CxjWSCcU.js';
3
3
  export { ThemeMode, getGlobalDarkMode, getGlobalThemeMode, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode } from './darkModeStore';
4
4
  export { deleteDynamicToken, getDynamicToken, setDynamicToken, subscribeDynamicTokens } from './dynamicTokens';
5
5
  import 'react';
package/dist/index.web.js CHANGED
@@ -271,15 +271,33 @@ function resolveColorEntry(value, lookup) {
271
271
  return opacity === void 0 ? base : applyOpacityToHex(base, opacity);
272
272
  }
273
273
  function resolveColors(extend, existing) {
274
- const resolved = {};
275
- const lookup = (name) => {
276
- const found = resolved[name] ?? existing[name];
274
+ const { dark: darkExtend, ...lightExtend } = extend;
275
+ const resolvedLight = {};
276
+ const lookupLight = (name) => {
277
+ const found = resolvedLight[name] ?? existing[name];
277
278
  return typeof found === "string" ? found : void 0;
278
279
  };
279
- for (const [name, value] of Object.entries(extend)) {
280
- resolved[name] = resolveColorEntry(value, lookup);
280
+ for (const [name, value] of Object.entries(lightExtend)) {
281
+ if (typeof value !== "string") continue;
282
+ resolvedLight[name] = resolveColorEntry(value, lookupLight);
281
283
  }
282
- return resolved;
284
+ const result = { ...resolvedLight };
285
+ if (darkExtend) {
286
+ const resolvedDark = {};
287
+ const lookupDark = (name) => {
288
+ const found = resolvedDark[name] ?? existing[name];
289
+ return typeof found === "string" ? found : void 0;
290
+ };
291
+ for (const [name, value] of Object.entries(darkExtend)) {
292
+ resolvedDark[name] = resolveColorEntry(value, lookupDark);
293
+ }
294
+ for (const [name, darkValue] of Object.entries(resolvedDark)) {
295
+ const lightValue = resolvedLight[name];
296
+ if (lightValue === void 0) continue;
297
+ result[name] = { light: lightValue, dark: darkValue };
298
+ }
299
+ }
300
+ return result;
283
301
  }
284
302
  function resolveKbachConfig(config) {
285
303
  let theme = { ...import_theme2.defaultTheme, ...config.theme };
@@ -43,15 +43,33 @@ function resolveColorEntry(value, lookup) {
43
43
  return opacity === void 0 ? base : applyOpacityToHex(base, opacity);
44
44
  }
45
45
  function resolveColors(extend, existing) {
46
- const resolved = {};
47
- const lookup = (name) => {
48
- const found = resolved[name] ?? existing[name];
46
+ const { dark: darkExtend, ...lightExtend } = extend;
47
+ const resolvedLight = {};
48
+ const lookupLight = (name) => {
49
+ const found = resolvedLight[name] ?? existing[name];
49
50
  return typeof found === "string" ? found : void 0;
50
51
  };
51
- for (const [name, value] of Object.entries(extend)) {
52
- resolved[name] = resolveColorEntry(value, lookup);
52
+ for (const [name, value] of Object.entries(lightExtend)) {
53
+ if (typeof value !== "string") continue;
54
+ resolvedLight[name] = resolveColorEntry(value, lookupLight);
53
55
  }
54
- return resolved;
56
+ const result = { ...resolvedLight };
57
+ if (darkExtend) {
58
+ const resolvedDark = {};
59
+ const lookupDark = (name) => {
60
+ const found = resolvedDark[name] ?? existing[name];
61
+ return typeof found === "string" ? found : void 0;
62
+ };
63
+ for (const [name, value] of Object.entries(darkExtend)) {
64
+ resolvedDark[name] = resolveColorEntry(value, lookupDark);
65
+ }
66
+ for (const [name, darkValue] of Object.entries(resolvedDark)) {
67
+ const lightValue = resolvedLight[name];
68
+ if (lightValue === void 0) continue;
69
+ result[name] = { light: lightValue, dark: darkValue };
70
+ }
71
+ }
72
+ return result;
55
73
  }
56
74
  function resolveKbachConfig(config) {
57
75
  let theme = { ...defaultTheme, ...config.theme };
@@ -1379,6 +1379,29 @@ function lookupHex(theme, key) {
1379
1379
  const entry = theme.colors[key];
1380
1380
  return typeof entry === "string" ? entry : null;
1381
1381
  }
1382
+ var MODE_AWARE_COLOR_PREFIXES = ["bg-", "text-", "border-"];
1383
+ function findModeAwareColor(base, theme) {
1384
+ const important = base.startsWith("!") ? "!" : "";
1385
+ const rest = important ? base.slice(1) : base;
1386
+ for (const prefix of MODE_AWARE_COLOR_PREFIXES) {
1387
+ if (!rest.startsWith(prefix)) continue;
1388
+ const value = rest.slice(prefix.length);
1389
+ const entry = theme.colors[value];
1390
+ if (typeof entry === "object" && entry !== null) {
1391
+ return { prefix: `${important}${prefix}`, light: entry.light, dark: entry.dark };
1392
+ }
1393
+ }
1394
+ return null;
1395
+ }
1396
+ function substituteModeAwareColorToken(token, theme, colorScheme) {
1397
+ const segments = token.split(":");
1398
+ const base = segments.pop() ?? token;
1399
+ const modifierPrefix = segments.length > 0 ? `${segments.join(":")}:` : "";
1400
+ const found = findModeAwareColor(base, theme);
1401
+ if (found === null) return token;
1402
+ const hex = colorScheme === "dark" ? found.dark : found.light;
1403
+ return `${modifierPrefix}${found.prefix}[${hex}]`;
1404
+ }
1382
1405
  function hexToRgb(hex) {
1383
1406
  const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
1384
1407
  if (!m) return null;
@@ -2104,7 +2127,8 @@ function resolveStyleJsWithWarnings(classString, theme, colorScheme, pressed, wi
2104
2127
  const warnings = [];
2105
2128
  let shadowOffset = null;
2106
2129
  const transformOps = /* @__PURE__ */ new Map();
2107
- for (const token of classString.split(/\s+/).filter(Boolean)) {
2130
+ for (const rawToken of classString.split(/\s+/).filter(Boolean)) {
2131
+ const token = substituteModeAwareColorToken(rawToken, theme, colorScheme);
2108
2132
  const parsed = parseClass(token);
2109
2133
  const allModifiersHold = parsed.modifiers.every(
2110
2134
  (m) => nativeModifierState(m, theme, colorScheme, pressed, width) === true
@@ -1370,6 +1370,29 @@ function lookupHex(theme, key) {
1370
1370
  const entry = theme.colors[key];
1371
1371
  return typeof entry === "string" ? entry : null;
1372
1372
  }
1373
+ var MODE_AWARE_COLOR_PREFIXES = ["bg-", "text-", "border-"];
1374
+ function findModeAwareColor(base, theme) {
1375
+ const important = base.startsWith("!") ? "!" : "";
1376
+ const rest = important ? base.slice(1) : base;
1377
+ for (const prefix of MODE_AWARE_COLOR_PREFIXES) {
1378
+ if (!rest.startsWith(prefix)) continue;
1379
+ const value = rest.slice(prefix.length);
1380
+ const entry = theme.colors[value];
1381
+ if (typeof entry === "object" && entry !== null) {
1382
+ return { prefix: `${important}${prefix}`, light: entry.light, dark: entry.dark };
1383
+ }
1384
+ }
1385
+ return null;
1386
+ }
1387
+ function substituteModeAwareColorToken(token, theme, colorScheme) {
1388
+ const segments = token.split(":");
1389
+ const base = segments.pop() ?? token;
1390
+ const modifierPrefix = segments.length > 0 ? `${segments.join(":")}:` : "";
1391
+ const found = findModeAwareColor(base, theme);
1392
+ if (found === null) return token;
1393
+ const hex = colorScheme === "dark" ? found.dark : found.light;
1394
+ return `${modifierPrefix}${found.prefix}[${hex}]`;
1395
+ }
1373
1396
  function hexToRgb(hex) {
1374
1397
  const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
1375
1398
  if (!m) return null;
@@ -2095,7 +2118,8 @@ function resolveStyleJsWithWarnings(classString, theme, colorScheme, pressed, wi
2095
2118
  const warnings = [];
2096
2119
  let shadowOffset = null;
2097
2120
  const transformOps = /* @__PURE__ */ new Map();
2098
- for (const token of classString.split(/\s+/).filter(Boolean)) {
2121
+ for (const rawToken of classString.split(/\s+/).filter(Boolean)) {
2122
+ const token = substituteModeAwareColorToken(rawToken, theme, colorScheme);
2099
2123
  const parsed = parseClass(token);
2100
2124
  const allModifiersHold = parsed.modifiers.every(
2101
2125
  (m) => nativeModifierState(m, theme, colorScheme, pressed, width) === true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react-native",
3
- "version": "1.0.0-beta.33",
3
+ "version": "1.0.0-beta.35",
4
4
  "description": "Kbach's utility-class styling engine for React Native — the same class names as @kbach/react, resolved to a StyleSheet-ready style object via a Kotlin/JNI TurboModule (native), the same WASM engine @kbach/react uses (Expo Web / react-native-web), or a pure-JS fallback engine (Expo Go). Ships its own native Android module — one npm install, no separate native package. Beta. Native is Android-only (no iOS bridge yet); works with the React Native CLI, an Expo dev client, AND Expo Go (a reduced-coverage pure-JS fallback, since Expo Go can't load any custom native module).",
5
5
  "keywords": [
6
6
  "css",