@kbach/react 0.5.2 → 0.5.4

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.
@@ -3078,12 +3078,12 @@ function injectClassRule(cls, bucketKey, styles, darkMode, important = false) {
3078
3078
  injectRule(styles.__keyframe);
3079
3079
  }
3080
3080
  const isDivide = "__divideX" in styles || "__divideY" in styles || "__divideColor" in styles || "__divideStyle" in styles;
3081
- const cssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, important);
3082
- if (!cssDecls) return;
3083
3081
  const escaped = escapeCSSSelector(cls);
3084
3082
  const childSuffix = isDivide ? " > * + *" : "";
3085
3083
  if (bucketKey === "base") {
3086
- injectRule(`.${escaped}${childSuffix} { ${cssDecls} }`);
3084
+ const baseCssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, important);
3085
+ if (!baseCssDecls) return;
3086
+ injectRule(`.${escaped}${childSuffix} { ${baseCssDecls} }`);
3087
3087
  return;
3088
3088
  }
3089
3089
  const mods = bucketKey.split(":");
@@ -3103,6 +3103,18 @@ function injectClassRule(cls, bucketKey, styles, darkMode, important = false) {
3103
3103
  const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
3104
3104
  const dirPrefix = dirMods.length ? dirMods.map((m) => DIR_MODS[m]).join(" ") + " " : "";
3105
3105
  const selector = `${dirPrefix}${ancestorPrefix}${elementSelector}`;
3106
+ const CSS_ONLY_PSEUDOS = /* @__PURE__ */ new Set([
3107
+ "first",
3108
+ "last",
3109
+ "odd",
3110
+ "even",
3111
+ "only",
3112
+ "focus-within",
3113
+ "focus-visible"
3114
+ ]);
3115
+ const needsImportant = important || hasPrint || mediaMods.length > 0 || dirMods.length > 0 || ancestorMods.length > 0 || pseudoMods.some((m) => CSS_ONLY_PSEUDOS.has(m));
3116
+ const cssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, needsImportant);
3117
+ if (!cssDecls) return;
3106
3118
  let rule;
