@kbach/react 0.6.37 → 0.6.40

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
@@ -184,6 +184,7 @@ var _mountedProviderCount = 0;
184
184
  var _warnedMultipleProviders = false;
185
185
  var _mountedConfigOverrideCount = 0;
186
186
  var _warnedConfigOverride = false;
187
+ var _warnedMediaModeToggle = false;
187
188
  function loadPersistedMode() {
188
189
  try {
189
190
  if (import_core2.isWeb) return localStorage.getItem(STORAGE_KEY);
@@ -262,6 +263,9 @@ function ThemeProvider({
262
263
  return () => {
263
264
  _mountedProviderCount--;
264
265
  if (configOverride) _mountedConfigOverrideCount--;
266
+ if (_prevConfigOverrideRef.current !== void 0 && _mountedProviderCount === 0) {
267
+ (0, import_core2.resetConfig)();
268
+ }
265
269
  };
266
270
  }, []);
267
271
  const [webWidth, setWebWidth] = (0, import_react5.useState)(0);
@@ -328,9 +332,15 @@ function ThemeProvider({
328
332
  return () => window.removeEventListener("storage", onStorage);
329
333
  }, [disablePersistence]);
330
334
  const setMode = (0, import_react5.useCallback)((next) => {
335
+ if (process.env.NODE_ENV !== "production" && import_core2.isWeb && resolvedConfig.darkMode === "media" && !_warnedMediaModeToggle) {
336
+ _warnedMediaModeToggle = true;
337
+ (0, import_core2.kbachWarn)(
338
+ 'setMode()/toggle() was called while darkMode is "media" \u2014 the resolved mode/isDark from useTheme()/useIsDark() will update, but the actual CSS stays driven by prefers-color-scheme and will not change to match. Use darkMode: "attribute" or "class" if the app needs manual toggling.'
339
+ );
340
+ }
331
341
  _setMode(next);
332
342
  if (!disablePersistence) persistMode(next);
333
- }, [disablePersistence]);
343
+ }, [disablePersistence, resolvedConfig.darkMode]);
334
344
  const toggle = (0, import_react5.useCallback)(() => {
335
345
  setMode(mode === "dark" || mode === "system" && systemScheme === "dark" ? "light" : "dark");
336
346
  }, [mode, systemScheme, setMode]);
@@ -499,6 +509,10 @@ function stripWebOnlyProps(s) {
499
509
  delete s.position;
500
510
  }
501
511
  }
512
+ function composeNativeStyle(computed, userStyle) {
513
+ if (!userStyle) return computed;
514
+ return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
515
+ }
502
516
 
503
517
  // src/web-substitute.ts
