@kbach/react 0.5.47 → 0.5.48

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
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  normalizeClassString: () => normalizeClassString,
47
47
  parseClass: () => parseClass,
48
48
  parseClasses: () => parseClasses,
49
+ registerWebElement: () => registerWebElement,
49
50
  resolve: () => resolve,
50
51
  splitClassTokens: () => splitClassTokens,
51
52
  styled: () => styled,
@@ -835,6 +836,12 @@ function isKnownModifier(name) {
835
836
  function _allEntries() {
836
837
  return [...Object.entries(BUILTIN_MODIFIERS), ...Object.entries(_pluginModifiers)];
837
838
  }
839
+ function getInteractiveModifiers() {
840
+ if (!_interactiveNames) {
841
+ _interactiveNames = new Set(_allEntries().filter(([, d]) => d.jsBehavior === "interactive").map(([k]) => k));
842
+ }
843
+ return _interactiveNames;
844
+ }
838
845
  function getResponsiveModifiers() {
839
846
  if (!_responsiveNames) {
840
847
  _responsiveNames = new Set(_allEntries().filter(([, d]) => d.jsBehavior === "responsive").map(([k]) => k));
@@ -887,7 +894,12 @@ function resolveColor(value, colors, isArbitrary) {
887
894
  }
888
895
  }
889
896
  if (!hex) return null;
890
- if (!opacityPart) return hex;
897
+ if (!opacityPart) {
898
+ if (!isWeb && hex.startsWith("#") && hex.length === 9) {
899
+ return hexToRgba(hex, Math.round(parseInt(hex.slice(7, 9), 16) / 255 * 1e3) / 1e3);
900
+ }
901
+ return hex;
902
+ }
891
903
  let alpha;
892
904
  if (opacityPart.startsWith("[") && opacityPart.endsWith("]")) {
893
905
  const v = parseFloat(opacityPart.slice(1, -1));
@@ -2450,12 +2462,6 @@ var RESOLVERS = {
2450
2462
  function resolveUtility(parsed, theme) {
2451
2463
  if (!parsed.value) {
2452
2464
  if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
2453
- if (parsed.utility === "grd" || parsed.utility === "grid") {
2454
- return getEffectiveIsWeb() ? { display: "grid" } : null;
2455
- }
2456
- if (parsed.utility === "inline-grid") {
2457
- return getEffectiveIsWeb() ? { display: "inline-grid" } : null;
2458
- }
2459
2465
  if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
2460
2466
  }
2461
2467
  const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
@@ -3062,6 +3068,8 @@ function buildConfig(userConfig) {
3062
3068
  };
3063
3069
  clearPluginUtilities();
3064
3070
  clearPluginModifiers();
3071
+ const _store = getConfigStore();
3072
+ for (const k of Object.keys(_store.customVariants)) delete _store.customVariants[k];
3065
3073
  const pluginAPI = makePluginAPI(resolved.theme);
3066
3074
  for (const plugin of resolved.plugins) {
3067
3075
  plugin(pluginAPI);
@@ -3217,16 +3225,23 @@ function ThemeProvider({
3217
3225
  () => isWeb && typeof window !== "undefined" ? window.innerWidth : 0
3218
3226
  );
3219
3227
  const effectiveWidth = windowWidthProp ?? (isWeb ? webWidth : 0);
3220
- const numericScreens = {};
3221
- for (const [k, v] of Object.entries(resolvedConfig.theme.screens ?? {})) {
3222
- numericScreens[k] = typeof v === "number" ? v : parseInt(String(v), 10);
3223
- }
3228
+ const numericScreens = (0, import_react3.useMemo)(() => {
3229
+ const out = {};
3230
+ for (const [k, v] of Object.entries(resolvedConfig.theme.screens ?? {})) {
3231
+ out[k] = typeof v === "number" ? v : parseInt(String(v), 10);
3232
+ }
3233
+ return out;
3234
+ }, [resolvedConfig.theme.screens]);
3224
3235
  const _prevWidthRef = (0, import_react3.useRef)(-1);
3225
3236
  if (_prevWidthRef.current !== effectiveWidth) {
3226
3237
  _prevWidthRef.current = effectiveWidth;
3227
3238
  syncGlobalWidth(effectiveWidth);
3228
3239
  }
3229
- syncGlobalScreens(numericScreens);
3240
+ const _prevScreensRef = (0, import_react3.useRef)(null);
3241
+ if (_prevScreensRef.current !== numericScreens) {
3242
+ _prevScreensRef.current = numericScreens;
3243
+ syncGlobalScreens(numericScreens);
3244
+ }
3230
3245
  (0, import_react3.useEffect)(() => {
3231
3246
  setGlobalWidth(effectiveWidth);
3232
3247
  }, [effectiveWidth]);
@@ -3511,6 +3526,17 @@ function hasResponsiveBuckets(resolved) {
3511
3526
  }
3512
3527
  return false;
3513
3528
  }
3529
+ function hasInteractiveBuckets(resolved) {
3530
+ const interactiveMods = getInteractiveModifiers();
3531
+ for (const key of Object.keys(resolved)) {
3532
+ if (key === "base") continue;
3533
+ for (const mod of key.split(":")) {
3534
+ if (interactiveMods.has(mod)) return true;
3535
+ }
3536
+ }
3537
+ return false;
3538
+ }
3539
+ var EMPTY_BREAKPOINTS2 = /* @__PURE__ */ new Set();
3514
3540
 
3515
3541
  // src/styled.tsx
3516
3542
  function styled(Component, baseClasses = "") {
@@ -3530,6 +3556,8 @@ function styled(Component, baseClasses = "") {
3530
3556
  onBlur,
3531
3557
  ...rest
3532
3558
  } = props;
3559
+ const disabled = !!props.disabled;
3560
+ const checked = !!props.checked;
3533
3561
  const { isDark, config } = useTheme();
3534
3562
  const [pressed, setPressed] = (0, import_react6.useState)(false);
3535
3563
  const [hovered, setHovered] = (0, import_react6.useState)(false);
@@ -3572,28 +3600,30 @@ function styled(Component, baseClasses = "") {
3572
3600
  }, [onBlur]);
3573
3601
  const combined = extraClasses ? `${baseClasses} ${extraClasses}` : baseClasses;
3574
3602
  const resolved = (0, import_react6.useMemo)(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
3603
+ const hasInteractive = (0, import_react6.useMemo)(() => hasInteractiveBuckets(resolved), [resolved]);
3575
3604
  const needsWidth = hasResponsiveBuckets(resolved);
3576
3605
  const width = useConditionalWidth(needsWidth);
3577
3606
  const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
3578
3607
  const computedStyle = (0, import_react6.useMemo)(
3579
- () => flatten(resolved, isDark, { pressed, hover: hovered, focus: focused }, breakpoints),
3580
- [resolved, isDark, pressed, hovered, focused, width]
3608
+ () => flatten(resolved, isDark, { pressed, hover: hovered, focus: focused, disabled, checked }, breakpoints),
3609
+ [resolved, isDark, pressed, hovered, focused, disabled, checked, width]
3581
3610
  // eslint-disable-line react-hooks/exhaustive-deps
3582
3611
  );
3583
3612
  const extraStyle = Array.isArray(styleProp) ? Object.assign({}, ...styleProp) : styleProp;
3584
- const finalStyle = extraStyle ? { ...computedStyle, ...extraStyle } : computedStyle;
3613
+ const finalStyle = isWeb ? extraStyle ?? void 0 : extraStyle ? { ...computedStyle, ...extraStyle } : computedStyle;
3585
3614
  return import_react6.default.createElement(Component, {
3586
3615
  ref,
3587
3616
  ...rest,
3588
3617
  style: finalStyle,
3589
3618
  // className lets injected CSS rules (group-hover:, before:, print:, etc.) match the element.
3590
3619
  ...isWeb && combined ? { className: normalizeClassString(combined) } : {},
3591
- // onPressIn / onPressOut are React Native-only; on web use Pointer Events instead.
3592
- ...isWeb ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3593
- onMouseEnter: handleMouseEnter,
3594
- onMouseLeave: handleMouseLeave,
3595
- onFocus: handleFocus,
3596
- onBlur: handleBlur
3620
+ // Only attach state-tracking handlers when interactive modifiers are present.
3621
+ // Always forward user-provided handlers to avoid silently swallowing them.
3622
+ ...isWeb ? hasInteractive ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave } : { onPointerDown, onPointerUp, onPointerLeave } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
3623
+ onMouseEnter: hasInteractive ? handleMouseEnter : onMouseEnter,
3624
+ onMouseLeave: hasInteractive ? handleMouseLeave : onMouseLeave,
3625
+ onFocus: hasInteractive ? handleFocus : onFocus,
3626
+ onBlur: hasInteractive ? handleBlur : onBlur
3597
3627
  });
3598
3628
  }
3599
3629
  );
@@ -3710,7 +3740,7 @@ var InteractiveWrapper = (0, import_react10.forwardRef)(
3710
3740
  const handleBlur = (0, import_react10.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
3711
3741
  const computedStyle = (0, import_react10.useMemo)(
3712
3742
  () => {
3713
- const s = flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused }, breakpoints);
3743
+ const s = flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused, disabled: !!rest.disabled, checked: !!rest.checked }, breakpoints);
3714
3744
  delete s.__divideX;
3715
3745
  delete s.__divideY;
3716
3746
  delete s.__divideColor;
@@ -3744,8 +3774,8 @@ var InteractiveWrapper = (0, import_react10.forwardRef)(
3744
3774
  [resolvedStyle, isDark, pressed, hovered, focused, width]
3745
3775
  // eslint-disable-line react-hooks/exhaustive-deps
3746
3776
  );
3747
- const isWebHTMLElement = !isNative && typeof Component === "string";
3748
- const finalStyle = isWebHTMLElement ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
3777
+ const skipComputedInline = isWeb || !isNative && typeof Component === "string";
3778
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
3749
3779
  const { children, ...restWithoutChildren } = rest;
3750
3780
  const componentProps = {
3751
3781
  ref,
@@ -3777,6 +3807,72 @@ function kb(classString, isDark = false) {
3777
3807
  function cx(...classes) {
3778
3808
  return classes.filter(Boolean).join(" ");
3779
3809
  }
3810
+
3811
+ // src/jsx-runtime.tsx
3812
+ var import_jsx_runtime2 = require("react/jsx-runtime");
3813
+ var import_react12 = require("react");
3814
+
3815
+ // src/DarkWrapper.tsx
3816
+ var import_react11 = __toESM(require("react"));
3817
+ var DarkWrapper = import_react11.default.forwardRef(
3818
+ function DarkWrapper2({ Component, resolvedStyle, style: styleProp, children, ...rest }, ref) {
3819
+ const isDark = useGlobalDarkMode();
3820
+ const needsWidth = hasResponsiveBuckets(resolvedStyle);
3821
+ const width = useConditionalWidth(needsWidth);
3822
+ const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS2;
3823
+ const isNonStringComponent = typeof Component !== "string";
3824
+ const computedStyle = (0, import_react11.useMemo)(() => {
3825
+ const s = flatten(resolvedStyle, isDark, {}, breakpoints);
3826
+ stripInternalMarkers(s);
3827
+ if (isNonStringComponent) stripWebOnlyProps(s);
3828
+ return s;
3829
+ }, [resolvedStyle, isDark, width, isNonStringComponent]);
3830
+ const skipComputedInline = isWeb || !isNative && typeof Component === "string";
3831
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
3832
+ const props = { ref, ...rest, style: finalStyle };
3833
+ return Array.isArray(children) ? import_react11.default.createElement(Component, props, ...children) : import_react11.default.createElement(Component, props, children);
3834
+ }
3835
+ );
3836
+ DarkWrapper.displayName = "Kbach.DarkWrapper";
3837
+ function stripInternalMarkers(s) {
3838
+ delete s.__divideX;
3839
+ delete s.__divideY;
3840
+ delete s.__divideColor;
3841
+ delete s.__divideStyle;
3842
+ delete s.__keyframe;
3843
+ }
3844
+ function stripWebOnlyProps(s) {
3845
+ if (s.display === "grid" || s.display === "inline-grid") delete s.display;
3846
+ delete s.gridTemplateColumns;
3847
+ delete s.gridTemplateRows;
3848
+ delete s.gridColumn;
3849
+ delete s.gridRow;
3850
+ delete s.gridArea;
3851
+ delete s.gridColumnStart;
3852
+ delete s.gridColumnEnd;
3853
+ delete s.gridRowStart;
3854
+ delete s.gridRowEnd;
3855
+ delete s.gridAutoFlow;
3856
+ delete s.gridAutoColumns;
3857
+ delete s.gridAutoRows;
3858
+ delete s.placeItems;
3859
+ delete s.placeContent;
3860
+ delete s.justifyItems;
3861
+ delete s.placeSelf;
3862
+ delete s.justifySelf;
3863
+ if (s.position === "sticky" || s.position === "fixed" || s.position === "static") {
3864
+ delete s.position;
3865
+ }
3866
+ }
3867
+
3868
+ // src/jsx-runtime.tsx
3869
+ var _userWebTypeMap = /* @__PURE__ */ new Map();
3870
+ var _webTypeCache = /* @__PURE__ */ new WeakMap();
3871
+ function registerWebElement(rnComponent, htmlTag) {
3872
+ if (typeof rnComponent !== "object" && typeof rnComponent !== "function") return;
3873
+ _userWebTypeMap.set(rnComponent, htmlTag);
3874
+ _webTypeCache.delete(rnComponent);
3875
+ }
3780
3876
  // Annotate the CommonJS export names for ESM import in node:
3781
3877
  0 && (module.exports = {
3782
3878
  InteractiveWrapper,
@@ -3795,6 +3891,7 @@ function cx(...classes) {
3795
3891
  normalizeClassString,
3796
3892
  parseClass,
3797
3893
  parseClasses,
3894
+ registerWebElement,
3798
3895
  resolve,
3799
3896
  splitClassTokens,
3800
3897
  styled,
package/dist/index.mjs CHANGED
@@ -1,13 +1,15 @@
1
1
  import {
2
2
  EMPTY_BREAKPOINTS,
3
3
  InteractiveWrapper,
4
+ hasInteractiveBuckets,
4
5
  hasResponsiveBuckets,
6
+ registerWebElement,
5
7
  setGlobalDarkMode,
6
8
  syncGlobalDarkMode,
7
9
  useConditionalWidth,
8
10
  useGlobalDarkMode,
9
11
  useGlobalWidth
10
- } from "./chunk-U3GY5Y6J.mjs";
12
+ } from "./chunk-YZNSWFJT.mjs";
11
13
  import {
12
14
  __require,
13
15
  buildConfig,
@@ -29,7 +31,7 @@ import {
29
31
  syncGlobalScreens,
30
32
  syncGlobalWidth,
31
33
  updateConfig
32
- } from "./chunk-NIBECTRQ.mjs";
34
+ } from "./chunk-BKSXIWL7.mjs";
33
35
 
34
36
  // src/context.tsx
35
37
  import { createContext, useContext } from "react";
@@ -166,16 +168,23 @@ function ThemeProvider({
166
168
  () => isWeb && typeof window !== "undefined" ? window.innerWidth : 0
167
169
  );
168
170
  const effectiveWidth = windowWidthProp ?? (isWeb ? webWidth : 0);
169
- const numericScreens = {};
170
- for (const [k, v] of Object.entries(resolvedConfig.theme.screens ?? {})) {
171
- numericScreens[k] = typeof v === "number" ? v : parseInt(String(v), 10);
172
- }
171
+ const numericScreens = useMemo2(() => {
172
+ const out = {};
173
+ for (const [k, v] of Object.entries(resolvedConfig.theme.screens ?? {})) {
174
+ out[k] = typeof v === "number" ? v : parseInt(String(v), 10);
175
+ }
176
+ return out;
177
+ }, [resolvedConfig.theme.screens]);
173
178
  const _prevWidthRef = useRef(-1);
174
179
  if (_prevWidthRef.current !== effectiveWidth) {
175
180
  _prevWidthRef.current = effectiveWidth;
176
181
  syncGlobalWidth(effectiveWidth);
177
182
  }
178
- syncGlobalScreens(numericScreens);
183
+ const _prevScreensRef = useRef(null);
184
+ if (_prevScreensRef.current !== numericScreens) {
185
+ _prevScreensRef.current = numericScreens;
186
+ syncGlobalScreens(numericScreens);
187
+ }
179
188
  useEffect(() => {
180
189
  setGlobalWidth(effectiveWidth);
181
190
  }, [effectiveWidth]);
@@ -416,6 +425,8 @@ function styled(Component, baseClasses = "") {
416
425
  onBlur,
417
426
  ...rest
418
427
  } = props;
428
+ const disabled = !!props.disabled;
429
+ const checked = !!props.checked;
419
430
  const { isDark, config } = useTheme();
420
431
  const [pressed, setPressed] = useState2(false);
421
432
  const [hovered, setHovered] = useState2(false);
@@ -458,28 +469,30 @@ function styled(Component, baseClasses = "") {
458
469
  }, [onBlur]);
459
470
  const combined = extraClasses ? `${baseClasses} ${extraClasses}` : baseClasses;
460
471
  const resolved = useMemo3(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
472
+ const hasInteractive = useMemo3(() => hasInteractiveBuckets(resolved), [resolved]);
461
473
  const needsWidth = hasResponsiveBuckets(resolved);
462
474
  const width = useConditionalWidth(needsWidth);
463
475
  const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
464
476
  const computedStyle = useMemo3(
465
- () => flatten(resolved, isDark, { pressed, hover: hovered, focus: focused }, breakpoints),
466
- [resolved, isDark, pressed, hovered, focused, width]
477
+ () => flatten(resolved, isDark, { pressed, hover: hovered, focus: focused, disabled, checked }, breakpoints),
478
+ [resolved, isDark, pressed, hovered, focused, disabled, checked, width]
467
479
  // eslint-disable-line react-hooks/exhaustive-deps
468
480
  );
469
481
  const extraStyle = Array.isArray(styleProp) ? Object.assign({}, ...styleProp) : styleProp;
470
- const finalStyle = extraStyle ? { ...computedStyle, ...extraStyle } : computedStyle;
482
+ const finalStyle = isWeb ? extraStyle ?? void 0 : extraStyle ? { ...computedStyle, ...extraStyle } : computedStyle;
471
483
  return React3.createElement(Component, {
472
484
  ref,
473
485
  ...rest,
474
486
  style: finalStyle,
475
487
  // className lets injected CSS rules (group-hover:, before:, print:, etc.) match the element.
476
488
  ...isWeb && combined ? { className: normalizeClassString(combined) } : {},
477
- // onPressIn / onPressOut are React Native-only; on web use Pointer Events instead.
478
- ...isWeb ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
479
- onMouseEnter: handleMouseEnter,
480
- onMouseLeave: handleMouseLeave,
481
- onFocus: handleFocus,
482
- onBlur: handleBlur
489
+ // Only attach state-tracking handlers when interactive modifiers are present.
490
+ // Always forward user-provided handlers to avoid silently swallowing them.
491
+ ...isWeb ? hasInteractive ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave } : { onPointerDown, onPointerUp, onPointerLeave } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
492
+ onMouseEnter: hasInteractive ? handleMouseEnter : onMouseEnter,
493
+ onMouseLeave: hasInteractive ? handleMouseLeave : onMouseLeave,
494
+ onFocus: hasInteractive ? handleFocus : onFocus,
495
+ onBlur: hasInteractive ? handleBlur : onBlur
483
496
  });
484
497
  }
485
498
  );
@@ -566,6 +579,7 @@ export {
566
579
  normalizeClassString,
567
580
  parseClass,
568
581
  parseClasses,
582
+ registerWebElement,
569
583
  resolve,
570
584
  splitClassTokens,
571
585
  styled,
@@ -787,7 +787,12 @@ function resolveColor(value, colors, isArbitrary) {
787
787
  }
788
788
  }
789
789
  if (!hex) return null;
790
- if (!opacityPart) return hex;
790
+ if (!opacityPart) {
791
+ if (!isWeb && hex.startsWith("#") && hex.length === 9) {
792
+ return hexToRgba(hex, Math.round(parseInt(hex.slice(7, 9), 16) / 255 * 1e3) / 1e3);
793
+ }
794
+ return hex;
795
+ }
791
796
  let alpha;
792
797
  if (opacityPart.startsWith("[") && opacityPart.endsWith("]")) {
793
798
  const v = parseFloat(opacityPart.slice(1, -1));
@@ -2350,12 +2355,6 @@ var RESOLVERS = {
2350
2355
  function resolveUtility(parsed, theme) {
2351
2356
  if (!parsed.value) {
2352
2357
  if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
2353
- if (parsed.utility === "grd" || parsed.utility === "grid") {
2354
- return getEffectiveIsWeb() ? { display: "grid" } : null;
2355
- }
2356
- if (parsed.utility === "inline-grid") {
2357
- return getEffectiveIsWeb() ? { display: "inline-grid" } : null;
2358
- }
2359
2358
  if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
2360
2359
  }
2361
2360
  const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
@@ -2940,6 +2939,8 @@ function buildConfig(userConfig) {
2940
2939
  };
2941
2940
  clearPluginUtilities();
2942
2941
  clearPluginModifiers();
2942
+ const _store = getConfigStore();
2943
+ for (const k of Object.keys(_store.customVariants)) delete _store.customVariants[k];
2943
2944
  const pluginAPI = makePluginAPI(resolved.theme);
2944
2945
  for (const plugin of resolved.plugins) {
2945
2946
  plugin(pluginAPI);
@@ -3146,7 +3147,7 @@ var InteractiveWrapper = (0, import_react3.forwardRef)(
3146
3147
  const handleBlur = (0, import_react3.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
3147
3148
  const computedStyle = (0, import_react3.useMemo)(
3148
3149
  () => {
3149
- const s = flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused }, breakpoints);
3150
+ const s = flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused, disabled: !!rest.disabled, checked: !!rest.checked }, breakpoints);
3150
3151
  delete s.__divideX;
3151
3152
  delete s.__divideY;
3152
3153
  delete s.__divideColor;
@@ -3180,8 +3181,8 @@ var InteractiveWrapper = (0, import_react3.forwardRef)(
3180
3181
  [resolvedStyle, isDark, pressed, hovered, focused, width]
3181
3182
  // eslint-disable-line react-hooks/exhaustive-deps
3182
3183
  );
3183
- const isWebHTMLElement = !isNative && typeof Component === "string";
3184
- const finalStyle = isWebHTMLElement ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
3184
+ const skipComputedInline = isWeb || !isNative && typeof Component === "string";
3185
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
3185
3186
  const { children, ...restWithoutChildren } = rest;
3186
3187
  const componentProps = {
3187
3188
  ref,
@@ -3209,11 +3210,15 @@ var DarkWrapper = import_react4.default.forwardRef(
3209
3210
  const needsWidth = hasResponsiveBuckets(resolvedStyle);
3210
3211
  const width = useConditionalWidth(needsWidth);
3211
3212
  const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS2;
3212
- const computedStyle = flatten(resolvedStyle, isDark, {}, breakpoints);
3213
- stripInternalMarkers(computedStyle);
3214
- if (typeof Component !== "string") stripWebOnlyProps(computedStyle);
3215
- const isWebHTMLElement = !isNative && typeof Component === "string";
3216
- const finalStyle = isWebHTMLElement ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
3213
+ const isNonStringComponent = typeof Component !== "string";
3214
+ const computedStyle = (0, import_react4.useMemo)(() => {
3215
+ const s = flatten(resolvedStyle, isDark, {}, breakpoints);
3216
+ stripInternalMarkers(s);
3217
+ if (isNonStringComponent) stripWebOnlyProps(s);
3218
+ return s;
3219
+ }, [resolvedStyle, isDark, width, isNonStringComponent]);
3220
+ const skipComputedInline = isWeb || !isNative && typeof Component === "string";
3221
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
3217
3222
  const props = { ref, ...rest, style: finalStyle };
3218
3223
  return Array.isArray(children) ? import_react4.default.createElement(Component, props, ...children) : import_react4.default.createElement(Component, props, children);
3219
3224
  }
@@ -3328,14 +3333,96 @@ function stripWebOnlyInlineProps(s) {
3328
3333
  delete s.position;
3329
3334
  }
3330
3335
  }
3336
+ var _rnToHtml = {
3337
+ View: "div",
3338
+ SafeAreaView: "div",
3339
+ KeyboardAvoidingView: "div",
3340
+ ScrollView: "div",
3341
+ VirtualizedList: "div",
3342
+ Text: "span",
3343
+ Pressable: "div",
3344
+ TouchableOpacity: "div",
3345
+ TouchableHighlight: "div",
3346
+ TouchableWithoutFeedback: "div",
3347
+ TouchableNativeFeedback: "div"
3348
+ };
3349
+ var _userWebTypeMap = /* @__PURE__ */ new Map();
3350
+ var _webTypeCache = /* @__PURE__ */ new WeakMap();
3351
+ function getWebTag(type) {
3352
+ if (typeof type === "string") return null;
3353
+ if (type === null || typeof type !== "function" && typeof type !== "object") return null;
3354
+ const obj = type;
3355
+ if (_userWebTypeMap.has(obj)) return _userWebTypeMap.get(obj);
3356
+ if (_webTypeCache.has(obj)) return _webTypeCache.get(obj);
3357
+ const name = obj.displayName ?? obj.name;
3358
+ const tag = name ? _rnToHtml[name] ?? null : null;
3359
+ _webTypeCache.set(obj, tag);
3360
+ return tag;
3361
+ }
3362
+ var _rnOnlyProps = /* @__PURE__ */ new Set([
3363
+ "onLongPress",
3364
+ "delayLongPress",
3365
+ "activeOpacity",
3366
+ "underlayColor",
3367
+ "hitSlop",
3368
+ "pressRetentionOffset",
3369
+ "accessible",
3370
+ "accessibilityState",
3371
+ "accessibilityLiveRegion",
3372
+ "importantForAccessibility",
3373
+ "nativeID",
3374
+ "collapsable",
3375
+ "needsOffscreenAlphaCompositing",
3376
+ "renderToHardwareTextureAndroid",
3377
+ "shouldRasterizeIOS",
3378
+ "pointerEvents"
3379
+ // RN prop — values differ from HTML pointer-events CSS
3380
+ ]);
3381
+ var _pressableDisplayNames = /* @__PURE__ */ new Set([
3382
+ "Pressable",
3383
+ "TouchableOpacity",
3384
+ "TouchableHighlight",
3385
+ "TouchableWithoutFeedback",
3386
+ "TouchableNativeFeedback"
3387
+ ]);
3388
+ function transformToWebProps(originalName, props) {
3389
+ const out = {};
3390
+ const isPressable = _pressableDisplayNames.has(originalName);
3391
+ for (const [k, v] of Object.entries(props)) {
3392
+ if (_rnOnlyProps.has(k)) continue;
3393
+ if (k === "onPress") {
3394
+ if (!("onClick" in props)) out.onClick = v;
3395
+ continue;
3396
+ }
3397
+ if (k === "accessibilityLabel") {
3398
+ if (out["aria-label"] == null) out["aria-label"] = v;
3399
+ continue;
3400
+ }
3401
+ if (k === "accessibilityRole") {
3402
+ if (out.role == null) out.role = v;
3403
+ continue;
3404
+ }
3405
+ if (k === "testID") {
3406
+ if (out["data-testid"] == null) out["data-testid"] = v;
3407
+ continue;
3408
+ }
3409
+ out[k] = v;
3410
+ }
3411
+ if (isPressable && !out.role) out.role = "button";
3412
+ return out;
3413
+ }
3331
3414
  function processElement(type, rawProps, key, isStaticChildren) {
3332
3415
  if (typeof type === "symbol") return makeElement(isStaticChildren, type, rawProps ?? {}, key);
3333
- if (!rawProps) return (0, import_jsx_runtime.jsx)(type, null, key);
3334
- const { className, kb: kbProp, __kbachStyles, __kbachClasses } = rawProps;
3416
+ const webTag = isWeb ? getWebTag(type) : null;
3417
+ const effectiveType = webTag ?? type;
3418
+ const originalName = webTag ? type?.displayName ?? type?.name : void 0;
3419
+ if (!rawProps) return (0, import_jsx_runtime.jsx)(effectiveType, null, key);
3420
+ const workingProps = webTag && originalName ? transformToWebProps(originalName, rawProps) : rawProps;
3421
+ const { className, kb: kbProp, __kbachStyles, __kbachClasses } = workingProps;
3335
3422
  const classStr = className ?? kbProp ?? __kbachClasses;
3336
3423
  if (!isNative && isRuntimeCSSDisabled() && classStr && !__kbachStyles) {
3337
- const { style: userStyle2, ...passProps2 } = omitConsumed(rawProps);
3338
- return makeElement(isStaticChildren, type, {
3424
+ const { style: userStyle2, ...passProps2 } = omitConsumed(workingProps);
3425
+ return makeElement(isStaticChildren, effectiveType, {
3339
3426
  ...passProps2,
3340
3427
  className: normalizeClassString(classStr),
3341
3428
  ...userStyle2 !== void 0 ? { style: userStyle2 } : {}
@@ -3343,18 +3430,18 @@ function processElement(type, rawProps, key, isStaticChildren) {
3343
3430
  }
3344
3431
  if (!classStr && !__kbachStyles) {
3345
3432
  const defaultFont = getCachedDefaultFont();
3346
- if (!defaultFont) return makeElement(isStaticChildren, type, rawProps, key);
3347
- const { style: userStyle2, ...passProps2 } = omitConsumed(rawProps);
3433
+ if (!defaultFont) return makeElement(isStaticChildren, effectiveType, workingProps, key);
3434
+ const { style: userStyle2, ...passProps2 } = omitConsumed(workingProps);
3348
3435
  const finalStyle2 = userStyle2 ? Array.isArray(userStyle2) ? { fontFamily: defaultFont, ...Object.assign({}, ...userStyle2) } : { fontFamily: defaultFont, ...userStyle2 } : { fontFamily: defaultFont };
3349
- return makeElement(isStaticChildren, type, { ...passProps2, style: finalStyle2 }, key);
3436
+ return makeElement(isStaticChildren, effectiveType, { ...passProps2, style: finalStyle2 }, key);
3350
3437
  }
3351
3438
  const config = getConfig();
3352
3439
  const resolved = __kbachStyles ?? (classStr ? resolve(classStr, config.theme, config.darkMode) : {});
3353
- const { style: userStyle, ...passProps } = omitConsumed(rawProps);
3440
+ const { style: userStyle, ...passProps } = omitConsumed(workingProps);
3354
3441
  const { interactive, modeOrResponsive } = bucketMods(resolved);
3355
3442
  if (interactive) {
3356
3443
  return (0, import_jsx_runtime.jsx)(InteractiveWrapper, {
3357
- Component: type,
3444
+ Component: effectiveType,
3358
3445
  resolvedStyle: resolved,
3359
3446
  ...!isNative && classStr ? { className: normalizeClassString(classStr) } : {},
3360
3447
  style: userStyle,
@@ -3363,24 +3450,24 @@ function processElement(type, rawProps, key, isStaticChildren) {
3363
3450
  }
3364
3451
  if (modeOrResponsive) {
3365
3452
  return (0, import_jsx_runtime.jsx)(DarkWrapper, {
3366
- Component: type,
3453
+ Component: effectiveType,
3367
3454
  resolvedStyle: resolved,
3368
3455
  ...!isNative && classStr ? { className: normalizeClassString(classStr) } : {},
3369
3456
  style: userStyle,
3370
3457
  ...passProps
3371
3458
  }, key);
3372
3459
  }
3373
- const isWebHTMLElement = !isNative && typeof type === "string";
3460
+ const skipComputedInline = isWeb || !isNative && typeof effectiveType === "string";
3374
3461
  let finalStyle;
3375
- if (!isWebHTMLElement) {
3462
+ if (!skipComputedInline) {
3376
3463
  const computedStyle = flatten(resolved, false);
3377
3464
  stripInternalMarkers2(computedStyle);
3378
- if (typeof type !== "string") stripWebOnlyInlineProps(computedStyle);
3465
+ if (typeof effectiveType !== "string") stripWebOnlyInlineProps(computedStyle);
3379
3466
  finalStyle = mergeStyle(computedStyle, userStyle);
3380
3467
  } else {
3381
3468
  finalStyle = userStyle ?? void 0;
3382
3469
  }
3383
- return makeElement(isStaticChildren, type, {
3470
+ return makeElement(isStaticChildren, effectiveType, {
3384
3471
  ...passProps,
3385
3472
  style: finalStyle,
3386
3473
  ...!isNative && classStr ? { className: normalizeClassString(classStr) } : {}
@@ -2,9 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-LCK5OGHT.mjs";
6
- import "./chunk-U3GY5Y6J.mjs";
7
- import "./chunk-NIBECTRQ.mjs";
5
+ } from "./chunk-YZNSWFJT.mjs";
6
+ import "./chunk-BKSXIWL7.mjs";
8
7
 
9
8
  // src/jsx-dev-runtime.tsx
10
9
  function jsxDEV(type, props, key, isStaticChildren, source, self) {
@@ -10,7 +10,18 @@ export { Fragment, JSX } from 'react';
10
10
  */
11
11
 
12
12
  declare function _invalidateDefaultFontCache(): void;
13
+ /**
14
+ * Register a custom React Native component → HTML tag substitution.
15
+ * Call this once at app startup (before first render).
16
+ *
17
+ * ```ts
18
+ * import { Animated } from 'react-native';
19
+ * import { registerWebElement } from '@kbach/react/jsx-runtime';
20
+ * registerWebElement(Animated.View, 'div');
21
+ * ```
22
+ */
23
+ declare function registerWebElement(rnComponent: unknown, htmlTag: string): void;
13
24
  declare function jsx(type: unknown, props: Record<string, unknown> | null, key?: string): ReactElement;
14
25
  declare function jsxs(type: unknown, props: Record<string, unknown> | null, key?: string): ReactElement;
15
26
 
16
- export { _invalidateDefaultFontCache, jsx, jsxs };
27
+ export { _invalidateDefaultFontCache, jsx, jsxs, registerWebElement };
@@ -10,7 +10,18 @@ export { Fragment, JSX } from 'react';
10
10
  */
11
11
 
12
12
  declare function _invalidateDefaultFontCache(): void;
13
+ /**
14
+ * Register a custom React Native component → HTML tag substitution.
15
+ * Call this once at app startup (before first render).
16
+ *
17
+ * ```ts
18
+ * import { Animated } from 'react-native';
19
+ * import { registerWebElement } from '@kbach/react/jsx-runtime';
20
+ * registerWebElement(Animated.View, 'div');
21
+ * ```
22
+ */
23
+ declare function registerWebElement(rnComponent: unknown, htmlTag: string): void;
13
24
  declare function jsx(type: unknown, props: Record<string, unknown> | null, key?: string): ReactElement;
14
25
  declare function jsxs(type: unknown, props: Record<string, unknown> | null, key?: string): ReactElement;
15
26
 
16
- export { _invalidateDefaultFontCache, jsx, jsxs };
27
+ export { _invalidateDefaultFontCache, jsx, jsxs, registerWebElement };