3107
3119
  if (hasDark) {
3108
3120
  if (darkMode === "media") {
@@ -3225,9 +3237,19 @@ function resolve(classString, theme, darkMode = "attribute") {
3225
3237
  return result;
3226
3238
  }
3227
3239
  function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
3228
- const result = { ...resolved.base ?? {} };
3229
- for (const [key, styles] of Object.entries(resolved)) {
3230
- if (key === "base" || !styles) continue;
3240
+ const result = {};
3241
+ const entries = Object.entries(resolved);
3242
+ entries.sort((a, b) => {
3243
+ const pa = a[0] === "base" ? -1 : a[0].split(":").length;
3244
+ const pb = b[0] === "base" ? -1 : b[0].split(":").length;
3245
+ return pa - pb;
3246
+ });
3247
+ for (const [key, styles] of entries) {
3248
+ if (!styles) continue;
3249
+ if (key === "base") {
3250
+ Object.assign(result, styles);
3251
+ continue;
3252
+ }
3231
3253
  const mods = key.split(":");
3232
3254
  if (matchesMods(mods, isDark, state, breakpoints)) {
3233
3255
  Object.assign(result, styles);
@@ -3578,6 +3600,9 @@ var InteractiveWrapper = forwardRef(
3578
3600
  style: styleProp,
3579
3601
  onPressIn,
3580
3602
  onPressOut,
3603
+ onPointerDown,
3604
+ onPointerUp,
3605
+ onPointerLeave,
3581
3606
  onMouseEnter,
3582
3607
  onMouseLeave,
3583
3608
  onFocus,
@@ -3593,6 +3618,9 @@ var InteractiveWrapper = forwardRef(
3593
3618
  const [focused, setFocused] = useState(false);
3594
3619
  const handlePressIn = useCallback(chain(onPressIn, () => setPressed(true)), [onPressIn]);
3595
3620
  const handlePressOut = useCallback(chain(onPressOut, () => setPressed(false)), [onPressOut]);
3621
+ const handlePointerDown = useCallback(chain(onPointerDown, () => setPressed(true)), [onPointerDown]);
3622
+ const handlePointerUp = useCallback(chain(onPointerUp, () => setPressed(false)), [onPointerUp]);
3623
+ const handlePointerLeave = useCallback(chain(onPointerLeave, () => setPressed(false)), [onPointerLeave]);
3596
3624
  const handleMouseEnter = useCallback(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
3597
3625
  const handleMouseLeave = useCallback(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
3598
3626
  const handleFocus = useCallback(chain(onFocus, () => setFocused(true)), [onFocus]);
@@ -3640,8 +3668,9 @@ var InteractiveWrapper = forwardRef(
3640
3668
  ...restWithoutChildren,
3641
3669
  style: finalStyle,
3642
3670
  ...isWeb && className ? { className } : {},
3643
- // onPressIn / onPressOut are React Native-only; skip on web to avoid DOM warnings.
3644
- ...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3671
+ // onPressIn / onPressOut are React Native-only.
3672
+ // On web, onPointerDown/Up drive the pressed state instead (covers mouse + touch).
3673
+ ...isWeb ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3645
3674
  onMouseEnter: handleMouseEnter,
3646
3675
  onMouseLeave: handleMouseLeave,
3647
3676
  onFocus: handleFocus,
@@ -8,7 +8,7 @@ import {
8
8
  resolve,
9
9
  useConditionalWidth,
10
10
  useGlobalDarkMode
11
- } from "./chunk-XVPIJMIR.mjs";
11
+ } from "./chunk-D7Q7ZCOW.mjs";
12
12
 
13
13
  // src/jsx-runtime.tsx
14
14
  import { jsx as _jsx, jsxs as _jsxs, Fragment } from "react/jsx-runtime";
@@ -88,17 +88,12 @@ var INTERACTIVE_MODS = /* @__PURE__ */ new Set([
88
88
  "visited",
89
89
  "not-visited",
90
90
  "placeholder",
91
- "focus-within",
92
- "focus-visible",
93
91
  "group-hover",
94
92
  "group-focus",
95
93
  "peer-hover",
96
- "peer-focus",
97
- "first",
98
- "last",
99
- "odd",
100
- "even",
101
- "only"
94
+ "peer-focus"
95
+ // focus-within / focus-visible / first / last / odd / even / only are CSS-only —
96
+ // they have no JS state to track, so they don't need InteractiveWrapper.
102
97
  ]);
103
98
  var RESPONSIVE_MODS2 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
104
99
  var MODE_MODS_JSX = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
package/dist/index.d.mts CHANGED
@@ -315,6 +315,9 @@ interface InteractiveWrapperProps {
315
315
  children?: React__default.ReactNode;
316
316
  onPressIn?: (...args: any[]) => void;
317
317
  onPressOut?: (...args: any[]) => void;
318
+ onPointerDown?: (...args: any[]) => void;
319
+ onPointerUp?: (...args: any[]) => void;
320
+ onPointerLeave?: (...args: any[]) => void;
318
321
  onMouseEnter?: (...args: any[]) => void;
319
322
  onMouseLeave?: (...args: any[]) => void;
320
323
  onFocus?: (...args: any[]) => void;
package/dist/index.d.ts CHANGED
@@ -315,6 +315,9 @@ interface InteractiveWrapperProps {
315
315
  children?: React__default.ReactNode;
316
316
  onPressIn?: (...args: any[]) => void;
317
317
  onPressOut?: (...args: any[]) => void;
318
+ onPointerDown?: (...args: any[]) => void;
319
+ onPointerUp?: (...args: any[]) => void;
320
+ onPointerLeave?: (...args: any[]) => void;
318
321
  onMouseEnter?: (...args: any[]) => void;
319
322
  onMouseLeave?: (...args: any[]) => void;
320
323
  onFocus?: (...args: any[]) => void;
package/dist/index.js CHANGED
@@ -3208,12 +3208,12 @@ function injectClassRule(cls, bucketKey, styles, darkMode, important = false) {
3208
3208
  injectRule(styles.__keyframe);
3209
3209
  }
3210
3210
  const isDivide = "__divideX" in styles || "__divideY" in styles || "__divideColor" in styles || "__divideStyle" in styles;
3211
- const cssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, important);
3212
- if (!cssDecls) return;
3213
3211
  const escaped = escapeCSSSelector(cls);
3214
3212
  const childSuffix = isDivide ? " > * + *" : "";
3215
3213
  if (bucketKey === "base") {
3216
- injectRule(`.${escaped}${childSuffix} { ${cssDecls} }`);
3214
+ const baseCssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, important);
3215
+ if (!baseCssDecls) return;
3216
+ injectRule(`.${escaped}${childSuffix} { ${baseCssDecls} }`);
3217
3217
  return;
3218
3218
  }
3219
3219
  const mods = bucketKey.split(":");
@@ -3233,6 +3233,18 @@ function injectClassRule(cls, bucketKey, styles, darkMode, important = false) {
3233
3233
  const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
3234
3234
  const dirPrefix = dirMods.length ? dirMods.map((m) => DIR_MODS[m]).join(" ") + " " : "";
3235
3235
  const selector = `${dirPrefix}${ancestorPrefix}${elementSelector}`;
3236
+ const CSS_ONLY_PSEUDOS = /* @__PURE__ */ new Set([
3237
+ "first",
3238
+ "last",
3239
+ "odd",
3240
+ "even",
3241
+ "only",
3242
+ "focus-within",
3243
+ "focus-visible"
3244
+ ]);
3245
+ const needsImportant = important || hasPrint || mediaMods.length > 0 || dirMods.length > 0 || ancestorMods.length > 0 || pseudoMods.some((m) => CSS_ONLY_PSEUDOS.has(m));
3246
+ const cssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, needsImportant);
3247
+ if (!cssDecls) return;
3236
3248
  let rule;
3237
3249
  if (hasDark) {
3238
3250
  if (darkMode === "media") {
@@ -3355,9 +3367,19 @@ function resolve(classString, theme, darkMode = "attribute") {
3355
3367
  return result;
3356
3368
  }
3357
3369
  function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
3358
- const result = { ...resolved.base ?? {} };
3359
- for (const [key, styles] of Object.entries(resolved)) {
3360
- if (key === "base" || !styles) continue;
3370
+ const result = {};
3371
+ const entries = Object.entries(resolved);
3372
+ entries.sort((a, b) => {
3373
+ const pa = a[0] === "base" ? -1 : a[0].split(":").length;
3374
+ const pb = b[0] === "base" ? -1 : b[0].split(":").length;
3375
+ return pa - pb;
3376
+ });
3377
+ for (const [key, styles] of entries) {
3378
+ if (!styles) continue;
3379
+ if (key === "base") {
3380
+ Object.assign(result, styles);
3381
+ continue;
3382
+ }
3361
3383
  const mods = key.split(":");
3362
3384
  if (matchesMods(mods, isDark, state, breakpoints)) {
3363
3385
  Object.assign(result, styles);
@@ -4098,6 +4120,9 @@ var InteractiveWrapper = (0, import_react9.forwardRef)(
4098
4120
  style: styleProp,
4099
4121
  onPressIn,
4100
4122
  onPressOut,
4123
+ onPointerDown,
4124
+ onPointerUp,
4125
+ onPointerLeave,
4101
4126
  onMouseEnter,
4102
4127
  onMouseLeave,
4103
4128
  onFocus,
@@ -4113,6 +4138,9 @@ var InteractiveWrapper = (0, import_react9.forwardRef)(
4113
4138
  const [focused, setFocused] = (0, import_react9.useState)(false);
4114
4139
  const handlePressIn = (0, import_react9.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
4115
4140
  const handlePressOut = (0, import_react9.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
4141
+ const handlePointerDown = (0, import_react9.useCallback)(chain(onPointerDown, () => setPressed(true)), [onPointerDown]);
4142
+ const handlePointerUp = (0, import_react9.useCallback)(chain(onPointerUp, () => setPressed(false)), [onPointerUp]);
4143
+ const handlePointerLeave = (0, import_react9.useCallback)(chain(onPointerLeave, () => setPressed(false)), [onPointerLeave]);
4116
4144
  const handleMouseEnter = (0, import_react9.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
4117
4145
  const handleMouseLeave = (0, import_react9.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
4118
4146
  const handleFocus = (0, import_react9.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
@@ -4160,8 +4188,9 @@ var InteractiveWrapper = (0, import_react9.forwardRef)(
4160
4188
  ...restWithoutChildren,
4161
4189
  style: finalStyle,
4162
4190
  ...isWeb && className ? { className } : {},
4163
- // onPressIn / onPressOut are React Native-only; skip on web to avoid DOM warnings.
4164
- ...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
4191
+ // onPressIn / onPressOut are React Native-only.
4192
+ // On web, onPointerDown/Up drive the pressed state instead (covers mouse + touch).
4193
+ ...isWeb ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
4165
4194
  onMouseEnter: handleMouseEnter,
4166
4195
  onMouseLeave: handleMouseLeave,
4167
4196
  onFocus: handleFocus,
package/dist/index.mjs CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  useConditionalWidth,
24
24
  useGlobalDarkMode,
25
25
  useGlobalWidth
26
- } from "./chunk-XVPIJMIR.mjs";
26
+ } from "./chunk-D7Q7ZCOW.mjs";
27
27
 
28
28
  // src/context.tsx
29
29
  import { createContext, useContext } from "react";
@@ -3099,12 +3099,12 @@ function injectClassRule(cls, bucketKey, styles, darkMode, important = false) {
3099
3099
  injectRule(styles.__keyframe);
3100
3100
  }
3101
3101
  const isDivide = "__divideX" in styles || "__divideY" in styles || "__divideColor" in styles || "__divideStyle" in styles;
3102
- const cssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, important);
3103
- if (!cssDecls) return;
3104
3102
  const escaped = escapeCSSSelector(cls);
3105
3103
  const childSuffix = isDivide ? " > * + *" : "";
3106
3104
  if (bucketKey === "base") {
3107
- injectRule(`.${escaped}${childSuffix} { ${cssDecls} }`);
3105
+ const baseCssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, important);
3106
+ if (!baseCssDecls) return;
3107
+ injectRule(`.${escaped}${childSuffix} { ${baseCssDecls} }`);
3108
3108
  return;
3109
3109
  }
3110
3110
  const mods = bucketKey.split(":");
@@ -3124,6 +3124,18 @@ function injectClassRule(cls, bucketKey, styles, darkMode, important = false) {
3124
3124
  const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
3125
3125
  const dirPrefix = dirMods.length ? dirMods.map((m) => DIR_MODS[m]).join(" ") + " " : "";
3126
3126
  const selector = `${dirPrefix}${ancestorPrefix}${elementSelector}`;
3127
+ const CSS_ONLY_PSEUDOS = /* @__PURE__ */ new Set([
3128
+ "first",
3129
+ "last",
3130
+ "odd",
3131
+ "even",
3132
+ "only",
3133
+ "focus-within",
3134
+ "focus-visible"
3135
+ ]);
3136
+ const needsImportant = important || hasPrint || mediaMods.length > 0 || dirMods.length > 0 || ancestorMods.length > 0 || pseudoMods.some((m) => CSS_ONLY_PSEUDOS.has(m));
3137
+ const cssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, needsImportant);
3138
+ if (!cssDecls) return;
3127
3139
  let rule;
3128
3140
  if (hasDark) {
3129
3141
  if (darkMode === "media") {
@@ -3246,9 +3258,19 @@ function resolve(classString, theme, darkMode = "attribute") {
3246
3258
  return result;
3247
3259
  }
3248
3260
  function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
3249
- const result = { ...resolved.base ?? {} };
3250
- for (const [key, styles] of Object.entries(resolved)) {
3251
- if (key === "base" || !styles) continue;
3261
+ const result = {};
3262
+ const entries = Object.entries(resolved);
3263
+ entries.sort((a, b) => {
3264
+ const pa = a[0] === "base" ? -1 : a[0].split(":").length;
3265
+ const pb = b[0] === "base" ? -1 : b[0].split(":").length;
3266
+ return pa - pb;
3267
+ });
3268
+ for (const [key, styles] of entries) {
3269
+ if (!styles) continue;
3270
+ if (key === "base") {
3271
+ Object.assign(result, styles);
3272
+ continue;
3273
+ }
3252
3274
  const mods = key.split(":");
3253
3275
  if (matchesMods(mods, isDark, state, breakpoints)) {
3254
3276
  Object.assign(result, styles);
@@ -3556,6 +3578,9 @@ var InteractiveWrapper = (0, import_react3.forwardRef)(
3556
3578
  style: styleProp,
3557
3579
  onPressIn,
3558
3580
  onPressOut,
3581
+ onPointerDown,
3582
+ onPointerUp,
3583
+ onPointerLeave,
3559
3584
  onMouseEnter,
3560
3585
  onMouseLeave,
3561
3586
  onFocus,
@@ -3571,6 +3596,9 @@ var InteractiveWrapper = (0, import_react3.forwardRef)(
3571
3596
  const [focused, setFocused] = (0, import_react3.useState)(false);
3572
3597
  const handlePressIn = (0, import_react3.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
3573
3598
  const handlePressOut = (0, import_react3.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
3599
+ const handlePointerDown = (0, import_react3.useCallback)(chain(onPointerDown, () => setPressed(true)), [onPointerDown]);
3600
+ const handlePointerUp = (0, import_react3.useCallback)(chain(onPointerUp, () => setPressed(false)), [onPointerUp]);
3601
+ const handlePointerLeave = (0, import_react3.useCallback)(chain(onPointerLeave, () => setPressed(false)), [onPointerLeave]);
3574
3602
  const handleMouseEnter = (0, import_react3.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
3575
3603
  const handleMouseLeave = (0, import_react3.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
3576
3604
  const handleFocus = (0, import_react3.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
@@ -3618,8 +3646,9 @@ var InteractiveWrapper = (0, import_react3.forwardRef)(
3618
3646
  ...restWithoutChildren,
3619
3647
  style: finalStyle,
3620
3648
  ...isWeb && className ? { className } : {},
3621
- // onPressIn / onPressOut are React Native-only; skip on web to avoid DOM warnings.
3622
- ...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3649
+ // onPressIn / onPressOut are React Native-only.
3650
+ // On web, onPointerDown/Up drive the pressed state instead (covers mouse + touch).
3651
+ ...isWeb ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3623
3652
  onMouseEnter: handleMouseEnter,
3624
3653
  onMouseLeave: handleMouseLeave,
3625
3654
  onFocus: handleFocus,
@@ -3705,17 +3734,12 @@ var INTERACTIVE_MODS = /* @__PURE__ */ new Set([
3705
3734
  "visited",
3706
3735
  "not-visited",
3707
3736
  "placeholder",
3708
- "focus-within",
3709
- "focus-visible",
3710
3737
  "group-hover",
3711
3738
  "group-focus",
3712
3739
  "peer-hover",
3713
- "peer-focus",
3714
- "first",
3715
- "last",
3716
- "odd",
3717
- "even",
3718
- "only"
3740
+ "peer-focus"
3741
+ // focus-within / focus-visible / first / last / odd / even / only are CSS-only —
3742
+ // they have no JS state to track, so they don't need InteractiveWrapper.
3719
3743
  ]);
3720
3744
  var RESPONSIVE_MODS4 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
3721
3745
  var MODE_MODS_JSX = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-XGWS3OQD.mjs";
6
- import "./chunk-XVPIJMIR.mjs";
5
+ } from "./chunk-UR4DTYE3.mjs";
6
+ import "./chunk-D7Q7ZCOW.mjs";
7
7
 
8
8
  // src/jsx-dev-runtime.tsx
9
9
  function jsxDEV(type, props, key, isStaticChildren, _source, _self) {
@@ -3098,12 +3098,12 @@ function injectClassRule(cls, bucketKey, styles, darkMode, important = false) {
3098
3098
  injectRule(styles.__keyframe);
3099
3099
  }
3100
3100
  const isDivide = "__divideX" in styles || "__divideY" in styles || "__divideColor" in styles || "__divideStyle" in styles;
3101
- const cssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, important);
3102
- if (!cssDecls) return;
3103
3101
  const escaped = escapeCSSSelector(cls);
3104
3102
  const childSuffix = isDivide ? " > * + *" : "";
3105
3103
  if (bucketKey === "base") {
3106
- injectRule(`.${escaped}${childSuffix} { ${cssDecls} }`);
3104
+ const baseCssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, important);
3105
+ if (!baseCssDecls) return;
3106
+ injectRule(`.${escaped}${childSuffix} { ${baseCssDecls} }`);
3107
3107
  return;
3108
3108
  }
3109
3109
  const mods = bucketKey.split(":");
@@ -3123,6 +3123,18 @@ function injectClassRule(cls, bucketKey, styles, darkMode, important = false) {
3123
3123
  const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
3124
3124
  const dirPrefix = dirMods.length ? dirMods.map((m) => DIR_MODS[m]).join(" ") + " " : "";
3125
3125
  const selector = `${dirPrefix}${ancestorPrefix}${elementSelector}`;
3126
+ const CSS_ONLY_PSEUDOS = /* @__PURE__ */ new Set([
3127
+ "first",
3128
+ "last",
3129
+ "odd",
3130
+ "even",
3131
+ "only",
3132
+ "focus-within",
3133
+ "focus-visible"
3134
+ ]);
3135
+ const needsImportant = important || hasPrint || mediaMods.length > 0 || dirMods.length > 0 || ancestorMods.length > 0 || pseudoMods.some((m) => CSS_ONLY_PSEUDOS.has(m));
3136
+ const cssDecls = isDivide ? buildDivideDecls(styles) : styleValueToCSS(styles, needsImportant);
3137
+ if (!cssDecls) return;
3126
3138
  let rule;
3127
3139
  if (hasDark) {
3128
3140
  if (darkMode === "media") {
@@ -3245,9 +3257,19 @@ function resolve(classString, theme, darkMode = "attribute") {
3245
3257
  return result;
3246
3258
  }
3247
3259
  function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
3248
- const result = { ...resolved.base ?? {} };
3249
- for (const [key, styles] of Object.entries(resolved)) {
3250
- if (key === "base" || !styles) continue;
3260
+ const result = {};
3261
+ const entries = Object.entries(resolved);
3262
+ entries.sort((a, b) => {
3263
+ const pa = a[0] === "base" ? -1 : a[0].split(":").length;
3264
+ const pb = b[0] === "base" ? -1 : b[0].split(":").length;
3265
+ return pa - pb;
3266
+ });
3267
+ for (const [key, styles] of entries) {
3268
+ if (!styles) continue;
3269
+ if (key === "base") {
3270
+ Object.assign(result, styles);
3271
+ continue;
3272
+ }
3251
3273
  const mods = key.split(":");
3252
3274
  if (matchesMods(mods, isDark, state, breakpoints)) {
3253
3275
  Object.assign(result, styles);
@@ -3555,6 +3577,9 @@ var InteractiveWrapper = (0, import_react3.forwardRef)(
3555
3577
  style: styleProp,
3556
3578
  onPressIn,
3557
3579
  onPressOut,
3580
+ onPointerDown,
3581
+ onPointerUp,
3582
+ onPointerLeave,
3558
3583
  onMouseEnter,
3559
3584
  onMouseLeave,
3560
3585
  onFocus,
@@ -3570,6 +3595,9 @@ var InteractiveWrapper = (0, import_react3.forwardRef)(
3570
3595
  const [focused, setFocused] = (0, import_react3.useState)(false);
3571
3596
  const handlePressIn = (0, import_react3.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
3572
3597
  const handlePressOut = (0, import_react3.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
3598
+ const handlePointerDown = (0, import_react3.useCallback)(chain(onPointerDown, () => setPressed(true)), [onPointerDown]);
3599
+ const handlePointerUp = (0, import_react3.useCallback)(chain(onPointerUp, () => setPressed(false)), [onPointerUp]);
3600
+ const handlePointerLeave = (0, import_react3.useCallback)(chain(onPointerLeave, () => setPressed(false)), [onPointerLeave]);
3573
3601
  const handleMouseEnter = (0, import_react3.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
3574
3602
  const handleMouseLeave = (0, import_react3.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
3575
3603
  const handleFocus = (0, import_react3.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
@@ -3617,8 +3645,9 @@ var InteractiveWrapper = (0, import_react3.forwardRef)(
3617
3645
  ...restWithoutChildren,
3618
3646
  style: finalStyle,
3619
3647
  ...isWeb && className ? { className } : {},
3620
- // onPressIn / onPressOut are React Native-only; skip on web to avoid DOM warnings.
3621
- ...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3648
+ // onPressIn / onPressOut are React Native-only.
3649
+ // On web, onPointerDown/Up drive the pressed state instead (covers mouse + touch).
3650
+ ...isWeb ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3622
3651
  onMouseEnter: handleMouseEnter,
3623
3652
  onMouseLeave: handleMouseLeave,
3624
3653
  onFocus: handleFocus,
@@ -3704,17 +3733,12 @@ var INTERACTIVE_MODS = /* @__PURE__ */ new Set([
3704
3733
  "visited",
3705
3734
  "not-visited",
3706
3735
  "placeholder",
3707
- "focus-within",
3708
- "focus-visible",
3709
3736
  "group-hover",
3710
3737
  "group-focus",
3711
3738
  "peer-hover",
3712
- "peer-focus",
3713
- "first",
3714
- "last",
3715
- "odd",
3716
- "even",
3717
- "only"
3739
+ "peer-focus"
3740
+ // focus-within / focus-visible / first / last / odd / even / only are CSS-only —
3741
+ // they have no JS state to track, so they don't need InteractiveWrapper.
3718
3742
  ]);
3719
3743
  var RESPONSIVE_MODS4 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
3720
3744
  var MODE_MODS_JSX = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-XGWS3OQD.mjs";
6
- import "./chunk-XVPIJMIR.mjs";
5
+ } from "./chunk-UR4DTYE3.mjs";
6
+ import "./chunk-D7Q7ZCOW.mjs";
7
7
  export {
8
8
  Fragment,
9
9
  jsx,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
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",