504
518
  var _rnToHtml = {
@@ -526,11 +540,21 @@ function registerWebElement(rnComponent, htmlTag) {
526
540
  _userMap.set(rnComponent, htmlTag);
527
541
  _cache.delete(rnComponent);
528
542
  }
543
+ var _forwardRefOrMemoType = /* @__PURE__ */ new Set([
544
+ /* @__PURE__ */ Symbol.for("react.forward_ref"),
545
+ /* @__PURE__ */ Symbol.for("react.memo")
546
+ ]);
547
+ function looksLikeRealRNPrimitive(type) {
548
+ const t = type.$$typeof;
549
+ if (t !== void 0) return _forwardRefOrMemoType.has(t);
550
+ return !!type.prototype?.isReactComponent;
551
+ }
529
552
  function getWebTag(type, props) {
530
553
  if (typeof type === "string") return null;
531
554
  if (type === null || typeof type !== "function" && typeof type !== "object") return null;
532
555
  const obj = type;
533
556
  if (_userMap.has(obj)) return _userMap.get(obj);
557
+ if (!looksLikeRealRNPrimitive(obj)) return null;
534
558
  const name = obj.displayName ?? obj.name;
535
559
  if (!name) {
536
560
  _cache.set(obj, null);
@@ -600,8 +624,9 @@ var _rnOnlyProps = /* @__PURE__ */ new Set([
600
624
  "lineBreakStrategyIOS",
601
625
  // TextInput
602
626
  "multiline",
603
- "resizeMode",
604
- // handled separately
627
+ // NOTE: 'resizeMode' (Image's prop, not TextInput's) deliberately does NOT
628
+ // go here — it's handled below in the isImage branch (mapped to CSS
629
+ // object-fit). Blacklisting it here would make that branch unreachable.
605
630
  "blurOnSubmit",
606
631
  "clearButtonMode",
607
632
  "clearTextOnFocus",
@@ -727,7 +752,7 @@ function transformToWebProps(originalName, tag, props) {
727
752
  continue;
728
753
  }
729
754
  if (k === "keyboardType") {
730
- if (!("type" in props) && !("secureTextEntry" in props)) {
755
+ if (!("type" in props) && !props.secureTextEntry) {
731
756
  const mapped = _keyboardTypeMap[v];
732
757
  if (mapped) out.type = mapped;
733
758
  }
@@ -743,7 +768,7 @@ function transformToWebProps(originalName, tag, props) {
743
768
  }
744
769
  }
745
770
  if (isScrollable && k === "horizontal") {
746
- if (v) pendingStyle = { display: "flex", flexDirection: "row", overflowX: "auto" };
771
+ if (v) pendingStyle = { ...pendingStyle ?? {}, display: "flex", flexDirection: "row", overflowX: "auto" };
747
772
  continue;
748
773
  }
749
774
  if (k === "pointerEvents") {
@@ -851,9 +876,10 @@ function styled(Component, baseClasses = "") {
851
876
  const needsWidth = hasResponsiveBuckets(resolved);
852
877
  const width = useConditionalWidth(needsWidth && !isWebPlatform);
853
878
  const breakpoints = needsWidth ? (0, import_core7.getActiveBreakpoints)(width) : EMPTY_BREAKPOINTS;
879
+ const screens = (0, import_core7.getGlobalScreens)();
854
880
  const computedStyle = (0, import_react6.useMemo)(
855
881
  () => isWebPlatform ? {} : (0, import_core7.flatten)(resolved, isDark, { pressed, hover: hovered, focus: focused, disabled, checked }, breakpoints),
856
- [resolved, isDark, pressed, hovered, focused, disabled, checked, width]
882
+ [resolved, isDark, pressed, hovered, focused, disabled, checked, width, screens]
857
883
  // eslint-disable-line react-hooks/exhaustive-deps
858
884
  );
859
885
  const webTag = isWebPlatform ? getWebTag(Component, rest) : null;
@@ -862,7 +888,7 @@ function styled(Component, baseClasses = "") {
862
888
  const compensation = getImpliedRNStyle(webTag, resolved.base);
863
889
  if (!compensation) return flatStyleProp;
864
890
  return flatStyleProp ? { ...compensation, ...flatStyleProp } : compensation;
865
- })() : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
891
+ })() : composeNativeStyle(computedStyle, styleProp);
866
892
  const effectiveComponent = webTag ?? Component;
867
893
  const componentName2 = Component.displayName ?? Component.name ?? "";
868
894
  const effectiveRest = webTag && componentName2 ? transformToWebProps(componentName2, webTag, rest) : rest;
@@ -1002,6 +1028,7 @@ var InteractiveWrapper = (0, import_react9.forwardRef)(
1002
1028
  const needsWidth = hasResponsiveBuckets(resolvedStyle);
1003
1029
  const width = useConditionalWidth(needsWidth && !isWebPlatform);
1004
1030
  const breakpoints = needsWidth ? (0, import_core10.getActiveBreakpoints)(width) : EMPTY_BREAKPOINTS;
1031
+ const screens = (0, import_core10.getGlobalScreens)();
1005
1032
  const [pressed, setPressed] = (0, import_react9.useState)(false);
1006
1033
  const [hovered, setHovered] = (0, import_react9.useState)(false);
1007
1034
  const [focused, setFocused] = (0, import_react9.useState)(false);
@@ -1045,10 +1072,10 @@ var InteractiveWrapper = (0, import_react9.forwardRef)(
1045
1072
  if (isNonStringComponent) stripWebOnlyProps(s);
1046
1073
  return s;
1047
1074
  },
1048
- [resolvedStyle, isDark, pressed, hovered, focused, width, disabled, checked, isNonStringComponent]
1075
+ [resolvedStyle, isDark, pressed, hovered, focused, width, screens, disabled, checked, isNonStringComponent]
1049
1076
  );
1050
1077
  const skipComputedInline = isWebPlatform;
1051
- const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
1078
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : composeNativeStyle(computedStyle, styleProp);
1052
1079
  const componentProps = {
1053
1080
  ref,
1054
1081
  ...restForComponent,
package/dist/index.mjs CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  buildConfig,
8
8
  chain,
9
9
  clearCache,
10
+ composeNativeStyle,
10
11
  defaultColors,
11
12
  defaultTheme,
12
13
  disableRuntimeCSS,
@@ -15,6 +16,7 @@ import {
15
16
  getActiveBreakpoints,
16
17
  getConfig,
17
18
  getEffectiveIsWeb,
19
+ getGlobalScreens,
18
20
  getImpliedRNStyle,
19
21
  getWebTag,
20
22
  hasInteractiveBuckets,
@@ -30,6 +32,7 @@ import {
30
32
  parseClasses,
31
33
  parseHexRgb,
32
34
  registerWebElement,
35
+ resetConfig,
33
36
  resolve,
34
37
  setGlobalDarkMode,
35
38
  setGlobalWidth,
@@ -45,7 +48,7 @@ import {
45
48
  useGlobalDarkMode,
46
49
  useGlobalWidth,
47
50
  useSyncExternalStore
48
- } from "./chunk-ZTDE7V7I.mjs";
51
+ } from "./chunk-LQH3CJ3G.mjs";
49
52
 
50
53
  // src/context.tsx
51
54
  import { createContext, useContext } from "react";
@@ -152,6 +155,7 @@ var _mountedProviderCount = 0;
152
155
  var _warnedMultipleProviders = false;
153
156
  var _mountedConfigOverrideCount = 0;
154
157
  var _warnedConfigOverride = false;
158
+ var _warnedMediaModeToggle = false;
155
159
  function loadPersistedMode() {
156
160
  try {
157
161
  if (isWeb) return localStorage.getItem(STORAGE_KEY);
@@ -230,6 +234,9 @@ function ThemeProvider({
230
234
  return () => {
231
235
  _mountedProviderCount--;
232
236
  if (configOverride) _mountedConfigOverrideCount--;
237
+ if (_prevConfigOverrideRef.current !== void 0 && _mountedProviderCount === 0) {
238
+ resetConfig();
239
+ }
233
240
  };
234
241
  }, []);
235
242
  const [webWidth, setWebWidth] = useState(0);
@@ -296,9 +303,15 @@ function ThemeProvider({
296
303
  return () => window.removeEventListener("storage", onStorage);
297
304
  }, [disablePersistence]);
298
305
  const setMode = useCallback((next) => {
306
+ if (process.env.NODE_ENV !== "production" && isWeb && resolvedConfig.darkMode === "media" && !_warnedMediaModeToggle) {
307
+ _warnedMediaModeToggle = true;
308
+ kbachWarn(
309
+ 'setMode()/toggle() was called while darkMode is "media" \u2014 the resolved mode/isDark from useTheme()/useIsDark() will update, but the actual CSS stays driven by prefers-color-scheme and will not change to match. Use darkMode: "attribute" or "class" if the app needs manual toggling.'
310
+ );
311
+ }
299
312
  _setMode(next);
300
313
  if (!disablePersistence) persistMode(next);
301
- }, [disablePersistence]);
314
+ }, [disablePersistence, resolvedConfig.darkMode]);
302
315
  const toggle = useCallback(() => {
303
316
  setMode(mode === "dark" || mode === "system" && systemScheme === "dark" ? "light" : "dark");
304
317
  }, [mode, systemScheme, setMode]);
@@ -403,9 +416,10 @@ function styled(Component, baseClasses = "") {
403
416
  const needsWidth = hasResponsiveBuckets(resolved);
404
417
  const width = useConditionalWidth(needsWidth && !isWebPlatform);
405
418
  const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
419
+ const screens = getGlobalScreens();
406
420
  const computedStyle = useMemo4(
407
421
  () => isWebPlatform ? {} : flatten(resolved, isDark, { pressed, hover: hovered, focus: focused, disabled, checked }, breakpoints),
408
- [resolved, isDark, pressed, hovered, focused, disabled, checked, width]
422
+ [resolved, isDark, pressed, hovered, focused, disabled, checked, width, screens]
409
423
  // eslint-disable-line react-hooks/exhaustive-deps
410
424
  );
411
425
  const webTag = isWebPlatform ? getWebTag(Component, rest) : null;
@@ -414,7 +428,7 @@ function styled(Component, baseClasses = "") {
414
428
  const compensation = getImpliedRNStyle(webTag, resolved.base);
415
429
  if (!compensation) return flatStyleProp;
416
430
  return flatStyleProp ? { ...compensation, ...flatStyleProp } : compensation;
417
- })() : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
431
+ })() : composeNativeStyle(computedStyle, styleProp);
418
432
  const effectiveComponent = webTag ?? Component;
419
433
  const componentName2 = Component.displayName ?? Component.name ?? "";
420
434
  const effectiveRest = webTag && componentName2 ? transformToWebProps(componentName2, webTag, rest) : rest;
@@ -163,6 +163,10 @@ function stripWebOnlyProps(s) {
163
163
  delete s.position;
164
164
  }
165
165
  }
166
+ function composeNativeStyle(computed, userStyle) {
167
+ if (!userStyle) return computed;
168
+ return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
169
+ }
166
170
 
167
171
  // src/InteractiveWrapper.tsx
168
172
  var InteractiveWrapper = (0, import_react2.forwardRef)(
@@ -188,6 +192,7 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
188
192
  const needsWidth = hasResponsiveBuckets(resolvedStyle);
189
193
  const width = useConditionalWidth(needsWidth && !isWebPlatform);
190
194
  const breakpoints = needsWidth ? (0, import_core4.getActiveBreakpoints)(width) : EMPTY_BREAKPOINTS;
195
+ const screens = (0, import_core4.getGlobalScreens)();
191
196
  const [pressed, setPressed] = (0, import_react2.useState)(false);
192
197
  const [hovered, setHovered] = (0, import_react2.useState)(false);
193
198
  const [focused, setFocused] = (0, import_react2.useState)(false);
@@ -231,10 +236,10 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
231
236
  if (isNonStringComponent) stripWebOnlyProps(s);
232
237
  return s;
233
238
  },
234
- [resolvedStyle, isDark, pressed, hovered, focused, width, disabled, checked, isNonStringComponent]
239
+ [resolvedStyle, isDark, pressed, hovered, focused, width, screens, disabled, checked, isNonStringComponent]
235
240
  );
236
241
  const skipComputedInline = isWebPlatform;
237
- const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
242
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : composeNativeStyle(computedStyle, styleProp);
238
243
  const componentProps = {
239
244
  ref,
240
245
  ...restForComponent,
@@ -291,7 +296,7 @@ var DarkWrapper = import_react3.default.forwardRef(
291
296
  return s;
292
297
  }, [resolvedStyle, isDark, width, screens, isNonStringComponent]);
293
298
  const skipComputedInline = isWebPlatform;
294
- const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
299
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : composeNativeStyle(computedStyle, styleProp);
295
300
  const props = { ref, ...rest, style: finalStyle };
296
301
  return Array.isArray(children) ? import_react3.default.createElement(Component, props, ...children) : import_react3.default.createElement(Component, props, children);
297
302
  }
@@ -319,11 +324,21 @@ var _rnToHtml = {
319
324
  };
320
325
  var _userMap = /* @__PURE__ */ new Map();
321
326
  var _cache = /* @__PURE__ */ new WeakMap();
327
+ var _forwardRefOrMemoType = /* @__PURE__ */ new Set([
328
+ /* @__PURE__ */ Symbol.for("react.forward_ref"),
329
+ /* @__PURE__ */ Symbol.for("react.memo")
330
+ ]);
331
+ function looksLikeRealRNPrimitive(type) {
332
+ const t = type.$$typeof;
333
+ if (t !== void 0) return _forwardRefOrMemoType.has(t);
334
+ return !!type.prototype?.isReactComponent;
335
+ }
322
336
  function getWebTag(type, props) {
323
337
  if (typeof type === "string") return null;
324
338
  if (type === null || typeof type !== "function" && typeof type !== "object") return null;
325
339
  const obj = type;
326
340
  if (_userMap.has(obj)) return _userMap.get(obj);
341
+ if (!looksLikeRealRNPrimitive(obj)) return null;
327
342
  const name = obj.displayName ?? obj.name;
328
343
  if (!name) {
329
344
  _cache.set(obj, null);
@@ -393,8 +408,9 @@ var _rnOnlyProps = /* @__PURE__ */ new Set([
393
408
  "lineBreakStrategyIOS",
394
409
  // TextInput
395
410
  "multiline",
396
- "resizeMode",
397
- // handled separately
411
+ // NOTE: 'resizeMode' (Image's prop, not TextInput's) deliberately does NOT
412
+ // go here — it's handled below in the isImage branch (mapped to CSS
413
+ // object-fit). Blacklisting it here would make that branch unreachable.
398
414
  "blurOnSubmit",
399
415
  "clearButtonMode",
400
416
  "clearTextOnFocus",
@@ -520,7 +536,7 @@ function transformToWebProps(originalName, tag, props) {
520
536
  continue;
521
537
  }
522
538
  if (k === "keyboardType") {
523
- if (!("type" in props) && !("secureTextEntry" in props)) {
539
+ if (!("type" in props) && !props.secureTextEntry) {
524
540
  const mapped = _keyboardTypeMap[v];
525
541
  if (mapped) out.type = mapped;
526
542
  }
@@ -536,7 +552,7 @@ function transformToWebProps(originalName, tag, props) {
536
552
  }
537
553
  }
538
554
  if (isScrollable && k === "horizontal") {
539
- if (v) pendingStyle = { display: "flex", flexDirection: "row", overflowX: "auto" };
555
+ if (v) pendingStyle = { ...pendingStyle ?? {}, display: "flex", flexDirection: "row", overflowX: "auto" };
540
556
  continue;
541
557
  }
542
558
  if (k === "pointerEvents") {
@@ -630,10 +646,6 @@ function omitConsumed(props) {
630
646
  }
631
647
  return out;
632
648
  }
633
- function mergeStyle(computed, userStyle) {
634
- if (!userStyle) return computed;
635
- return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
636
- }
637
649
  function withImpliedRNStyleIfNeeded(webTag, resolvedBase, userStyle) {
638
650
  const compensation = getImpliedRNStyle(webTag, resolvedBase);
639
651
  if (!compensation) return userStyle;
@@ -709,7 +721,7 @@ function processElement(type, rawProps, key, isStaticChildren) {
709
721
  const computedStyle = (0, import_core6.flatten)(resolved, false);
710
722
  stripInternalMarkers(computedStyle);
711
723
  if (typeof effectiveType !== "string") stripWebOnlyProps(computedStyle);
712
- finalStyle = mergeStyle(computedStyle, userStyle);
724
+ finalStyle = composeNativeStyle(computedStyle, userStyle);
713
725
  } else {
714
726
  finalStyle = userStyle ?? void 0;
715
727
  }
@@ -3,8 +3,8 @@ import {
3
3
  Fragment,
4
4
  jsx,
5
5
  jsxs
6
- } from "./chunk-OUKVG4DI.mjs";
7
- import "./chunk-ZTDE7V7I.mjs";
6
+ } from "./chunk-ZFVHIT3V.mjs";
7
+ import "./chunk-LQH3CJ3G.mjs";
8
8
 
9
9
  // src/jsx-dev-runtime.tsx
10
10
  function jsxDEV(type, props, key, isStaticChildren, source, self) {
@@ -164,6 +164,10 @@ function stripWebOnlyProps(s) {
164
164
  delete s.position;
165
165
  }
166
166
  }
167
+ function composeNativeStyle(computed, userStyle) {
168
+ if (!userStyle) return computed;
169
+ return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
170
+ }
167
171
 
168
172
  // src/InteractiveWrapper.tsx
169
173
  var InteractiveWrapper = (0, import_react2.forwardRef)(
@@ -189,6 +193,7 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
189
193
  const needsWidth = hasResponsiveBuckets(resolvedStyle);
190
194
  const width = useConditionalWidth(needsWidth && !isWebPlatform);
191
195
  const breakpoints = needsWidth ? (0, import_core4.getActiveBreakpoints)(width) : EMPTY_BREAKPOINTS;
196
+ const screens = (0, import_core4.getGlobalScreens)();
192
197
  const [pressed, setPressed] = (0, import_react2.useState)(false);
193
198
  const [hovered, setHovered] = (0, import_react2.useState)(false);
194
199
  const [focused, setFocused] = (0, import_react2.useState)(false);
@@ -232,10 +237,10 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
232
237
  if (isNonStringComponent) stripWebOnlyProps(s);
233
238
  return s;
234
239
  },
235
- [resolvedStyle, isDark, pressed, hovered, focused, width, disabled, checked, isNonStringComponent]
240
+ [resolvedStyle, isDark, pressed, hovered, focused, width, screens, disabled, checked, isNonStringComponent]
236
241
  );
237
242
  const skipComputedInline = isWebPlatform;
238
- const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
243
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : composeNativeStyle(computedStyle, styleProp);
239
244
  const componentProps = {
240
245
  ref,
241
246
  ...restForComponent,
@@ -292,7 +297,7 @@ var DarkWrapper = import_react3.default.forwardRef(
292
297
  return s;
293
298
  }, [resolvedStyle, isDark, width, screens, isNonStringComponent]);
294
299
  const skipComputedInline = isWebPlatform;
295
- const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
300
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : composeNativeStyle(computedStyle, styleProp);
296
301
  const props = { ref, ...rest, style: finalStyle };
297
302
  return Array.isArray(children) ? import_react3.default.createElement(Component, props, ...children) : import_react3.default.createElement(Component, props, children);
298
303
  }
@@ -325,11 +330,21 @@ function registerWebElement(rnComponent, htmlTag) {
325
330
  _userMap.set(rnComponent, htmlTag);
326
331
  _cache.delete(rnComponent);
327
332
  }
333
+ var _forwardRefOrMemoType = /* @__PURE__ */ new Set([
334
+ /* @__PURE__ */ Symbol.for("react.forward_ref"),
335
+ /* @__PURE__ */ Symbol.for("react.memo")
336
+ ]);
337
+ function looksLikeRealRNPrimitive(type) {
338
+ const t = type.$$typeof;
339
+ if (t !== void 0) return _forwardRefOrMemoType.has(t);
340
+ return !!type.prototype?.isReactComponent;
341
+ }
328
342
  function getWebTag(type, props) {
329
343
  if (typeof type === "string") return null;
330
344
  if (type === null || typeof type !== "function" && typeof type !== "object") return null;
331
345
  const obj = type;
332
346
  if (_userMap.has(obj)) return _userMap.get(obj);
347
+ if (!looksLikeRealRNPrimitive(obj)) return null;
333
348
  const name = obj.displayName ?? obj.name;
334
349
  if (!name) {
335
350
  _cache.set(obj, null);
@@ -399,8 +414,9 @@ var _rnOnlyProps = /* @__PURE__ */ new Set([
399
414
  "lineBreakStrategyIOS",
400
415
  // TextInput
401
416
  "multiline",
402
- "resizeMode",
403
- // handled separately
417
+ // NOTE: 'resizeMode' (Image's prop, not TextInput's) deliberately does NOT
418
+ // go here — it's handled below in the isImage branch (mapped to CSS
419
+ // object-fit). Blacklisting it here would make that branch unreachable.
404
420
  "blurOnSubmit",
405
421
  "clearButtonMode",
406
422
  "clearTextOnFocus",
@@ -526,7 +542,7 @@ function transformToWebProps(originalName, tag, props) {
526
542
  continue;
527
543
  }
528
544
  if (k === "keyboardType") {
529
- if (!("type" in props) && !("secureTextEntry" in props)) {
545
+ if (!("type" in props) && !props.secureTextEntry) {
530
546
  const mapped = _keyboardTypeMap[v];
531
547
  if (mapped) out.type = mapped;
532
548
  }
@@ -542,7 +558,7 @@ function transformToWebProps(originalName, tag, props) {
542
558
  }
543
559
  }
544
560
  if (isScrollable && k === "horizontal") {
545
- if (v) pendingStyle = { display: "flex", flexDirection: "row", overflowX: "auto" };
561
+ if (v) pendingStyle = { ...pendingStyle ?? {}, display: "flex", flexDirection: "row", overflowX: "auto" };
546
562
  continue;
547
563
  }
548
564
  if (k === "pointerEvents") {
@@ -636,10 +652,6 @@ function omitConsumed(props) {
636
652
  }
637
653
  return out;
638
654
  }
639
- function mergeStyle(computed, userStyle) {
640
- if (!userStyle) return computed;
641
- return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
642
- }
643
655
  function withImpliedRNStyleIfNeeded(webTag, resolvedBase, userStyle) {
644
656
  const compensation = getImpliedRNStyle(webTag, resolvedBase);
645
657
  if (!compensation) return userStyle;
@@ -715,7 +727,7 @@ function processElement(type, rawProps, key, isStaticChildren) {
715
727
  const computedStyle = (0, import_core6.flatten)(resolved, false);
716
728
  stripInternalMarkers(computedStyle);
717
729
  if (typeof effectiveType !== "string") stripWebOnlyProps(computedStyle);
718
- finalStyle = mergeStyle(computedStyle, userStyle);
730
+ finalStyle = composeNativeStyle(computedStyle, userStyle);
719
731
  } else {
720
732
  finalStyle = userStyle ?? void 0;
721
733
  }
@@ -4,10 +4,10 @@ import {
4
4
  _invalidateDefaultFontCache,
5
5
  jsx,
6
6
  jsxs
7
- } from "./chunk-OUKVG4DI.mjs";
7
+ } from "./chunk-ZFVHIT3V.mjs";
8
8
  import {
9
9
  registerWebElement
10
- } from "./chunk-ZTDE7V7I.mjs";
10
+ } from "./chunk-LQH3CJ3G.mjs";
11
11
  export {
12
12
  Fragment,
13
13
  _invalidateDefaultFontCache,
@@ -11,6 +11,22 @@ import { Plugin } from 'vite';
11
11
  * Plugin authors can register custom modifiers via registerModifier().
12
12
  */
13
13
  interface ModifierDef {
14
+ /**
15
+ * Cascade priority for CSS rule ORDER — NOT specificity. Two rules that
16
+ * differ only by modifier (e.g. `.hover\:bg-blue-6:hover` and
17
+ * `.focus\:bg-red-6:focus`) have equal CSS specificity, so when both
18
+ * conditions are true at once (hovering AND focused), the winner is
19
+ * whichever rule appears LATER in the stylesheet — CSS's normal same-
20
+ * specificity tiebreak. Without a fixed priority, "later" would depend on
21
+ * encounter order (whichever class the app happens to render/scan first),
22
+ * making the winner effectively random and inconsistent across reloads/
23
+ * builds. `order` fixes that: rules are emitted/injected sorted by this
24
+ * value (ascending — higher wins ties), regardless of source order, so
25
+ * e.g. `disabled:` always beats `hover:` on the same element no matter
26
+ * which one was written first in the className or rendered first in the
27
+ * app. Omit for the default (0). See getModifierOrder() below.
28
+ */
29
+ order?: number;
14
30
  /** CSS pseudo-class or pseudo-element appended to the selector (e.g. ':hover', '::before') */
15
31
  pseudo?: string;
16
32
  /** Ancestor selector prefix INCLUDING trailing space (e.g. '.group:hover ', '.peer:focus ~ ') */
@@ -128,6 +144,7 @@ interface FrameworkConfig {
128
144
  content?: string[];
129
145
  }
130
146
 
147
+ declare function formatKbachCSS(tokenCSS: Map<string, string>, theme: ThemeConfig, responsiveRe: RegExp): string;
131
148
  interface KbachPluginOptions {
132
149
  framework?: FrameworkConfig;
133
150
  /** Directories to scan for class strings (relative to Vite root). Defaults to common source dirs. */
@@ -135,4 +152,4 @@ interface KbachPluginOptions {
135
152
  }
136
153
  declare function kbach(userConfigOrOptions?: FrameworkConfig | KbachPluginOptions): Plugin;
137
154
 
138
- export { type KbachPluginOptions, kbach };
155
+ export { type KbachPluginOptions, formatKbachCSS, kbach };
@@ -11,6 +11,22 @@ import { Plugin } from 'vite';
11
11
  * Plugin authors can register custom modifiers via registerModifier().
12
12
  */
13
13
  interface ModifierDef {
14
+ /**
15
+ * Cascade priority for CSS rule ORDER — NOT specificity. Two rules that
16
+ * differ only by modifier (e.g. `.hover\:bg-blue-6:hover` and
17
+ * `.focus\:bg-red-6:focus`) have equal CSS specificity, so when both
18
+ * conditions are true at once (hovering AND focused), the winner is
19
+ * whichever rule appears LATER in the stylesheet — CSS's normal same-
20
+ * specificity tiebreak. Without a fixed priority, "later" would depend on
21
+ * encounter order (whichever class the app happens to render/scan first),
22
+ * making the winner effectively random and inconsistent across reloads/
23
+ * builds. `order` fixes that: rules are emitted/injected sorted by this
24
+ * value (ascending — higher wins ties), regardless of source order, so
25
+ * e.g. `disabled:` always beats `hover:` on the same element no matter
26
+ * which one was written first in the className or rendered first in the
27
+ * app. Omit for the default (0). See getModifierOrder() below.
28
+ */
29
+ order?: number;
14
30
  /** CSS pseudo-class or pseudo-element appended to the selector (e.g. ':hover', '::before') */
15
31
  pseudo?: string;
16
32
  /** Ancestor selector prefix INCLUDING trailing space (e.g. '.group:hover ', '.peer:focus ~ ') */
@@ -128,6 +144,7 @@ interface FrameworkConfig {
128
144
  content?: string[];
129
145
  }
130
146
 
147
+ declare function formatKbachCSS(tokenCSS: Map<string, string>, theme: ThemeConfig, responsiveRe: RegExp): string;
131
148
  interface KbachPluginOptions {
132
149
  framework?: FrameworkConfig;
133
150
  /** Directories to scan for class strings (relative to Vite root). Defaults to common source dirs. */
@@ -135,4 +152,4 @@ interface KbachPluginOptions {
135
152
  }
136
153
  declare function kbach(userConfigOrOptions?: FrameworkConfig | KbachPluginOptions): Plugin;
137
154
 
138
- export { type KbachPluginOptions, kbach };
155
+ export { type KbachPluginOptions, formatKbachCSS, kbach };