@kbach/react 0.6.39 → 0.6.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -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,7 +16,8 @@ import {
15
16
  getActiveBreakpoints,
16
17
  getConfig,
17
18
  getEffectiveIsWeb,
18
- getImpliedRNStyle,
19
+ getGlobalScreens,
20
+ getImpliedRNClasses,
19
21
  getWebTag,
20
22
  hasInteractiveBuckets,
21
23
  hasResponsiveBuckets,
@@ -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-NE4LAJXK.mjs";
51
+ } from "./chunk-PTOTQ6A2.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,10 +234,13 @@ 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);
236
- const effectiveWidth = windowWidthProp ?? (isWeb ? webWidth : 0);
243
+ const effectiveWidth = getEffectiveIsWeb() ? webWidth : windowWidthProp ?? 0;
237
244
  const numericScreens = useMemo3(() => {
238
245
  const out = {};
239
246
  for (const [k, v] of Object.entries(resolvedConfig.theme.screens ?? {})) {
@@ -277,7 +284,7 @@ function ThemeProvider({
277
284
  getSystemScheme,
278
285
  getSystemSchemeServerSnapshot
279
286
  );
280
- const systemScheme = isWeb ? webScheme : colorScheme === "dark" ? "dark" : "light";
287
+ const systemScheme = getEffectiveIsWeb() ? webScheme : colorScheme === "dark" ? "dark" : "light";
281
288
  const [mode, _setMode] = useState(defaultMode);
282
289
  useEffect(() => {
283
290
  if (disablePersistence) return;
@@ -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]);
@@ -398,23 +411,22 @@ function styled(Component, baseClasses = "") {
398
411
  if (!isWebPlatform) setFocused(false);
399
412
  }), [onBlur, isWebPlatform]);
400
413
  const combined = extraClasses ? `${baseClasses} ${extraClasses}` : baseClasses;
401
- const resolved = useMemo4(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
414
+ const firstPassResolved = useMemo4(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
415
+ const webTag = isWebPlatform ? getWebTag(Component, rest) : null;
416
+ const impliedClasses = getImpliedRNClasses(webTag, firstPassResolved.base);
417
+ const finalClassStr = impliedClasses ? `${combined} ${impliedClasses}` : combined;
418
+ const resolved = useMemo4(() => resolve(finalClassStr, config.theme, config.darkMode), [finalClassStr, config.theme, config.darkMode]);
402
419
  const hasInteractive = useMemo4(() => hasInteractiveBuckets(resolved), [resolved]);
403
420
  const needsWidth = hasResponsiveBuckets(resolved);
404
421
  const width = useConditionalWidth(needsWidth && !isWebPlatform);
405
422
  const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
423
+ const screens = getGlobalScreens();
406
424
  const computedStyle = useMemo4(
407
425
  () => isWebPlatform ? {} : flatten(resolved, isDark, { pressed, hover: hovered, focus: focused, disabled, checked }, breakpoints),
408
- [resolved, isDark, pressed, hovered, focused, disabled, checked, width]
426
+ [resolved, isDark, pressed, hovered, focused, disabled, checked, width, screens]
409
427
  // eslint-disable-line react-hooks/exhaustive-deps
410
428
  );
411
- const webTag = isWebPlatform ? getWebTag(Component, rest) : null;
412
- const finalStyle = isWebPlatform ? (() => {
413
- const flatStyleProp = (Array.isArray(styleProp) ? Object.assign({}, ...styleProp) : styleProp) ?? void 0;
414
- const compensation = getImpliedRNStyle(webTag, resolved.base);
415
- if (!compensation) return flatStyleProp;
416
- return flatStyleProp ? { ...compensation, ...flatStyleProp } : compensation;
417
- })() : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
429
+ const finalStyle = isWebPlatform ? (Array.isArray(styleProp) ? Object.assign({}, ...styleProp) : styleProp) ?? void 0 : composeNativeStyle(computedStyle, styleProp);
418
430
  const effectiveComponent = webTag ?? Component;
419
431
  const componentName2 = Component.displayName ?? Component.name ?? "";
420
432
  const effectiveRest = webTag && componentName2 ? transformToWebProps(componentName2, webTag, rest) : rest;
@@ -423,7 +435,7 @@ function styled(Component, baseClasses = "") {
423
435
  ...effectiveRest,
424
436
  style: finalStyle,
425
437
  // className lets injected CSS rules (group-hover:, before:, print:, etc.) match the element.
426
- ...isWebPlatform && combined ? { className: normalizeClassString(combined) } : {},
438
+ ...isWebPlatform && finalClassStr ? { className: normalizeClassString(finalClassStr) } : {},
427
439
  // Only attach state-tracking handlers when interactive modifiers are present.
428
440
  // Always forward user-provided handlers to avoid silently swallowing them.
429
441
  // Gated on isNative, not isWeb: isWeb is false during SSR too (no `window` there),
@@ -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
  }
@@ -348,18 +353,17 @@ function getWebTag(type, props) {
348
353
  _cache.set(obj, tag);
349
354
  return tag;
350
355
  }
351
- function getImpliedRNStyle(webTag, resolvedBase) {
356
+ function getImpliedRNClasses(webTag, resolvedBase) {
352
357
  if (!webTag || !resolvedBase) return void 0;
353
- const compensation = {};
354
- if (resolvedBase.position === void 0) compensation.position = "relative";
358
+ const classes = [];
359
+ if (resolvedBase.position === void 0) classes.push("relative");
355
360
  const explicitDisplay = resolvedBase.display;
356
361
  const hasFlexItemProps = "flexGrow" in resolvedBase || "flexShrink" in resolvedBase || "flex" in resolvedBase || "gap" in resolvedBase || "columnGap" in resolvedBase || "rowGap" in resolvedBase;
357
362
  const willBeFlex = explicitDisplay === "flex" || explicitDisplay === void 0 && hasFlexItemProps;
358
- if (willBeFlex) {
359
- if (explicitDisplay === void 0) compensation.display = "flex";
360
- if (resolvedBase.flexDirection === void 0) compensation.flexDirection = "column";
363
+ if (willBeFlex && (explicitDisplay === void 0 || resolvedBase.flexDirection === void 0)) {
364
+ classes.push("flex-col");
361
365
  }
362
- return Object.keys(compensation).length > 0 ? compensation : void 0;
366
+ return classes.length > 0 ? classes.join(" ") : void 0;
363
367
  }
364
368
  var _rnOnlyProps = /* @__PURE__ */ new Set([
365
369
  // Interaction
@@ -403,8 +407,9 @@ var _rnOnlyProps = /* @__PURE__ */ new Set([
403
407
  "lineBreakStrategyIOS",
404
408
  // TextInput
405
409
  "multiline",
406
- "resizeMode",
407
- // handled separately
410
+ // NOTE: 'resizeMode' (Image's prop, not TextInput's) deliberately does NOT
411
+ // go here — it's handled below in the isImage branch (mapped to CSS
412
+ // object-fit). Blacklisting it here would make that branch unreachable.
408
413
  "blurOnSubmit",
409
414
  "clearButtonMode",
410
415
  "clearTextOnFocus",
@@ -530,7 +535,7 @@ function transformToWebProps(originalName, tag, props) {
530
535
  continue;
531
536
  }
532
537
  if (k === "keyboardType") {
533
- if (!("type" in props) && !("secureTextEntry" in props)) {
538
+ if (!("type" in props) && !props.secureTextEntry) {
534
539
  const mapped = _keyboardTypeMap[v];
535
540
  if (mapped) out.type = mapped;
536
541
  }
@@ -546,7 +551,7 @@ function transformToWebProps(originalName, tag, props) {
546
551
  }
547
552
  }
548
553
  if (isScrollable && k === "horizontal") {
549
- if (v) pendingStyle = { display: "flex", flexDirection: "row", overflowX: "auto" };
554
+ if (v) pendingStyle = { ...pendingStyle ?? {}, display: "flex", flexDirection: "row", overflowX: "auto" };
550
555
  continue;
551
556
  }
552
557
  if (k === "pointerEvents") {
@@ -640,15 +645,6 @@ function omitConsumed(props) {
640
645
  }
641
646
  return out;
642
647
  }
643
- function mergeStyle(computed, userStyle) {
644
- if (!userStyle) return computed;
645
- return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
646
- }
647
- function withImpliedRNStyleIfNeeded(webTag, resolvedBase, userStyle) {
648
- const compensation = getImpliedRNStyle(webTag, resolvedBase);
649
- if (!compensation) return userStyle;
650
- return Array.isArray(userStyle) ? [compensation, ...userStyle] : { ...compensation, ...userStyle };
651
- }
652
648
  function makeElement(isStaticChildren, type, props, key) {
653
649
  return (isStaticChildren ? import_jsx_runtime.jsxs : import_jsx_runtime.jsx)(type, props, key);
654
650
  }
@@ -666,7 +662,7 @@ function processElement(type, rawProps, key, isStaticChildren) {
666
662
  }
667
663
  const classStr = classStrRaw;
668
664
  const config = (0, import_core6.getConfig)();
669
- const expandedClassStr = classStr ? (0, import_core6.expandModeAwareColorClasses)(classStr, config.theme.colors) : classStr;
665
+ let expandedClassStr = classStr ? (0, import_core6.expandModeAwareColorClasses)(classStr, config.theme.colors) : classStr;
670
666
  if (!import_core6.isNative && (0, import_core6.isRuntimeCSSDisabled)() && expandedClassStr && !__kbachStyles) {
671
667
  const { style: userStyle2, ...passProps2 } = omitConsumed(workingProps);
672
668
  return makeElement(isStaticChildren, effectiveType, {
@@ -691,9 +687,13 @@ function processElement(type, rawProps, key, isStaticChildren) {
691
687
  const finalStyle2 = userStyle2 ? Array.isArray(userStyle2) ? { fontFamily: defaultFont, ...Object.assign({}, ...userStyle2) } : { fontFamily: defaultFont, ...userStyle2 } : { fontFamily: defaultFont };
692
688
  return makeElement(isStaticChildren, effectiveType, { ...passProps2, style: finalStyle2 }, key);
693
689
  }
694
- const resolved = !import_core6.isWeb && __kbachStyles != null ? __kbachStyles : expandedClassStr ? (0, import_core6.resolve)(expandedClassStr, config.theme, config.darkMode) : {};
695
- const { style: rawUserStyle, ...passProps } = omitConsumed(workingProps);
696
- const userStyle = withImpliedRNStyleIfNeeded(webTag, resolved.base, rawUserStyle);
690
+ let resolved = import_core6.isNative && __kbachStyles != null ? __kbachStyles : expandedClassStr ? (0, import_core6.resolve)(expandedClassStr, config.theme, config.darkMode) : {};
691
+ const impliedClasses = getImpliedRNClasses(webTag, resolved.base);
692
+ if (impliedClasses) {
693
+ expandedClassStr = expandedClassStr ? `${expandedClassStr} ${impliedClasses}` : impliedClasses;
694
+ resolved = (0, import_core6.resolve)(expandedClassStr, config.theme, config.darkMode);
695
+ }
696
+ const { style: userStyle, ...passProps } = omitConsumed(workingProps);
697
697
  const { interactive, modeOrResponsive } = bucketMods(resolved);
698
698
  if (interactive) {
699
699
  return (0, import_jsx_runtime.jsx)(InteractiveWrapper, {
@@ -719,7 +719,7 @@ function processElement(type, rawProps, key, isStaticChildren) {
719
719
  const computedStyle = (0, import_core6.flatten)(resolved, false);
720
720
  stripInternalMarkers(computedStyle);
721
721
  if (typeof effectiveType !== "string") stripWebOnlyProps(computedStyle);
722
- finalStyle = mergeStyle(computedStyle, userStyle);
722
+ finalStyle = composeNativeStyle(computedStyle, userStyle);
723
723
  } else {
724
724
  finalStyle = userStyle ?? void 0;
725
725
  }
@@ -3,8 +3,8 @@ import {
3
3
  Fragment,
4
4
  jsx,
5
5
  jsxs
6
- } from "./chunk-Z4ZOGRZG.mjs";
7
- import "./chunk-NE4LAJXK.mjs";
6
+ } from "./chunk-LM3OWOK2.mjs";
7
+ import "./chunk-PTOTQ6A2.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
  }
@@ -354,18 +359,17 @@ function getWebTag(type, props) {
354
359
  _cache.set(obj, tag);
355
360
  return tag;
356
361
  }
357
- function getImpliedRNStyle(webTag, resolvedBase) {
362
+ function getImpliedRNClasses(webTag, resolvedBase) {
358
363
  if (!webTag || !resolvedBase) return void 0;
359
- const compensation = {};
360
- if (resolvedBase.position === void 0) compensation.position = "relative";
364
+ const classes = [];
365
+ if (resolvedBase.position === void 0) classes.push("relative");
361
366
  const explicitDisplay = resolvedBase.display;
362
367
  const hasFlexItemProps = "flexGrow" in resolvedBase || "flexShrink" in resolvedBase || "flex" in resolvedBase || "gap" in resolvedBase || "columnGap" in resolvedBase || "rowGap" in resolvedBase;
363
368
  const willBeFlex = explicitDisplay === "flex" || explicitDisplay === void 0 && hasFlexItemProps;
364
- if (willBeFlex) {
365
- if (explicitDisplay === void 0) compensation.display = "flex";
366
- if (resolvedBase.flexDirection === void 0) compensation.flexDirection = "column";
369
+ if (willBeFlex && (explicitDisplay === void 0 || resolvedBase.flexDirection === void 0)) {
370
+ classes.push("flex-col");
367
371
  }
368
- return Object.keys(compensation).length > 0 ? compensation : void 0;
372
+ return classes.length > 0 ? classes.join(" ") : void 0;
369
373
  }
370
374
  var _rnOnlyProps = /* @__PURE__ */ new Set([
371
375
  // Interaction
@@ -409,8 +413,9 @@ var _rnOnlyProps = /* @__PURE__ */ new Set([
409
413
  "lineBreakStrategyIOS",
410
414
  // TextInput
411
415
  "multiline",
412
- "resizeMode",
413
- // handled separately
416
+ // NOTE: 'resizeMode' (Image's prop, not TextInput's) deliberately does NOT
417
+ // go here — it's handled below in the isImage branch (mapped to CSS
418
+ // object-fit). Blacklisting it here would make that branch unreachable.
414
419
  "blurOnSubmit",
415
420
  "clearButtonMode",
416
421
  "clearTextOnFocus",
@@ -536,7 +541,7 @@ function transformToWebProps(originalName, tag, props) {
536
541
  continue;
537
542
  }
538
543
  if (k === "keyboardType") {
539
- if (!("type" in props) && !("secureTextEntry" in props)) {
544
+ if (!("type" in props) && !props.secureTextEntry) {
540
545
  const mapped = _keyboardTypeMap[v];
541
546
  if (mapped) out.type = mapped;
542
547
  }
@@ -552,7 +557,7 @@ function transformToWebProps(originalName, tag, props) {
552
557
  }
553
558
  }
554
559
  if (isScrollable && k === "horizontal") {
555
- if (v) pendingStyle = { display: "flex", flexDirection: "row", overflowX: "auto" };
560
+ if (v) pendingStyle = { ...pendingStyle ?? {}, display: "flex", flexDirection: "row", overflowX: "auto" };
556
561
  continue;
557
562
  }
558
563
  if (k === "pointerEvents") {
@@ -646,15 +651,6 @@ function omitConsumed(props) {
646
651
  }
647
652
  return out;
648
653
  }
649
- function mergeStyle(computed, userStyle) {
650
- if (!userStyle) return computed;
651
- return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
652
- }
653
- function withImpliedRNStyleIfNeeded(webTag, resolvedBase, userStyle) {
654
- const compensation = getImpliedRNStyle(webTag, resolvedBase);
655
- if (!compensation) return userStyle;
656
- return Array.isArray(userStyle) ? [compensation, ...userStyle] : { ...compensation, ...userStyle };
657
- }
658
654
  function makeElement(isStaticChildren, type, props, key) {
659
655
  return (isStaticChildren ? import_jsx_runtime.jsxs : import_jsx_runtime.jsx)(type, props, key);
660
656
  }
@@ -672,7 +668,7 @@ function processElement(type, rawProps, key, isStaticChildren) {
672
668
  }
673
669
  const classStr = classStrRaw;
674
670
  const config = (0, import_core6.getConfig)();
675
- const expandedClassStr = classStr ? (0, import_core6.expandModeAwareColorClasses)(classStr, config.theme.colors) : classStr;
671
+ let expandedClassStr = classStr ? (0, import_core6.expandModeAwareColorClasses)(classStr, config.theme.colors) : classStr;
676
672
  if (!import_core6.isNative && (0, import_core6.isRuntimeCSSDisabled)() && expandedClassStr && !__kbachStyles) {
677
673
  const { style: userStyle2, ...passProps2 } = omitConsumed(workingProps);
678
674
  return makeElement(isStaticChildren, effectiveType, {
@@ -697,9 +693,13 @@ function processElement(type, rawProps, key, isStaticChildren) {
697
693
  const finalStyle2 = userStyle2 ? Array.isArray(userStyle2) ? { fontFamily: defaultFont, ...Object.assign({}, ...userStyle2) } : { fontFamily: defaultFont, ...userStyle2 } : { fontFamily: defaultFont };
698
694
  return makeElement(isStaticChildren, effectiveType, { ...passProps2, style: finalStyle2 }, key);
699
695
  }
700
- const resolved = !import_core6.isWeb && __kbachStyles != null ? __kbachStyles : expandedClassStr ? (0, import_core6.resolve)(expandedClassStr, config.theme, config.darkMode) : {};
701
- const { style: rawUserStyle, ...passProps } = omitConsumed(workingProps);
702
- const userStyle = withImpliedRNStyleIfNeeded(webTag, resolved.base, rawUserStyle);
696
+ let resolved = import_core6.isNative && __kbachStyles != null ? __kbachStyles : expandedClassStr ? (0, import_core6.resolve)(expandedClassStr, config.theme, config.darkMode) : {};
697
+ const impliedClasses = getImpliedRNClasses(webTag, resolved.base);
698
+ if (impliedClasses) {
699
+ expandedClassStr = expandedClassStr ? `${expandedClassStr} ${impliedClasses}` : impliedClasses;
700
+ resolved = (0, import_core6.resolve)(expandedClassStr, config.theme, config.darkMode);
701
+ }
702
+ const { style: userStyle, ...passProps } = omitConsumed(workingProps);
703
703
  const { interactive, modeOrResponsive } = bucketMods(resolved);
704
704
  if (interactive) {
705
705
  return (0, import_jsx_runtime.jsx)(InteractiveWrapper, {
@@ -725,7 +725,7 @@ function processElement(type, rawProps, key, isStaticChildren) {
725
725
  const computedStyle = (0, import_core6.flatten)(resolved, false);
726
726
  stripInternalMarkers(computedStyle);
727
727
  if (typeof effectiveType !== "string") stripWebOnlyProps(computedStyle);
728
- finalStyle = mergeStyle(computedStyle, userStyle);
728
+ finalStyle = composeNativeStyle(computedStyle, userStyle);
729
729
  } else {
730
730
  finalStyle = userStyle ?? void 0;
731
731
  }
@@ -4,10 +4,10 @@ import {
4
4
  _invalidateDefaultFontCache,
5
5
  jsx,
6
6
  jsxs
7
- } from "./chunk-Z4ZOGRZG.mjs";
7
+ } from "./chunk-LM3OWOK2.mjs";
8
8
  import {
9
9
  registerWebElement
10
- } from "./chunk-NE4LAJXK.mjs";
10
+ } from "./chunk-PTOTQ6A2.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 };