@kbach/react 0.5.60 → 0.6.0

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
@@ -11,9 +11,8 @@ import {
11
11
  useConditionalWidth,
12
12
  useGlobalDarkMode,
13
13
  useGlobalWidth
14
- } from "./chunk-2MVCYYH5.mjs";
14
+ } from "./chunk-D4LJICWZ.mjs";
15
15
  import {
16
- __require,
17
16
  buildConfig,
18
17
  defaultColors,
19
18
  defaultTheme,
@@ -33,7 +32,7 @@ import {
33
32
  syncGlobalScreens,
34
33
  syncGlobalWidth,
35
34
  updateConfig
36
- } from "./chunk-NP4FVE77.mjs";
35
+ } from "./chunk-6NED62DB.mjs";
37
36
 
38
37
  // src/context.tsx
39
38
  import { createContext, useContext } from "react";
@@ -270,150 +269,11 @@ function ThemeProvider({
270
269
  return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: contextValue, children });
271
270
  }
272
271
 
273
- // src/ThemeToggle.tsx
274
- import React2 from "react";
275
- function getPrimitives() {
276
- try {
277
- const RN = __require("react-native");
278
- return {
279
- View: RN.View,
280
- Text: RN.Text,
281
- Touchable: RN.TouchableOpacity,
282
- Switch: RN.Switch
283
- };
284
- } catch {
285
- const View = ({ style, ...rest }) => React2.createElement("div", { style, ...rest });
286
- const Text = ({ style, ...rest }) => React2.createElement("span", { style, ...rest });
287
- const Touchable = ({ onPress, style, ...rest }) => React2.createElement("button", { onClick: onPress, style, ...rest });
288
- const Switch = ({
289
- value,
290
- onValueChange,
291
- accessibilityLabel
292
- }) => React2.createElement("input", {
293
- type: "checkbox",
294
- checked: value,
295
- onChange: (e) => onValueChange?.(e.target.checked),
296
- "aria-label": accessibilityLabel
297
- });
298
- return { View, Text, Touchable, Switch };
299
- }
300
- }
301
- var { View: _View, Text: _Text, Touchable: _Touchable, Switch: _Switch } = getPrimitives();
302
- function ThemeToggle({
303
- variant = "button",
304
- label,
305
- lightLabel = "Light",
306
- darkLabel = "Dark",
307
- includeSystem = false,
308
- style,
309
- labelStyle
310
- }) {
311
- const { mode, isDark, setMode, toggle } = useTheme();
312
- const View = _View, Text = _Text, Touchable = _Touchable, Switch = _Switch;
313
- if (variant === "switch") {
314
- return React2.createElement(
315
- View,
316
- { style: { flexDirection: "row", alignItems: "center", gap: 8, ...style } },
317
- React2.createElement(Text, { style: { fontSize: 14, ...labelStyle } }, lightLabel),
318
- React2.createElement(Switch, {
319
- value: isDark,
320
- onValueChange: (v) => setMode(v ? "dark" : "light"),
321
- trackColor: { false: "#d1d5db", true: "#6366f1" },
322
- thumbColor: isDark ? "#ffffff" : "#f3f4f6",
323
- ios_backgroundColor: "#d1d5db",
324
- accessibilityLabel: "Toggle dark mode"
325
- }),
326
- React2.createElement(Text, { style: { fontSize: 14, ...labelStyle } }, darkLabel)
327
- );
328
- }
329
- if (variant === "icon-button") {
330
- const icon = isDark ? darkLabel : lightLabel;
331
- return React2.createElement(
332
- Touchable,
333
- {
334
- onPress: toggle,
335
- style: {
336
- padding: 8,
337
- borderRadius: 8,
338
- backgroundColor: isDark ? "#374151" : "#f3f4f6",
339
- ...style
340
- },
341
- accessibilityRole: "button",
342
- accessibilityLabel: `Switch to ${isDark ? "light" : "dark"} mode`,
343
- accessibilityState: {}
344
- },
345
- React2.createElement(Text, { style: { fontSize: 18, ...labelStyle } }, icon)
346
- );
347
- }
348
- if (includeSystem) {
349
- const modes = [
350
- { key: "light", label: lightLabel },
351
- { key: "dark", label: darkLabel },
352
- { key: "system", label: "System" }
353
- ];
354
- return React2.createElement(
355
- View,
356
- { style: { flexDirection: "row", gap: 4, ...style } },
357
- ...modes.map(
358
- ({ key, label: mLabel }) => React2.createElement(
359
- Touchable,
360
- {
361
- key,
362
- onPress: () => setMode(key),
363
- style: {
364
- paddingHorizontal: 12,
365
- paddingVertical: 6,
366
- borderRadius: 6,
367
- backgroundColor: mode === key ? isDark ? "#6366f1" : "#3b82f6" : isDark ? "#374151" : "#e5e7eb"
368
- },
369
- accessibilityRole: "button",
370
- accessibilityLabel: `Set ${key} theme`,
371
- accessibilityState: { selected: mode === key }
372
- },
373
- React2.createElement(Text, {
374
- style: {
375
- fontSize: 13,
376
- fontWeight: "500",
377
- color: mode === key ? "#ffffff" : isDark ? "#d1d5db" : "#374151",
378
- ...labelStyle
379
- }
380
- }, mLabel)
381
- )
382
- )
383
- );
384
- }
385
- const currentLabel = label ?? (isDark ? darkLabel : lightLabel);
386
- return React2.createElement(
387
- Touchable,
388
- {
389
- onPress: toggle,
390
- style: {
391
- paddingHorizontal: 16,
392
- paddingVertical: 8,
393
- borderRadius: 8,
394
- backgroundColor: isDark ? "#374151" : "#e5e7eb",
395
- ...style
396
- },
397
- accessibilityRole: "button",
398
- accessibilityLabel: `Switch to ${isDark ? "light" : "dark"} mode`,
399
- accessibilityState: {}
400
- },
401
- React2.createElement(Text, {
402
- style: {
403
- fontSize: 14,
404
- fontWeight: "500",
405
- color: isDark ? "#f9fafb" : "#111827",
406
- ...labelStyle
407
- }
408
- }, currentLabel)
409
- );
410
- }
411
-
412
272
  // src/styled.tsx
