@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.
@@ -619,6 +619,11 @@ var defaultTheme = {
619
619
  function isModeAwareColor(v) {
620
620
  return typeof v === "object" && v !== null && "light" in v && "dark" in v;
621
621
  }
622
+ function splitColorShadeRef(ref) {
623
+ const lastDash = ref.lastIndexOf("-");
624
+ if (lastDash <= 0) return null;
625
+ return { name: ref.slice(0, lastDash), shade: ref.slice(lastDash + 1) };
626
+ }
622
627
 
623
628
  // src/core/resolvers/color.ts
624
629
  function pickModeAwareFallback(v) {
@@ -638,13 +643,11 @@ function resolveColor(value, colors, isArbitrary) {
638
643
  else if (isModeAwareColor(entry)) hex = pickModeAwareFallback(entry);
639
644
  else if (typeof entry === "object" && "6" in entry) hex = pickModeAwareFallback(entry["6"]);
640
645
  } else {
641
- const lastDash = colorPart.lastIndexOf("-");
642
- if (lastDash > 0) {
643
- const colorName = colorPart.slice(0, lastDash);
644
- const shade = colorPart.slice(lastDash + 1);
645
- const scale = colors[colorName];
646
- if (scale && typeof scale === "object" && !isModeAwareColor(scale) && shade in scale) {
647
- const shadeVal = scale[shade];
646
+ const split = splitColorShadeRef(colorPart);
647
+ if (split) {
648
+ const scale = colors[split.name];
649
+ if (scale && typeof scale === "object" && !isModeAwareColor(scale) && split.shade in scale) {
650
+ const shadeVal = scale[split.shade];
648
651
  hex = shadeVal !== void 0 ? pickModeAwareFallback(shadeVal) : null;
649
652
  }
650
653
  }
@@ -785,6 +788,7 @@ function resolveSpacing(value, negative, spacing, isArbitrary) {
785
788
  if (negative) {
786
789
  if (typeof resolved === "number") return -resolved;
787
790
  if (typeof resolved === "string") {
791
+ if (resolved.startsWith("-")) return resolved.slice(1);
788
792
  if (/^\d/.test(resolved)) return `-${resolved}`;
789
793
  if (/^(calc|var|min|max|clamp|env)\s*\(/.test(resolved)) return `calc(-1 * (${resolved}))`;
790
794
  }
@@ -963,7 +967,7 @@ function makeBorderSideResolver(widthProp, colorProp) {
963
967
  if (!value) return { [widthProp]: 1 };
964
968
  if (isArbitrary) {
965
969
  const w2 = toNativeValue(value);
966
- if (typeof w2 === "number") return { [widthProp]: w2 };
970
+ if (typeof w2 === "number") return { [widthProp]: getEffectiveIsWeb() ? value : w2 };
967
971
  return { [colorProp]: value };
968
972
  }
969
973
  const color = resolveColor(value, colors, false);
@@ -984,7 +988,7 @@ var borderResolvers = {
984
988
  if (!value) return { borderWidth: borderWidth["DEFAULT"] ?? 1 };
985
989
  if (isArbitrary) {
986
990
  const w2 = toNativeValue(value);
987
- if (typeof w2 === "number") return { borderWidth: w2 };
991
+ if (typeof w2 === "number") return { borderWidth: getEffectiveIsWeb() ? value : w2 };
988
992
  return { borderColor: value };
989
993
  }
990
994
  const color = resolveColor(value, colors, false);
@@ -1183,9 +1187,9 @@ var filterResolvers = {
1183
1187
  const v = isArbitrary ? `contrast(${value})` : `contrast(${parseFloat(value) / 100})`;
1184
1188
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-contrast": v, filter: FILTER_COMPOSE };
1185
1189
  },
1186
- grayscale: ({ value }) => {
1190
+ grayscale: ({ value, isArbitrary }) => {
1187
1191
  if (!getEffectiveIsWeb()) return null;
1188
- const v = value === "0" ? "grayscale(0)" : "grayscale(100%)";
1192
+ const v = isArbitrary ? `grayscale(${value})` : value === "0" ? "grayscale(0)" : "grayscale(100%)";
1189
1193
  return { "--kb-grayscale": v, filter: FILTER_COMPOSE };
1190
1194
  },
1191
1195
  "hue-rotate": ({ value, negative, isArbitrary }) => {
@@ -1194,9 +1198,9 @@ var filterResolvers = {
1194
1198
  if (isNaN(parseFloat(deg)) && !isArbitrary) return null;
1195
1199
  return { "--kb-hue-rotate": `hue-rotate(${deg})`, filter: FILTER_COMPOSE };
1196
1200
  },
1197
- invert: ({ value }) => {
1201
+ invert: ({ value, isArbitrary }) => {
1198
1202
  if (!getEffectiveIsWeb()) return null;
1199
- const v = value === "0" ? "invert(0)" : "invert(100%)";
1203
+ const v = isArbitrary ? `invert(${value})` : value === "0" ? "invert(0)" : "invert(100%)";
1200
1204
  return { "--kb-invert": v, filter: FILTER_COMPOSE };
1201
1205
  },
1202
1206
  saturate: ({ value, isArbitrary }) => {
@@ -1204,9 +1208,9 @@ var filterResolvers = {
1204
1208
  const v = isArbitrary ? `saturate(${value})` : `saturate(${parseFloat(value) / 100})`;
1205
1209
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-saturate": v, filter: FILTER_COMPOSE };
1206
1210
  },
1207
- sepia: ({ value }) => {
1211
+ sepia: ({ value, isArbitrary }) => {
1208
1212
  if (!getEffectiveIsWeb()) return null;
1209
- const v = value === "0" ? "sepia(0)" : "sepia(100%)";
1213
+ const v = isArbitrary ? `sepia(${value})` : value === "0" ? "sepia(0)" : "sepia(100%)";
1210
1214
  return { "--kb-sepia": v, filter: FILTER_COMPOSE };
1211
1215
  },
1212
1216
  "drop-shadow": ({ value, isArbitrary }) => {
@@ -1257,9 +1261,10 @@ var filterResolvers = {
1257
1261
  const v = isArbitrary ? `contrast(${value})` : `contrast(${parseFloat(value) / 100})`;
1258
1262
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-backdrop-contrast": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
1259
1263
  },
1260
- "backdrop-grayscale": ({ value }) => {
1264
+ "backdrop-grayscale": ({ value, isArbitrary }) => {
1261
1265
  if (!getEffectiveIsWeb()) return null;
1262
- return { "--kb-backdrop-grayscale": value === "0" ? "grayscale(0)" : "grayscale(100%)", backdropFilter: BACKDROP_FILTER_COMPOSE };
1266
+ const v = isArbitrary ? `grayscale(${value})` : value === "0" ? "grayscale(0)" : "grayscale(100%)";
1267
+ return { "--kb-backdrop-grayscale": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
1263
1268
  },
1264
1269
  "backdrop-hue-rotate": ({ value, negative, isArbitrary }) => {
1265
1270
  if (!getEffectiveIsWeb()) return null;
@@ -1267,9 +1272,10 @@ var filterResolvers = {
1267
1272
  if (isNaN(parseFloat(deg)) && !isArbitrary) return null;
1268
1273
  return { "--kb-backdrop-hue-rotate": `hue-rotate(${deg})`, backdropFilter: BACKDROP_FILTER_COMPOSE };
1269
1274
  },
1270
- "backdrop-invert": ({ value }) => {
1275
+ "backdrop-invert": ({ value, isArbitrary }) => {
1271
1276
  if (!getEffectiveIsWeb()) return null;
1272
- return { "--kb-backdrop-invert": value === "0" ? "invert(0)" : "invert(100%)", backdropFilter: BACKDROP_FILTER_COMPOSE };
1277
+ const v = isArbitrary ? `invert(${value})` : value === "0" ? "invert(0)" : "invert(100%)";
1278
+ return { "--kb-backdrop-invert": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
1273
1279
  },
1274
1280
  "backdrop-opacity": ({ value, isArbitrary }) => {
1275
1281
  if (!getEffectiveIsWeb()) return null;
@@ -1281,9 +1287,10 @@ var filterResolvers = {
1281
1287
  const v = isArbitrary ? `saturate(${value})` : `saturate(${parseFloat(value) / 100})`;
1282
1288
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-backdrop-saturate": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
1283
1289
  },
1284
- "backdrop-sepia": ({ value }) => {
1290
+ "backdrop-sepia": ({ value, isArbitrary }) => {
1285
1291
  if (!getEffectiveIsWeb()) return null;
1286
- return { "--kb-backdrop-sepia": value === "0" ? "sepia(0)" : "sepia(100%)", backdropFilter: BACKDROP_FILTER_COMPOSE };
1292
+ const v = isArbitrary ? `sepia(${value})` : value === "0" ? "sepia(0)" : "sepia(100%)";
1293
+ return { "--kb-backdrop-sepia": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
1287
1294
  },
1288
1295
  "backdrop-filter": ({ value, isArbitrary }) => {
1289
1296
  if (!getEffectiveIsWeb()) return null;
@@ -1353,8 +1360,8 @@ var filterResolvers = {
1353
1360
 
1354
1361
  // src/core/resolvers/layout.ts
1355
1362
  var _standalone = null;
1356
- function buildStandalone() {
1357
- const web = getEffectiveIsWeb();
1363
+ var _standaloneWeb = null;
1364
+ function buildStandalone(web) {
1358
1365
  return {
1359
1366
  // Display
1360
1367
  // React Native only supports display:'flex'|'none'. Setting 'flex' explicitly is
@@ -1369,7 +1376,6 @@ function buildStandalone() {
1369
1376
  "inline-block": { display: web ? "inline-block" : "flex" },
1370
1377
  inline: { display: web ? "inline" : "flex" },
1371
1378
  grid: web ? { display: "grid" } : null,
1372
- grd: web ? { display: "grid" } : null,
1373
1379
  "inline-flex": { display: web ? "inline-flex" : "flex" },
1374
1380
  "inline-grid": { display: web ? "inline-grid" : "flex" },
1375
1381
  hidden: { display: "none" },
@@ -1721,7 +1727,11 @@ function buildStandalone() {
1721
1727
  };
1722
1728
  }
1723
1729
  function getStandalone() {
1724
- if (!_standalone) _standalone = buildStandalone();
1730
+ const web = getEffectiveIsWeb();
1731
+ if (!_standalone || _standaloneWeb !== web) {
1732
+ _standalone = buildStandalone(web);
1733
+ _standaloneWeb = web;
1734
+ }
1725
1735
  return _standalone;
1726
1736
  }
1727
1737
  var layoutResolvers = {
@@ -2489,64 +2499,64 @@ var LRUCache = class {
2489
2499
  // src/core/registry.ts
2490
2500
  var BUILTIN_MODIFIERS = {
2491
2501
  // ── Mode ─────────────────────────────────────────────────────────────────
2492
- dark: { darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
2493
- "not-light": { darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
2494
- light: { darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
2495
- "not-dark": { darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
2502
+ dark: { order: 60, darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
2503
+ "not-light": { order: 60, darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
2504
+ light: { order: 60, darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
2505
+ "not-dark": { order: 60, darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
2496
2506
  // ── Interactive — JS state + CSS pseudo ───────────────────────────────────
2497
- hover: { pseudo: ":hover", jsBehavior: "interactive", jsMatch: (_, s) => !!s.hover },
2498
- "not-hover": { pseudo: ":not(:hover)", jsBehavior: "interactive", jsMatch: (_, s) => !s.hover },
2499
- focus: { pseudo: ":focus", jsBehavior: "interactive", jsMatch: (_, s) => !!s.focus },
2500
- "not-focus": { pseudo: ":not(:focus)", jsBehavior: "interactive", jsMatch: (_, s) => !s.focus },
2501
- active: { pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.active || !!s.pressed },
2502
- "not-active": { pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.active && !s.pressed },
2503
- pressed: { pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.pressed || !!s.active },
2504
- "not-pressed": { pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.pressed && !s.active },
2505
- disabled: { pseudo: ":disabled", jsBehavior: "interactive", jsMatch: (_, s) => !!s.disabled },
2506
- "not-disabled": { pseudo: ":not(:disabled)", jsBehavior: "interactive", jsMatch: (_, s) => !s.disabled },
2507
- checked: { pseudo: ":checked", jsBehavior: "interactive", jsMatch: (_, s) => !!s.checked },
2508
- "not-checked": { pseudo: ":not(:checked)", jsBehavior: "interactive", jsMatch: (_, s) => !s.checked },
2509
- visited: { pseudo: ":visited", jsBehavior: "interactive", jsMatch: (_, s) => !!s.visited },
2510
- "not-visited": { pseudo: ":not(:visited)", jsBehavior: "interactive", jsMatch: (_, s) => !s.visited },
2511
- placeholder: { pseudo: "::placeholder", jsBehavior: "interactive", jsMatch: (_, s) => !!s.placeholder },
2507
+ hover: { order: 10, pseudo: ":hover", jsBehavior: "interactive", jsMatch: (_, s) => !!s.hover },
2508
+ "not-hover": { order: 10, pseudo: ":not(:hover)", jsBehavior: "interactive", jsMatch: (_, s) => !s.hover },
2509
+ focus: { order: 20, pseudo: ":focus", jsBehavior: "interactive", jsMatch: (_, s) => !!s.focus },
2510
+ "not-focus": { order: 20, pseudo: ":not(:focus)", jsBehavior: "interactive", jsMatch: (_, s) => !s.focus },
2511
+ active: { order: 30, pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.active || !!s.pressed },
2512
+ "not-active": { order: 30, pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.active && !s.pressed },
2513
+ pressed: { order: 30, pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.pressed || !!s.active },
2514
+ "not-pressed": { order: 30, pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.pressed && !s.active },
2515
+ disabled: { order: 40, pseudo: ":disabled", jsBehavior: "interactive", jsMatch: (_, s) => !!s.disabled },
2516
+ "not-disabled": { order: 40, pseudo: ":not(:disabled)", jsBehavior: "interactive", jsMatch: (_, s) => !s.disabled },
2517
+ checked: { order: 35, pseudo: ":checked", jsBehavior: "interactive", jsMatch: (_, s) => !!s.checked },
2518
+ "not-checked": { order: 35, pseudo: ":not(:checked)", jsBehavior: "interactive", jsMatch: (_, s) => !s.checked },
2519
+ visited: { order: 5, pseudo: ":visited", jsBehavior: "interactive", jsMatch: (_, s) => !!s.visited },
2520
+ "not-visited": { order: 5, pseudo: ":not(:visited)", jsBehavior: "interactive", jsMatch: (_, s) => !s.visited },
2521
+ placeholder: { order: 0, pseudo: "::placeholder", jsBehavior: "interactive", jsMatch: (_, s) => !!s.placeholder },
2512
2522
  // ── CSS-only pseudo-classes (structural) ──────────────────────────────────
2513
- first: { pseudo: ":first-child", jsBehavior: "css-only", forcesImportant: true },
2514
- last: { pseudo: ":last-child", jsBehavior: "css-only", forcesImportant: true },
2515
- odd: { pseudo: ":nth-child(odd)", jsBehavior: "css-only", forcesImportant: true },
2516
- even: { pseudo: ":nth-child(even)", jsBehavior: "css-only", forcesImportant: true },
2517
- only: { pseudo: ":only-child", jsBehavior: "css-only", forcesImportant: true },
2518
- "focus-within": { pseudo: ":focus-within", jsBehavior: "css-only", forcesImportant: true },
2519
- "focus-visible": { pseudo: ":focus-visible", jsBehavior: "css-only", forcesImportant: true },
2523
+ first: { order: 0, pseudo: ":first-child", jsBehavior: "css-only", forcesImportant: true },
2524
+ last: { order: 0, pseudo: ":last-child", jsBehavior: "css-only", forcesImportant: true },
2525
+ odd: { order: 0, pseudo: ":nth-child(odd)", jsBehavior: "css-only", forcesImportant: true },
2526
+ even: { order: 0, pseudo: ":nth-child(even)", jsBehavior: "css-only", forcesImportant: true },
2527
+ only: { order: 0, pseudo: ":only-child", jsBehavior: "css-only", forcesImportant: true },
2528
+ "focus-within": { order: 15, pseudo: ":focus-within", jsBehavior: "css-only", forcesImportant: true },
2529
+ "focus-visible": { order: 25, pseudo: ":focus-visible", jsBehavior: "css-only", forcesImportant: true },
2520
2530
  // ── CSS-only pseudo-elements ───────────────────────────────────────────────
2521
- before: { pseudo: "::before", jsBehavior: "css-only" },
2522
- after: { pseudo: "::after", jsBehavior: "css-only" },
2523
- selection: { pseudo: "::selection", jsBehavior: "css-only" },
2524
- "first-letter": { pseudo: "::first-letter", jsBehavior: "css-only" },
2525
- "first-line": { pseudo: "::first-line", jsBehavior: "css-only" },
2526
- marker: { pseudo: "::marker", jsBehavior: "css-only" },
2531
+ before: { order: 0, pseudo: "::before", jsBehavior: "css-only" },
2532
+ after: { order: 0, pseudo: "::after", jsBehavior: "css-only" },
2533
+ selection: { order: 0, pseudo: "::selection", jsBehavior: "css-only" },
2534
+ "first-letter": { order: 0, pseudo: "::first-letter", jsBehavior: "css-only" },
2535
+ "first-line": { order: 0, pseudo: "::first-line", jsBehavior: "css-only" },
2536
+ marker: { order: 0, pseudo: "::marker", jsBehavior: "css-only" },
2527
2537
  // ── Group / peer ancestor selectors (CSS-only) ─────────────────────────────
2528
- "group-hover": { ancestorSelector: ".group:hover ", jsBehavior: "css-only", forcesImportant: true },
2529
- "group-focus": { ancestorSelector: ".group:focus ", jsBehavior: "css-only", forcesImportant: true },
2530
- "peer-hover": { ancestorSelector: ".peer:hover ~ ", jsBehavior: "css-only", forcesImportant: true },
2531
- "peer-focus": { ancestorSelector: ".peer:focus ~ ", jsBehavior: "css-only", forcesImportant: true },
2538
+ "group-hover": { order: 10, ancestorSelector: ".group:hover ", jsBehavior: "css-only", forcesImportant: true },
2539
+ "group-focus": { order: 20, ancestorSelector: ".group:focus ", jsBehavior: "css-only", forcesImportant: true },
2540
+ "peer-hover": { order: 10, ancestorSelector: ".peer:hover ~ ", jsBehavior: "css-only", forcesImportant: true },
2541
+ "peer-focus": { order: 20, ancestorSelector: ".peer:focus ~ ", jsBehavior: "css-only", forcesImportant: true },
2532
2542
  // ── Responsive — both CSS (@media min-width) and JS (breakpoints set) ──────
2533
- sm: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("sm") },
2534
- md: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("md") },
2535
- lg: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("lg") },
2536
- xl: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("xl") },
2537
- "2xl": { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("2xl") },
2543
+ sm: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("sm") },
2544
+ md: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("md") },
2545
+ lg: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("lg") },
2546
+ xl: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("xl") },
2547
+ "2xl": { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("2xl") },
2538
2548
  // ── Print media (CSS-only) ─────────────────────────────────────────────────
2539
- print: { mediaQuery: "print", jsBehavior: "css-only", forcesImportant: true },
2549
+ print: { order: 70, mediaQuery: "print", jsBehavior: "css-only", forcesImportant: true },
2540
2550
  // ── Orientation / accessibility media (CSS-only) ───────────────────────────
2541
- landscape: { mediaQuery: "(orientation: landscape)", jsBehavior: "css-only", forcesImportant: true },
2542
- portrait: { mediaQuery: "(orientation: portrait)", jsBehavior: "css-only", forcesImportant: true },
2543
- "motion-reduce": { mediaQuery: "(prefers-reduced-motion: reduce)", jsBehavior: "css-only", forcesImportant: true },
2544
- "motion-safe": { mediaQuery: "(prefers-reduced-motion: no-preference)", jsBehavior: "css-only", forcesImportant: true },
2545
- "contrast-more": { mediaQuery: "(prefers-contrast: more)", jsBehavior: "css-only", forcesImportant: true },
2546
- "contrast-less": { mediaQuery: "(prefers-contrast: less)", jsBehavior: "css-only", forcesImportant: true },
2551
+ landscape: { order: 70, mediaQuery: "(orientation: landscape)", jsBehavior: "css-only", forcesImportant: true },
2552
+ portrait: { order: 70, mediaQuery: "(orientation: portrait)", jsBehavior: "css-only", forcesImportant: true },
2553
+ "motion-reduce": { order: 70, mediaQuery: "(prefers-reduced-motion: reduce)", jsBehavior: "css-only", forcesImportant: true },
2554
+ "motion-safe": { order: 70, mediaQuery: "(prefers-reduced-motion: no-preference)", jsBehavior: "css-only", forcesImportant: true },
2555
+ "contrast-more": { order: 70, mediaQuery: "(prefers-contrast: more)", jsBehavior: "css-only", forcesImportant: true },
2556
+ "contrast-less": { order: 70, mediaQuery: "(prefers-contrast: less)", jsBehavior: "css-only", forcesImportant: true },
2547
2557
  // ── Directionality (CSS-only) ──────────────────────────────────────────────
2548
- rtl: { dirSelector: '[dir="rtl"] ', jsBehavior: "css-only", forcesImportant: true },
2549
- ltr: { dirSelector: '[dir="ltr"] ', jsBehavior: "css-only", forcesImportant: true }
2558
+ rtl: { order: 80, dirSelector: '[dir="rtl"] ', jsBehavior: "css-only", forcesImportant: true },
2559
+ ltr: { order: 80, dirSelector: '[dir="ltr"] ', jsBehavior: "css-only", forcesImportant: true }
2550
2560
  };
2551
2561
  var _pluginModifiers = {};
2552
2562
  var _allNames = null;
@@ -2583,6 +2593,8 @@ function getNamedGroupPeerModifier(name) {
2583
2593
  const pseudo = trigger === "hover" ? ":hover" : ":focus";
2584
2594
  const combinator = kind === "group" ? " " : " ~ ";
2585
2595
  const def = {
2596
+ // Matches the plain group-hover/group-focus/peer-hover/peer-focus order below.
2597
+ order: trigger === "hover" ? 10 : 20,
2586
2598
  ancestorSelector: `.${kind}\\/${escapedName}${pseudo}${combinator}`,
2587
2599
  jsBehavior: "css-only",
2588
2600
  forcesImportant: true
@@ -2596,6 +2608,15 @@ function getModifier(name) {
2596
2608
  function isKnownModifier(name) {
2597
2609
  return name in BUILTIN_MODIFIERS || name in _pluginModifiers || NAMED_GROUP_PEER_RE.test(name);
2598
2610
  }
2611
+ function getModifierOrder(bucketKey) {
2612
+ if (bucketKey === "base") return -1;
2613
+ let max = 0;
2614
+ for (const mod of bucketKey.split(":")) {
2615
+ const order = getModifier(mod)?.order ?? 0;
2616
+ if (order > max) max = order;
2617
+ }
2618
+ return max;
2619
+ }
2599
2620
 
2600
2621
  // src/core/parser.ts
2601
2622
  var UNSAFE_ARBITRARY_VALUE = /[{};\\]|\/\*|\*\//;
@@ -2808,9 +2829,12 @@ function setDefaultFontFamily(font) {
2808
2829
  }
2809
2830
  var _styleEl = null;
2810
2831
  var _ruleIndexByKey = /* @__PURE__ */ new Map();
2832
+ var _sheetKeys = [];
2833
+ var _ruleOrderByKey = /* @__PURE__ */ new Map();
2811
2834
  function evictInjectedRule(rule) {
2812
2835
  const idx = _ruleIndexByKey.get(rule);
2813
2836
  _ruleIndexByKey.delete(rule);
2837
+ _ruleOrderByKey.delete(rule);
2814
2838
  if (idx === void 0) return;
2815
2839
  const sheet = _styleEl?.sheet;
2816
2840
  if (!sheet) return;
@@ -2819,6 +2843,7 @@ function evictInjectedRule(rule) {
2819
2843
  } catch {
2820
2844
  return;
2821
2845
  }
2846
+ _sheetKeys.splice(idx, 1);
2822
2847
  for (const [key, i] of _ruleIndexByKey) {
2823
2848
  if (i > idx) _ruleIndexByKey.set(key, i - 1);
2824
2849
  }
@@ -3049,7 +3074,6 @@ function deepMerge(base, override) {
3049
3074
  }
3050
3075
  return result;
3051
3076
  }
3052
- var configStore = { resolved: null, listeners: /* @__PURE__ */ new Set(), customVariants: {} };
3053
3077
  function resolveColorRefs(colors) {
3054
3078
  const MAX_DEPTH = 5;
3055
3079
  function resolveChain(raw, side) {
@@ -3083,10 +3107,9 @@ function resolveColorRefs(colors) {
3083
3107
  const a = opacity > 1 ? opacity / 100 : opacity;
3084
3108
  return hexToRgba(baseHex, a);
3085
3109
  }
3086
- const lastDash = ref.lastIndexOf("-");
3087
- if (lastDash <= 0) return null;
3088
- const name = ref.slice(0, lastDash);
3089
- const shade = ref.slice(lastDash + 1);
3110
+ const split = splitColorShadeRef(ref);
3111
+ if (!split) return null;
3112
+ const { name, shade } = split;
3090
3113
  if (!/^\d+$/.test(shade)) return null;
3091
3114
  const entry = colors[name];
3092
3115
  if (!entry || typeof entry !== "object" || isModeAwareColor(entry)) return null;
@@ -3148,7 +3171,6 @@ function buildConfig(userConfig) {
3148
3171
  clearPluginUtilities();
3149
3172
  clearPluginModifiers();
3150
3173
  }
3151
- for (const k of Object.keys(configStore.customVariants)) delete configStore.customVariants[k];
3152
3174
  const pluginAPI = makePluginAPI(resolved.theme);
3153
3175
  for (const plugin of resolved.plugins) {
3154
3176
  plugin(pluginAPI);
@@ -3177,8 +3199,6 @@ function makePluginAPI(theme) {
3177
3199
  },
3178
3200
  addVariant(name, selectorOrDef) {
3179
3201
  const def = typeof selectorOrDef === "string" ? _selectorToModifierDef(selectorOrDef) : selectorOrDef;
3180
- const selector = typeof selectorOrDef === "string" ? selectorOrDef : selectorOrDef.pseudo ?? selectorOrDef.mediaQuery ?? selectorOrDef.ancestorSelector ?? null;
3181
- if (selector !== null && selector !== name) customVariants[name] = selector;
3182
3202
  registerModifier(name, def);
3183
3203
  },
3184
3204
  theme(path, defaultValue) {
@@ -3195,7 +3215,6 @@ function makePluginAPI(theme) {
3195
3215
  }
3196
3216
  };
3197
3217
  }
3198
- var customVariants = configStore.customVariants;
3199
3218
 
3200
3219
  // src/core/generateTypesDts.ts
3201
3220
  var HEADER = `// AUTO-GENERATED by Kbach from your kbach.config.js \u2014 do not edit by hand.
@@ -3297,7 +3316,8 @@ var GROUP_ORDER = ["Layout", "Sizing", "Spacing", "Typography", "Colors", "Borde
3297
3316
  function classifyToken(token, responsiveRe) {
3298
3317
  if (/^dark:/.test(token)) return "Dark Mode";
3299
3318
  if (responsiveRe.test(token)) return "Responsive";
3300
- const base = token.replace(/^(hover|focus|active|group-hover|peer-hover):/, "");
3319
+ const parsed = parseClass(token);
3320
+ const base = parsed ? parsed.value ? `${parsed.utility}-${parsed.value}` : parsed.utility : token;
3301
3321
  for (const [label, re] of GROUPS) {
3302
3322
  if (re.test(base)) return label;
3303
3323
  }
@@ -3433,7 +3453,9 @@ function formatKbachCSS(tokenCSS, theme, responsiveRe) {
3433
3453
  if (!css) continue;
3434
3454
  const label = classifyToken(token, responsiveRe);
3435
3455
  if (!groups.has(label)) groups.set(label, []);
3436
- groups.get(label).push(prettifyCSS(applyVars(css)));
3456
+ const parsedToken = parseClass(token);
3457
+ const bucketKey = parsedToken && parsedToken.modifiers.length > 0 ? parsedToken.modifiers.join(":") : "base";
3458
+ groups.get(label).push({ order: getModifierOrder(bucketKey), css: prettifyCSS(applyVars(css)) });
3437
3459
  }
3438
3460
  const out = ["/* Generated by Kbach \u2014 do not edit */"];
3439
3461
  if (declarations.size > 0) {
@@ -3448,9 +3470,10 @@ ${prettifyCSS(globalCSS)}`);
3448
3470
  for (const label of GROUP_ORDER) {
3449
3471
  const items = groups.get(label) ?? [];
3450
3472
  if (!items.length) continue;
3473
+ const sorted = [...items].sort((a, b) => a.order - b.order);
3451
3474
  out.push(`
3452
3475
  /* ${label} */`);
3453
- out.push(items.join("\n\n"));
3476
+ out.push(sorted.map((it) => it.css).join("\n\n"));
3454
3477
  }
3455
3478
  return out.join("\n");
3456
3479
  }
@@ -3719,6 +3742,13 @@ function kbach(userConfigOrOptions) {
3719
3742
  function scanProjectCssSelectors() {
3720
3743
  for (const dir of includeDirs) scanCssSelectorsInto(join(root, dir), projectCssClasses);
3721
3744
  }
3745
+ function isWithinIncludeDirs(file) {
3746
+ const nf = normPath(file);
3747
+ return includeDirs.some((dir) => {
3748
+ const base = normPath(join(root, dir));
3749
+ return nf === base || nf.startsWith(base.endsWith("/") ? base : `${base}/`);
3750
+ });
3751
+ }
3722
3752
  function buildTokenCSSView() {
3723
3753
  const active = /* @__PURE__ */ new Set();
3724
3754
  for (const tokens of fileTokens.values()) for (const t of tokens) active.add(t);
@@ -3820,6 +3850,7 @@ ${code}`,
3820
3850
  configureServer(server) {
3821
3851
  const onAddOrUnlink = (file, isUnlink) => {
3822
3852
  if (file.includes("node_modules")) return;
3853
+ if (!isWithinIncludeDirs(file)) return;
3823
3854
  if (CSS_FILE_RE.test(file)) {
3824
3855
  if (!isUnlink) scanCssFileInto(file, projectCssClasses);
3825
3856
  return;
@@ -3841,6 +3872,7 @@ ${code}`,
3841
3872
  },
3842
3873
  handleHotUpdate({ file, server }) {
3843
3874
  if (file.includes("node_modules")) return;
3875
+ if (!isWithinIncludeDirs(file)) return;
3844
3876
  if (CSS_FILE_RE.test(file)) {
3845
3877
  scanCssFileInto(file, projectCssClasses);
3846
3878
  return;
@@ -3856,5 +3888,6 @@ ${code}`,
3856
3888
  };
3857
3889
  }
3858
3890
  export {
3891
+ formatKbachCSS,
3859
3892
  kbach
3860
3893
  };
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@kbach/react",
3
- "version": "0.6.39",
3
+ "version": "0.6.41",
4
4
  "description": "React / React Native components and hooks for the Kbach framework",
5
+ "license": "MIT",
5
6
  "repository": {
6
7
  "type": "git",
7
8
  "url": "git+https://github.com/semsakadanupol/kbach.git",
@@ -49,8 +50,12 @@
49
50
  "postinstall": "node scripts/postinstall.js"
50
51
  },
51
52
  "devDependencies": {
53
+ "@testing-library/react": "^16.3.2",
52
54
  "@types/node": "^20.19.42",
53
55
  "@types/react": "^19.2.0",
56
+ "@types/react-dom": "^19.2.4",
57
+ "jsdom": "^30.0.1",
58
+ "react-dom": "^19.2.3",
54
59
  "tsup": "^8.0.0",
55
60
  "vite": "^5.4.21",
56
61
  "vitest": "^1.0.0"