413
- import React3, {
273
+ import React2, {
414
274
  forwardRef,
415
275
  useState as useState2,
416
- useCallback as useCallback3,
276
+ useCallback as useCallback2,
417
277
  useMemo as useMemo3
418
278
  } from "react";
419
279
  function styled(Component, baseClasses = "") {
@@ -440,39 +300,39 @@ function styled(Component, baseClasses = "") {
440
300
  const [pressed, setPressed] = useState2(false);
441
301
  const [hovered, setHovered] = useState2(false);
442
302
  const [focused, setFocused] = useState2(false);
443
- const handlePressIn = useCallback3((e) => {
303
+ const handlePressIn = useCallback2((e) => {
444
304
  setPressed(true);
445
305
  onPressIn?.(e);
446
306
  }, [onPressIn]);
447
- const handlePressOut = useCallback3((e) => {
307
+ const handlePressOut = useCallback2((e) => {
448
308
  setPressed(false);
449
309
  onPressOut?.(e);
450
310
  }, [onPressOut]);
451
- const handlePointerDown = useCallback3((e) => {
311
+ const handlePointerDown = useCallback2((e) => {
452
312
  setPressed(true);
453
313
  onPointerDown?.(e);
454
314
  }, [onPointerDown]);
455
- const handlePointerUp = useCallback3((e) => {
315
+ const handlePointerUp = useCallback2((e) => {
456
316
  setPressed(false);
457
317
  onPointerUp?.(e);
458
318
  }, [onPointerUp]);
459
- const handlePointerLeave = useCallback3((e) => {
319
+ const handlePointerLeave = useCallback2((e) => {
460
320
  setPressed(false);
461
321
  onPointerLeave?.(e);
462
322
  }, [onPointerLeave]);
463
- const handleMouseEnter = useCallback3((e) => {
323
+ const handleMouseEnter = useCallback2((e) => {
464
324
  setHovered(true);
465
325
  onMouseEnter?.(e);
466
326
  }, [onMouseEnter]);
467
- const handleMouseLeave = useCallback3((e) => {
327
+ const handleMouseLeave = useCallback2((e) => {
468
328
  setHovered(false);
469
329
  onMouseLeave?.(e);
470
330
  }, [onMouseLeave]);
471
- const handleFocus = useCallback3((e) => {
331
+ const handleFocus = useCallback2((e) => {
472
332
  setFocused(true);
473
333
  onFocus?.(e);
474
334
  }, [onFocus]);
475
- const handleBlur = useCallback3((e) => {
335
+ const handleBlur = useCallback2((e) => {
476
336
  setFocused(false);
477
337
  onBlur?.(e);
478
338
  }, [onBlur]);
@@ -493,7 +353,7 @@ function styled(Component, baseClasses = "") {
493
353
  const effectiveComponent = webTag ?? Component;
494
354
  const componentName2 = Component.displayName ?? Component.name ?? "";
495
355
  const effectiveRest = webTag && componentName2 ? transformToWebProps(componentName2, webTag, rest) : rest;
496
- return React3.createElement(effectiveComponent, {
356
+ return React2.createElement(effectiveComponent, {
497
357
  ref,
498
358
  ...effectiveRest,
499
359
  style: finalStyle,
@@ -501,7 +361,9 @@ function styled(Component, baseClasses = "") {
501
361
  ...isWeb && combined ? { className: normalizeClassString(combined) } : {},
502
362
  // Only attach state-tracking handlers when interactive modifiers are present.
503
363
  // Always forward user-provided handlers to avoid silently swallowing them.
504
- ...isWeb ? hasInteractive ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerLeave } : { onPointerDown, onPointerUp, onPointerLeave } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
364
+ // onPressIn/onPressOut are forwarded on web too (not just native) since
365
+ // react-native-web components rely on receiving them directly.
366
+ ...isWeb ? hasInteractive ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerLeave, onPressIn, onPressOut } : { onPointerDown, onPointerUp, onPointerLeave, onPressIn, onPressOut } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
505
367
  onMouseEnter: hasInteractive ? handleMouseEnter : onMouseEnter,
506
368
  onMouseLeave: hasInteractive ? handleMouseLeave : onMouseLeave,
507
369
  onFocus: hasInteractive ? handleFocus : onFocus,
@@ -579,7 +441,6 @@ export {
579
441
  InteractiveWrapper,
580
442
  ThemeContext,
581
443
  ThemeProvider,
582
- ThemeToggle,
583
444
  buildConfig,
584
445
  cx,
585
446
  defaultColors,
@@ -1219,10 +1219,12 @@ var RESOLVERS = {
1219
1219
  const color = resolveColor(value, colors, isArbitrary);
1220
1220
  return color ? { backgroundColor: color } : null;
1221
1221
  },
1222
- "bg-opacity": ({ value }, _) => {
1222
+ "bg-opacity": ({ value, isArbitrary }, _) => {
1223
1223
  if (!getEffectiveIsWeb()) return null;
1224
- const v = parseFloat(value) / 100;
1225
- return isNaN(v) ? null : { "--bg-opacity": v };
1224
+ const n = parseFloat(value);
1225
+ if (isNaN(n)) return null;
1226
+ const v = isArbitrary ? n > 1 ? n / 100 : n : n / 100;
1227
+ return { "--bg-opacity": v };
1226
1228
  },
1227
1229
  // ── Text ───────────────────────────────────────────────────────────────────
1228
1230
  text: ({ value, isArbitrary }, { colors, fontSize }) => {
@@ -1237,10 +1239,12 @@ var RESOLVERS = {
1237
1239
  const color = resolveColor(value, colors, false);
1238
1240
  return color ? { color } : null;
1239
1241
  },
1240
- "text-opacity": ({ value }, _) => {
1242
+ "text-opacity": ({ value, isArbitrary }, _) => {
1241
1243
  if (!getEffectiveIsWeb()) return null;
1242
- const v = parseFloat(value) / 100;
1243
- return isNaN(v) ? null : { "--text-opacity": v };
1244
+ const n = parseFloat(value);
1245
+ if (isNaN(n)) return null;
1246
+ const v = isArbitrary ? n > 1 ? n / 100 : n : n / 100;
1247
+ return { "--text-opacity": v };
1244
1248
  },
1245
1249
  // ── Padding ────────────────────────────────────────────────────────────────
1246
1250
  p: ({ value, negative, isArbitrary }, { spacing }) => {
@@ -2141,9 +2145,9 @@ var RESOLVERS = {
2141
2145
  const n = parseFloat(value);
2142
2146
  return { flexShrink: isNaN(n) ? 1 : n };
2143
2147
  },
2144
- order: ({ value, isArbitrary }, _) => {
2145
- const n = parseInt(value);
2146
- return isNaN(n) ? null : { order: n };
2148
+ order: ({ value, negative }, _) => {
2149
+ const n = parseInt(value, 10);
2150
+ return isNaN(n) ? null : { order: negative ? -n : n };
2147
2151
  },
2148
2152
  // ── Gap ────────────────────────────────────────────────────────────────────
2149
2153
  gap: ({ value, negative, isArbitrary }, { spacing }) => {
@@ -2360,10 +2364,17 @@ var RESOLVERS = {
2360
2364
  const DEFAULT_RING_COLOR = "rgba(59, 130, 246, 0.5)";
2361
2365
  const DEFAULT_RING_WIDTH = 3;
2362
2366
  const offsetTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
2363
- const offsetWidth = !isArbitrary ? value in offsetTokens ? offsetTokens[value] : null : parseFloat(value);
2364
- if (offsetWidth === null || isNaN(offsetWidth)) return null;
2367
+ let offsetWidth;
2368
+ if (!isArbitrary) {
2369
+ offsetWidth = value in offsetTokens ? offsetTokens[value] : null;
2370
+ } else {
2371
+ offsetWidth = /^-?\d+(\.\d+)?$/.test(value) ? parseFloat(value) : value;
2372
+ }
2373
+ if (offsetWidth === null || typeof offsetWidth === "number" && isNaN(offsetWidth)) return null;
2374
+ const isZero = offsetWidth === 0 || offsetWidth === "0";
2375
+ const offsetCss = typeof offsetWidth === "number" ? `${offsetWidth}px` : offsetWidth;
2365
2376
  return {
2366
- boxShadow: offsetWidth === 0 ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetWidth}px #fff, 0 0 0 ${offsetWidth + DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}`
2377
+ boxShadow: isZero ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetCss} #fff, 0 0 0 calc(${offsetCss} + ${DEFAULT_RING_WIDTH}px) ${DEFAULT_RING_COLOR}`
2367
2378
  };
2368
2379
  },
2369
2380
  // ── Space between ─────────────────────────────────────────────────────────
@@ -3196,7 +3207,6 @@ function hasResponsiveBuckets(resolved) {
3196
3207
  }
3197
3208
  return false;
3198
3209
  }
3199
- var EMPTY_BREAKPOINTS2 = /* @__PURE__ */ new Set();
3200
3210
 
3201
3211
  // src/InteractiveWrapper.tsx
3202
3212
  function chain(original, extra) {
@@ -3284,9 +3294,10 @@ var InteractiveWrapper = (0, import_react3.forwardRef)(
3284
3294
  ...checked !== void 0 ? { checked } : {},
3285
3295
  style: finalStyle,
3286
3296
  ...!isNative && className ? { className } : {},
3287
- // onPressIn / onPressOut are React Native-only.
3288
- // On web, onPointerDown/Up drive the pressed state instead (covers mouse + touch).
3289
- ...!isNative ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerLeave } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3297
+ // On web, onPointerDown/Up drive the wrapper's own pressed state (covers mouse + touch),
3298
+ // but onPressIn/onPressOut are still forwarded unchanged — react-native-web components
3299
+ // (and any component that accepts both prop names) depend on receiving them directly.
3300
+ ...!isNative ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerLeave, onPressIn, onPressOut } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3290
3301
  onMouseEnter: handleMouseEnter,
3291
3302
  onMouseLeave: handleMouseLeave,
3292
3303
  onFocus: handleFocus,
@@ -3304,7 +3315,7 @@ var DarkWrapper = import_react4.default.forwardRef(
3304
3315
  const isDark = useGlobalDarkMode();
3305
3316
  const needsWidth = hasResponsiveBuckets(resolvedStyle);
3306
3317
  const width = useConditionalWidth(needsWidth);
3307
- const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS2;
3318
+ const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
3308
3319
  const screens = getGlobalScreens();
3309
3320
  const isNonStringComponent = typeof Component !== "string";
3310
3321
  const computedStyle = (0, import_react4.useMemo)(() => {
@@ -3728,7 +3739,7 @@ function processElement(type, rawProps, key, isStaticChildren) {
3728
3739
  ...userStyle2 !== void 0 ? { style: userStyle2 } : {}
3729
3740
  }, key);
3730
3741
  }
3731
- if (classStr && !__kbachStyles && /^(__[^\s]+\s*)+$/.test(classStr.trim())) {
3742
+ if (classStr && !__kbachStyles && /^(__[A-Za-z0-9_]+\s*)+$/.test(classStr.trim())) {
3732
3743
  const { style: userStyle2, ...passProps2 } = omitConsumed(workingProps);
3733
3744
  return makeElement(isStaticChildren, effectiveType, {
3734
3745
  ...passProps2,
@@ -2,9 +2,9 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-LU2QNVYU.mjs";
6
- import "./chunk-2MVCYYH5.mjs";
7
- import "./chunk-NP4FVE77.mjs";
5
+ } from "./chunk-AAAI37Y4.mjs";
6
+ import "./chunk-D4LJICWZ.mjs";
7
+ import "./chunk-6NED62DB.mjs";
8
8
 
9
9
  // src/jsx-dev-runtime.tsx
10
10
  function jsxDEV(type, props, key, isStaticChildren, source, self) {
@@ -1220,10 +1220,12 @@ var RESOLVERS = {
1220
1220
  const color = resolveColor(value, colors, isArbitrary);
1221
1221
  return color ? { backgroundColor: color } : null;
1222
1222
  },
1223
- "bg-opacity": ({ value }, _) => {
1223
+ "bg-opacity": ({ value, isArbitrary }, _) => {
1224
1224
  if (!getEffectiveIsWeb()) return null;
1225
- const v = parseFloat(value) / 100;
1226
- return isNaN(v) ? null : { "--bg-opacity": v };
1225
+ const n = parseFloat(value);
1226
+ if (isNaN(n)) return null;
1227
+ const v = isArbitrary ? n > 1 ? n / 100 : n : n / 100;
1228
+ return { "--bg-opacity": v };
1227
1229
  },
1228
1230
  // ── Text ───────────────────────────────────────────────────────────────────
1229
1231
  text: ({ value, isArbitrary }, { colors, fontSize }) => {
@@ -1238,10 +1240,12 @@ var RESOLVERS = {
1238
1240
  const color = resolveColor(value, colors, false);
1239
1241
  return color ? { color } : null;
1240
1242
  },
1241
- "text-opacity": ({ value }, _) => {
1243
+ "text-opacity": ({ value, isArbitrary }, _) => {
1242
1244
  if (!getEffectiveIsWeb()) return null;
1243
- const v = parseFloat(value) / 100;
1244
- return isNaN(v) ? null : { "--text-opacity": v };
1245
+ const n = parseFloat(value);
1246
+ if (isNaN(n)) return null;
1247
+ const v = isArbitrary ? n > 1 ? n / 100 : n : n / 100;
1248
+ return { "--text-opacity": v };
1245
1249
  },
1246
1250
  // ── Padding ────────────────────────────────────────────────────────────────
1247
1251
  p: ({ value, negative, isArbitrary }, { spacing }) => {
@@ -2142,9 +2146,9 @@ var RESOLVERS = {
2142
2146
  const n = parseFloat(value);
2143
2147
  return { flexShrink: isNaN(n) ? 1 : n };
2144
2148
  },
2145
- order: ({ value, isArbitrary }, _) => {
2146
- const n = parseInt(value);
2147
- return isNaN(n) ? null : { order: n };
2149
+ order: ({ value, negative }, _) => {
2150
+ const n = parseInt(value, 10);
2151
+ return isNaN(n) ? null : { order: negative ? -n : n };
2148
2152
  },
2149
2153
  // ── Gap ────────────────────────────────────────────────────────────────────
2150
2154
  gap: ({ value, negative, isArbitrary }, { spacing }) => {
@@ -2361,10 +2365,17 @@ var RESOLVERS = {
2361
2365
  const DEFAULT_RING_COLOR = "rgba(59, 130, 246, 0.5)";
2362
2366
  const DEFAULT_RING_WIDTH = 3;
2363
2367
  const offsetTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
2364
- const offsetWidth = !isArbitrary ? value in offsetTokens ? offsetTokens[value] : null : parseFloat(value);
2365
- if (offsetWidth === null || isNaN(offsetWidth)) return null;
2368
+ let offsetWidth;
2369
+ if (!isArbitrary) {
2370
+ offsetWidth = value in offsetTokens ? offsetTokens[value] : null;
2371
+ } else {
2372
+ offsetWidth = /^-?\d+(\.\d+)?$/.test(value) ? parseFloat(value) : value;
2373
+ }
2374
+ if (offsetWidth === null || typeof offsetWidth === "number" && isNaN(offsetWidth)) return null;
2375
+ const isZero = offsetWidth === 0 || offsetWidth === "0";
2376
+ const offsetCss = typeof offsetWidth === "number" ? `${offsetWidth}px` : offsetWidth;
2366
2377
  return {
2367
- boxShadow: offsetWidth === 0 ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetWidth}px #fff, 0 0 0 ${offsetWidth + DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}`
2378
+ boxShadow: isZero ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetCss} #fff, 0 0 0 calc(${offsetCss} + ${DEFAULT_RING_WIDTH}px) ${DEFAULT_RING_COLOR}`
2368
2379
  };
2369
2380
  },
2370
2381
  // ── Space between ─────────────────────────────────────────────────────────
@@ -3197,7 +3208,6 @@ function hasResponsiveBuckets(resolved) {
3197
3208
  }
3198
3209
  return false;
3199
3210
  }
3200
- var EMPTY_BREAKPOINTS2 = /* @__PURE__ */ new Set();
3201
3211
 
3202
3212
  // src/InteractiveWrapper.tsx
3203
3213
  function chain(original, extra) {
@@ -3285,9 +3295,10 @@ var InteractiveWrapper = (0, import_react3.forwardRef)(
3285
3295
  ...checked !== void 0 ? { checked } : {},
3286
3296
  style: finalStyle,
3287
3297
  ...!isNative && className ? { className } : {},
3288
- // onPressIn / onPressOut are React Native-only.
3289
- // On web, onPointerDown/Up drive the pressed state instead (covers mouse + touch).
3290
- ...!isNative ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerLeave } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3298
+ // On web, onPointerDown/Up drive the wrapper's own pressed state (covers mouse + touch),
3299
+ // but onPressIn/onPressOut are still forwarded unchanged — react-native-web components
3300
+ // (and any component that accepts both prop names) depend on receiving them directly.
3301
+ ...!isNative ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerLeave, onPressIn, onPressOut } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3291
3302
  onMouseEnter: handleMouseEnter,
3292
3303
  onMouseLeave: handleMouseLeave,
3293
3304
  onFocus: handleFocus,
@@ -3305,7 +3316,7 @@ var DarkWrapper = import_react4.default.forwardRef(
3305
3316
  const isDark = useGlobalDarkMode();
3306
3317
  const needsWidth = hasResponsiveBuckets(resolvedStyle);
3307
3318
  const width = useConditionalWidth(needsWidth);
3308
- const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS2;
3319
+ const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
3309
3320
  const screens = getGlobalScreens();
3310
3321
  const isNonStringComponent = typeof Component !== "string";
3311
3322
  const computedStyle = (0, import_react4.useMemo)(() => {
@@ -3734,7 +3745,7 @@ function processElement(type, rawProps, key, isStaticChildren) {
3734
3745
  ...userStyle2 !== void 0 ? { style: userStyle2 } : {}
3735
3746
  }, key);
3736
3747
  }
3737
- if (classStr && !__kbachStyles && /^(__[^\s]+\s*)+$/.test(classStr.trim())) {
3748
+ if (classStr && !__kbachStyles && /^(__[A-Za-z0-9_]+\s*)+$/.test(classStr.trim())) {
3738
3749
  const { style: userStyle2, ...passProps2 } = omitConsumed(workingProps);
3739
3750
  return makeElement(isStaticChildren, effectiveType, {
3740
3751
  ...passProps2,
@@ -3,11 +3,11 @@ import {
3
3
  _invalidateDefaultFontCache,
4
4
  jsx,
5
5
  jsxs
6
- } from "./chunk-LU2QNVYU.mjs";
6
+ } from "./chunk-AAAI37Y4.mjs";
7
7
  import {
8
8
  registerWebElement
9
- } from "./chunk-2MVCYYH5.mjs";
10
- import "./chunk-NP4FVE77.mjs";
9
+ } from "./chunk-D4LJICWZ.mjs";
10
+ import "./chunk-6NED62DB.mjs";
11
11
  export {
12
12
  Fragment,
13
13
  _invalidateDefaultFontCache,
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/vite-plugin.ts
@@ -25,6 +35,7 @@ __export(vite_plugin_exports, {
25
35
  module.exports = __toCommonJS(vite_plugin_exports);
26
36
  var import_fs = require("fs");
27
37
  var import_path = require("path");
38
+ var import_node_process = __toESM(require("process"));
28
39
 
29
40
  // src/core/platform.ts
30
41
  var isWeb = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
@@ -1073,10 +1084,12 @@ var RESOLVERS = {
1073
1084
  const color = resolveColor(value, colors, isArbitrary);
1074
1085
  return color ? { backgroundColor: color } : null;
1075
1086
  },
1076
- "bg-opacity": ({ value }, _) => {
1087
+ "bg-opacity": ({ value, isArbitrary }, _) => {
1077
1088
  if (!getEffectiveIsWeb()) return null;
1078
- const v = parseFloat(value) / 100;
1079
- return isNaN(v) ? null : { "--bg-opacity": v };
1089
+ const n = parseFloat(value);
1090
+ if (isNaN(n)) return null;
1091
+ const v = isArbitrary ? n > 1 ? n / 100 : n : n / 100;
1092
+ return { "--bg-opacity": v };
1080
1093
  },
1081
1094
  // ── Text ───────────────────────────────────────────────────────────────────
1082
1095
  text: ({ value, isArbitrary }, { colors, fontSize }) => {
@@ -1091,10 +1104,12 @@ var RESOLVERS = {
1091
1104
  const color = resolveColor(value, colors, false);
1092
1105
  return color ? { color } : null;
1093
1106
  },
1094
- "text-opacity": ({ value }, _) => {
1107
+ "text-opacity": ({ value, isArbitrary }, _) => {
1095
1108
  if (!getEffectiveIsWeb()) return null;
1096
- const v = parseFloat(value) / 100;
1097
- return isNaN(v) ? null : { "--text-opacity": v };
1109
+ const n = parseFloat(value);
1110
+ if (isNaN(n)) return null;
1111
+ const v = isArbitrary ? n > 1 ? n / 100 : n : n / 100;
1112
+ return { "--text-opacity": v };
1098
1113
  },
1099
1114
  // ── Padding ────────────────────────────────────────────────────────────────
1100
1115
  p: ({ value, negative, isArbitrary }, { spacing }) => {
@@ -1995,9 +2010,9 @@ var RESOLVERS = {
1995
2010
  const n = parseFloat(value);
1996
2011
  return { flexShrink: isNaN(n) ? 1 : n };
1997
2012
  },
1998
- order: ({ value, isArbitrary }, _) => {
1999
- const n = parseInt(value);
2000
- return isNaN(n) ? null : { order: n };
2013
+ order: ({ value, negative }, _) => {
2014
+ const n = parseInt(value, 10);
2015
+ return isNaN(n) ? null : { order: negative ? -n : n };
2001
2016
  },
2002
2017
  // ── Gap ────────────────────────────────────────────────────────────────────
2003
2018
  gap: ({ value, negative, isArbitrary }, { spacing }) => {
@@ -2214,10 +2229,17 @@ var RESOLVERS = {
2214
2229
  const DEFAULT_RING_COLOR = "rgba(59, 130, 246, 0.5)";
2215
2230
  const DEFAULT_RING_WIDTH = 3;
2216
2231
  const offsetTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
2217
- const offsetWidth = !isArbitrary ? value in offsetTokens ? offsetTokens[value] : null : parseFloat(value);
2218
- if (offsetWidth === null || isNaN(offsetWidth)) return null;
2232
+ let offsetWidth;
2233
+ if (!isArbitrary) {
2234
+ offsetWidth = value in offsetTokens ? offsetTokens[value] : null;
2235
+ } else {
2236
+ offsetWidth = /^-?\d+(\.\d+)?$/.test(value) ? parseFloat(value) : value;
2237
+ }
2238
+ if (offsetWidth === null || typeof offsetWidth === "number" && isNaN(offsetWidth)) return null;
2239
+ const isZero = offsetWidth === 0 || offsetWidth === "0";
2240
+ const offsetCss = typeof offsetWidth === "number" ? `${offsetWidth}px` : offsetWidth;
2219
2241
  return {
2220
- boxShadow: offsetWidth === 0 ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetWidth}px #fff, 0 0 0 ${offsetWidth + DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}`
2242
+ boxShadow: isZero ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetCss} #fff, 0 0 0 calc(${offsetCss} + ${DEFAULT_RING_WIDTH}px) ${DEFAULT_RING_COLOR}`
2221
2243
  };
2222
2244
  },
2223
2245
  // ── Space between ─────────────────────────────────────────────────────────
@@ -2917,9 +2939,10 @@ var customVariants = new Proxy(
2917
2939
  );
2918
2940
 
2919
2941
  // src/vite-plugin.ts
2942
+ var process2 = import_node_process.default;
2920
2943
  function normPath(p) {
2921
2944
  const fwd = p.replace(/\\/g, "/");
2922
- return process.platform === "win32" ? fwd.toLowerCase() : fwd;
2945
+ return process2.platform === "win32" ? fwd.toLowerCase() : fwd;
2923
2946
  }
2924
2947
  setCSSGenMode(true);
2925
2948
  var KBACH_START = "/* kbach:start */";
@@ -3159,7 +3182,7 @@ function kbach(userConfigOrOptions) {
3159
3182
  const screenKeys = Object.keys(cfg.theme.screens ?? {});
3160
3183
  const responsivePattern = screenKeys.length ? screenKeys.map((k) => k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|") : "sm|md|lg|xl|2xl";
3161
3184
  const responsiveRe = new RegExp(`^(${responsivePattern}):`);
3162
- let root = process.cwd();
3185
+ let root = process2.cwd();
3163
3186
  let mainCSSFile = null;
3164
3187
  const fileTokens = /* @__PURE__ */ new Map();
3165
3188
  const tokenCSS = /* @__PURE__ */ new Map();
@@ -4,11 +4,13 @@ import {
4
4
  generateClassCSS,
5
5
  setCSSGenMode,
6
6
  splitClassTokens
7
- } from "./chunk-NP4FVE77.mjs";
7
+ } from "./chunk-6NED62DB.mjs";
8
8
 
9
9
  // src/vite-plugin.ts
10
10
  import { readFileSync, writeFileSync, readdirSync, statSync } from "fs";
11
11
  import { join } from "path";
12
+ import processImport from "process";
13
+ var process = processImport;
12
14
  function normPath(p) {
13
15
  const fwd = p.replace(/\\/g, "/");
14
16
  return process.platform === "win32" ? fwd.toLowerCase() : fwd;
package/kbach-react.md CHANGED
@@ -163,14 +163,6 @@ colors['white/20'] // 'rgba(255,255,255,0.2)'
163
163
  colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'
164
164
  ```
165
165
 
166
- ### ThemeToggle
167
- ```jsx
168
- <ThemeToggle /> // button (default)
169
- <ThemeToggle variant="switch" /> // toggle switch
170
- <ThemeToggle variant="icon-button" /> // icon button
171
- <ThemeToggle variant="button" includeSystem /> // three-way light/dark/system
172
- ```
173
-
174
166
  ---
175
167
 
176
168
  ## Modifier System
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react",
3
- "version": "0.5.60",
3
+ "version": "0.6.0",
4
4
  "description": "React / React Native components and hooks for the Kbach framework",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./dist/index.js",