@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.
@@ -75,64 +75,64 @@ var LRUCache = class {
75
75
  // src/core/registry.ts
76
76
  var BUILTIN_MODIFIERS = {
77
77
  // ── Mode ─────────────────────────────────────────────────────────────────
78
- dark: { darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
79
- "not-light": { darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
80
- light: { darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
81
- "not-dark": { darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
78
+ dark: { order: 60, darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
79
+ "not-light": { order: 60, darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
80
+ light: { order: 60, darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
81
+ "not-dark": { order: 60, darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
82
82
  // ── Interactive — JS state + CSS pseudo ───────────────────────────────────
83
- hover: { pseudo: ":hover", jsBehavior: "interactive", jsMatch: (_, s) => !!s.hover },
84
- "not-hover": { pseudo: ":not(:hover)", jsBehavior: "interactive", jsMatch: (_, s) => !s.hover },
85
- focus: { pseudo: ":focus", jsBehavior: "interactive", jsMatch: (_, s) => !!s.focus },
86
- "not-focus": { pseudo: ":not(:focus)", jsBehavior: "interactive", jsMatch: (_, s) => !s.focus },
87
- active: { pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.active || !!s.pressed },
88
- "not-active": { pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.active && !s.pressed },
89
- pressed: { pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.pressed || !!s.active },
90
- "not-pressed": { pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.pressed && !s.active },
91
- disabled: { pseudo: ":disabled", jsBehavior: "interactive", jsMatch: (_, s) => !!s.disabled },
92
- "not-disabled": { pseudo: ":not(:disabled)", jsBehavior: "interactive", jsMatch: (_, s) => !s.disabled },
93
- checked: { pseudo: ":checked", jsBehavior: "interactive", jsMatch: (_, s) => !!s.checked },
94
- "not-checked": { pseudo: ":not(:checked)", jsBehavior: "interactive", jsMatch: (_, s) => !s.checked },
95
- visited: { pseudo: ":visited", jsBehavior: "interactive", jsMatch: (_, s) => !!s.visited },
96
- "not-visited": { pseudo: ":not(:visited)", jsBehavior: "interactive", jsMatch: (_, s) => !s.visited },
97
- placeholder: { pseudo: "::placeholder", jsBehavior: "interactive", jsMatch: (_, s) => !!s.placeholder },
83
+ hover: { order: 10, pseudo: ":hover", jsBehavior: "interactive", jsMatch: (_, s) => !!s.hover },
84
+ "not-hover": { order: 10, pseudo: ":not(:hover)", jsBehavior: "interactive", jsMatch: (_, s) => !s.hover },
85
+ focus: { order: 20, pseudo: ":focus", jsBehavior: "interactive", jsMatch: (_, s) => !!s.focus },
86
+ "not-focus": { order: 20, pseudo: ":not(:focus)", jsBehavior: "interactive", jsMatch: (_, s) => !s.focus },
87
+ active: { order: 30, pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.active || !!s.pressed },
88
+ "not-active": { order: 30, pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.active && !s.pressed },
89
+ pressed: { order: 30, pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.pressed || !!s.active },
90
+ "not-pressed": { order: 30, pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.pressed && !s.active },
91
+ disabled: { order: 40, pseudo: ":disabled", jsBehavior: "interactive", jsMatch: (_, s) => !!s.disabled },
92
+ "not-disabled": { order: 40, pseudo: ":not(:disabled)", jsBehavior: "interactive", jsMatch: (_, s) => !s.disabled },
93
+ checked: { order: 35, pseudo: ":checked", jsBehavior: "interactive", jsMatch: (_, s) => !!s.checked },
94
+ "not-checked": { order: 35, pseudo: ":not(:checked)", jsBehavior: "interactive", jsMatch: (_, s) => !s.checked },
95
+ visited: { order: 5, pseudo: ":visited", jsBehavior: "interactive", jsMatch: (_, s) => !!s.visited },
96
+ "not-visited": { order: 5, pseudo: ":not(:visited)", jsBehavior: "interactive", jsMatch: (_, s) => !s.visited },
97
+ placeholder: { order: 0, pseudo: "::placeholder", jsBehavior: "interactive", jsMatch: (_, s) => !!s.placeholder },
98
98
  // ── CSS-only pseudo-classes (structural) ──────────────────────────────────
99
- first: { pseudo: ":first-child", jsBehavior: "css-only", forcesImportant: true },
100
- last: { pseudo: ":last-child", jsBehavior: "css-only", forcesImportant: true },
101
- odd: { pseudo: ":nth-child(odd)", jsBehavior: "css-only", forcesImportant: true },
102
- even: { pseudo: ":nth-child(even)", jsBehavior: "css-only", forcesImportant: true },
103
- only: { pseudo: ":only-child", jsBehavior: "css-only", forcesImportant: true },
104
- "focus-within": { pseudo: ":focus-within", jsBehavior: "css-only", forcesImportant: true },
105
- "focus-visible": { pseudo: ":focus-visible", jsBehavior: "css-only", forcesImportant: true },
99
+ first: { order: 0, pseudo: ":first-child", jsBehavior: "css-only", forcesImportant: true },
100
+ last: { order: 0, pseudo: ":last-child", jsBehavior: "css-only", forcesImportant: true },
101
+ odd: { order: 0, pseudo: ":nth-child(odd)", jsBehavior: "css-only", forcesImportant: true },
102
+ even: { order: 0, pseudo: ":nth-child(even)", jsBehavior: "css-only", forcesImportant: true },
103
+ only: { order: 0, pseudo: ":only-child", jsBehavior: "css-only", forcesImportant: true },
104
+ "focus-within": { order: 15, pseudo: ":focus-within", jsBehavior: "css-only", forcesImportant: true },
105
+ "focus-visible": { order: 25, pseudo: ":focus-visible", jsBehavior: "css-only", forcesImportant: true },
106
106
  // ── CSS-only pseudo-elements ───────────────────────────────────────────────
107
- before: { pseudo: "::before", jsBehavior: "css-only" },
108
- after: { pseudo: "::after", jsBehavior: "css-only" },
109
- selection: { pseudo: "::selection", jsBehavior: "css-only" },
110
- "first-letter": { pseudo: "::first-letter", jsBehavior: "css-only" },
111
- "first-line": { pseudo: "::first-line", jsBehavior: "css-only" },
112
- marker: { pseudo: "::marker", jsBehavior: "css-only" },
107
+ before: { order: 0, pseudo: "::before", jsBehavior: "css-only" },
108
+ after: { order: 0, pseudo: "::after", jsBehavior: "css-only" },
109
+ selection: { order: 0, pseudo: "::selection", jsBehavior: "css-only" },
110
+ "first-letter": { order: 0, pseudo: "::first-letter", jsBehavior: "css-only" },
111
+ "first-line": { order: 0, pseudo: "::first-line", jsBehavior: "css-only" },
112
+ marker: { order: 0, pseudo: "::marker", jsBehavior: "css-only" },
113
113
  // ── Group / peer ancestor selectors (CSS-only) ─────────────────────────────
114
- "group-hover": { ancestorSelector: ".group:hover ", jsBehavior: "css-only", forcesImportant: true },
115
- "group-focus": { ancestorSelector: ".group:focus ", jsBehavior: "css-only", forcesImportant: true },
116
- "peer-hover": { ancestorSelector: ".peer:hover ~ ", jsBehavior: "css-only", forcesImportant: true },
117
- "peer-focus": { ancestorSelector: ".peer:focus ~ ", jsBehavior: "css-only", forcesImportant: true },
114
+ "group-hover": { order: 10, ancestorSelector: ".group:hover ", jsBehavior: "css-only", forcesImportant: true },
115
+ "group-focus": { order: 20, ancestorSelector: ".group:focus ", jsBehavior: "css-only", forcesImportant: true },
116
+ "peer-hover": { order: 10, ancestorSelector: ".peer:hover ~ ", jsBehavior: "css-only", forcesImportant: true },
117
+ "peer-focus": { order: 20, ancestorSelector: ".peer:focus ~ ", jsBehavior: "css-only", forcesImportant: true },
118
118
  // ── Responsive — both CSS (@media min-width) and JS (breakpoints set) ──────
119
- sm: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("sm") },
120
- md: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("md") },
121
- lg: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("lg") },
122
- xl: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("xl") },
123
- "2xl": { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("2xl") },
119
+ sm: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("sm") },
120
+ md: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("md") },
121
+ lg: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("lg") },
122
+ xl: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("xl") },
123
+ "2xl": { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("2xl") },
124
124
  // ── Print media (CSS-only) ─────────────────────────────────────────────────
125
- print: { mediaQuery: "print", jsBehavior: "css-only", forcesImportant: true },
125
+ print: { order: 70, mediaQuery: "print", jsBehavior: "css-only", forcesImportant: true },
126
126
  // ── Orientation / accessibility media (CSS-only) ───────────────────────────
127
- landscape: { mediaQuery: "(orientation: landscape)", jsBehavior: "css-only", forcesImportant: true },
128
- portrait: { mediaQuery: "(orientation: portrait)", jsBehavior: "css-only", forcesImportant: true },
129
- "motion-reduce": { mediaQuery: "(prefers-reduced-motion: reduce)", jsBehavior: "css-only", forcesImportant: true },
130
- "motion-safe": { mediaQuery: "(prefers-reduced-motion: no-preference)", jsBehavior: "css-only", forcesImportant: true },
131
- "contrast-more": { mediaQuery: "(prefers-contrast: more)", jsBehavior: "css-only", forcesImportant: true },
132
- "contrast-less": { mediaQuery: "(prefers-contrast: less)", jsBehavior: "css-only", forcesImportant: true },
127
+ landscape: { order: 70, mediaQuery: "(orientation: landscape)", jsBehavior: "css-only", forcesImportant: true },
128
+ portrait: { order: 70, mediaQuery: "(orientation: portrait)", jsBehavior: "css-only", forcesImportant: true },
129
+ "motion-reduce": { order: 70, mediaQuery: "(prefers-reduced-motion: reduce)", jsBehavior: "css-only", forcesImportant: true },
130
+ "motion-safe": { order: 70, mediaQuery: "(prefers-reduced-motion: no-preference)", jsBehavior: "css-only", forcesImportant: true },
131
+ "contrast-more": { order: 70, mediaQuery: "(prefers-contrast: more)", jsBehavior: "css-only", forcesImportant: true },
132
+ "contrast-less": { order: 70, mediaQuery: "(prefers-contrast: less)", jsBehavior: "css-only", forcesImportant: true },
133
133
  // ── Directionality (CSS-only) ──────────────────────────────────────────────
134
- rtl: { dirSelector: '[dir="rtl"] ', jsBehavior: "css-only", forcesImportant: true },
135
- ltr: { dirSelector: '[dir="ltr"] ', jsBehavior: "css-only", forcesImportant: true }
134
+ rtl: { order: 80, dirSelector: '[dir="rtl"] ', jsBehavior: "css-only", forcesImportant: true },
135
+ ltr: { order: 80, dirSelector: '[dir="ltr"] ', jsBehavior: "css-only", forcesImportant: true }
136
136
  };
137
137
  var _pluginModifiers = {};
138
138
  var _allNames = null;
@@ -169,6 +169,8 @@ function getNamedGroupPeerModifier(name) {
169
169
  const pseudo = trigger === "hover" ? ":hover" : ":focus";
170
170
  const combinator = kind === "group" ? " " : " ~ ";
171
171
  const def = {
172
+ // Matches the plain group-hover/group-focus/peer-hover/peer-focus order below.
173
+ order: trigger === "hover" ? 10 : 20,
172
174
  ancestorSelector: `.${kind}\\/${escapedName}${pseudo}${combinator}`,
173
175
  jsBehavior: "css-only",
174
176
  forcesImportant: true
@@ -203,6 +205,15 @@ function getResponsiveModifiers() {
203
205
  }
204
206
  return _responsiveNames;
205
207
  }
208
+ function getModifierOrder(bucketKey) {
209
+ if (bucketKey === "base") return -1;
210
+ let max = 0;
211
+ for (const mod of bucketKey.split(":")) {
212
+ const order = getModifier(mod)?.order ?? 0;
213
+ if (order > max) max = order;
214
+ }
215
+ return max;
216
+ }
206
217
  function matchModifier(name, isDark, state, breakpoints) {
207
218
  return getModifier(name)?.jsMatch?.(isDark, state, breakpoints) ?? false;
208
219
  }
@@ -211,6 +222,11 @@ function matchModifier(name, isDark, state, breakpoints) {
211
222
  function isModeAwareColor(v) {
212
223
  return typeof v === "object" && v !== null && "light" in v && "dark" in v;
213
224
  }
225
+ function splitColorShadeRef(ref) {
226
+ const lastDash = ref.lastIndexOf("-");
227
+ if (lastDash <= 0) return null;
228
+ return { name: ref.slice(0, lastDash), shade: ref.slice(lastDash + 1) };
229
+ }
214
230
 
215
231
  // src/core/resolvers/color.ts
216
232
  function pickModeAwareFallback(v) {
@@ -230,13 +246,11 @@ function resolveColor(value, colors, isArbitrary) {
230
246
  else if (isModeAwareColor(entry)) hex = pickModeAwareFallback(entry);
231
247
  else if (typeof entry === "object" && "6" in entry) hex = pickModeAwareFallback(entry["6"]);
232
248
  } else {
233
- const lastDash = colorPart.lastIndexOf("-");
234
- if (lastDash > 0) {
235
- const colorName = colorPart.slice(0, lastDash);
236
- const shade = colorPart.slice(lastDash + 1);
237
- const scale = colors[colorName];
238
- if (scale && typeof scale === "object" && !isModeAwareColor(scale) && shade in scale) {
239
- const shadeVal = scale[shade];
249
+ const split = splitColorShadeRef(colorPart);
250
+ if (split) {
251
+ const scale = colors[split.name];
252
+ if (scale && typeof scale === "object" && !isModeAwareColor(scale) && split.shade in scale) {
253
+ const shadeVal = scale[split.shade];
240
254
  hex = shadeVal !== void 0 ? pickModeAwareFallback(shadeVal) : null;
241
255
  }
242
256
  }
@@ -377,6 +391,7 @@ function resolveSpacing(value, negative, spacing, isArbitrary) {
377
391
  if (negative) {
378
392
  if (typeof resolved === "number") return -resolved;
379
393
  if (typeof resolved === "string") {
394
+ if (resolved.startsWith("-")) return resolved.slice(1);
380
395
  if (/^\d/.test(resolved)) return `-${resolved}`;
381
396
  if (/^(calc|var|min|max|clamp|env)\s*\(/.test(resolved)) return `calc(-1 * (${resolved}))`;
382
397
  }
@@ -555,7 +570,7 @@ function makeBorderSideResolver(widthProp, colorProp) {
555
570
  if (!value) return { [widthProp]: 1 };
556
571
  if (isArbitrary) {
557
572
  const w2 = toNativeValue(value);
558
- if (typeof w2 === "number") return { [widthProp]: w2 };
573
+ if (typeof w2 === "number") return { [widthProp]: getEffectiveIsWeb() ? value : w2 };
559
574
  return { [colorProp]: value };
560
575
  }
561
576
  const color = resolveColor(value, colors, false);
@@ -576,7 +591,7 @@ var borderResolvers = {
576
591
  if (!value) return { borderWidth: borderWidth["DEFAULT"] ?? 1 };
577
592
  if (isArbitrary) {
578
593
  const w2 = toNativeValue(value);
579
- if (typeof w2 === "number") return { borderWidth: w2 };
594
+ if (typeof w2 === "number") return { borderWidth: getEffectiveIsWeb() ? value : w2 };
580
595
  return { borderColor: value };
581
596
  }
582
597
  const color = resolveColor(value, colors, false);
@@ -775,9 +790,9 @@ var filterResolvers = {
775
790
  const v = isArbitrary ? `contrast(${value})` : `contrast(${parseFloat(value) / 100})`;
776
791
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-contrast": v, filter: FILTER_COMPOSE };
777
792
  },
778
- grayscale: ({ value }) => {
793
+ grayscale: ({ value, isArbitrary }) => {
779
794
  if (!getEffectiveIsWeb()) return null;
780
- const v = value === "0" ? "grayscale(0)" : "grayscale(100%)";
795
+ const v = isArbitrary ? `grayscale(${value})` : value === "0" ? "grayscale(0)" : "grayscale(100%)";
781
796
  return { "--kb-grayscale": v, filter: FILTER_COMPOSE };
782
797
  },
783
798
  "hue-rotate": ({ value, negative, isArbitrary }) => {
@@ -786,9 +801,9 @@ var filterResolvers = {
786
801
  if (isNaN(parseFloat(deg)) && !isArbitrary) return null;
787
802
  return { "--kb-hue-rotate": `hue-rotate(${deg})`, filter: FILTER_COMPOSE };
788
803
  },
789
- invert: ({ value }) => {
804
+ invert: ({ value, isArbitrary }) => {
790
805
  if (!getEffectiveIsWeb()) return null;
791
- const v = value === "0" ? "invert(0)" : "invert(100%)";
806
+ const v = isArbitrary ? `invert(${value})` : value === "0" ? "invert(0)" : "invert(100%)";
792
807
  return { "--kb-invert": v, filter: FILTER_COMPOSE };
793
808
  },
794
809
  saturate: ({ value, isArbitrary }) => {
@@ -796,9 +811,9 @@ var filterResolvers = {
796
811
  const v = isArbitrary ? `saturate(${value})` : `saturate(${parseFloat(value) / 100})`;
797
812
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-saturate": v, filter: FILTER_COMPOSE };
798
813
  },
799
- sepia: ({ value }) => {
814
+ sepia: ({ value, isArbitrary }) => {
800
815
  if (!getEffectiveIsWeb()) return null;
801
- const v = value === "0" ? "sepia(0)" : "sepia(100%)";
816
+ const v = isArbitrary ? `sepia(${value})` : value === "0" ? "sepia(0)" : "sepia(100%)";
802
817
  return { "--kb-sepia": v, filter: FILTER_COMPOSE };
803
818
  },
804
819
  "drop-shadow": ({ value, isArbitrary }) => {
@@ -849,9 +864,10 @@ var filterResolvers = {
849
864
  const v = isArbitrary ? `contrast(${value})` : `contrast(${parseFloat(value) / 100})`;
850
865
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-backdrop-contrast": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
851
866
  },
852
- "backdrop-grayscale": ({ value }) => {
867
+ "backdrop-grayscale": ({ value, isArbitrary }) => {
853
868
  if (!getEffectiveIsWeb()) return null;
854
- return { "--kb-backdrop-grayscale": value === "0" ? "grayscale(0)" : "grayscale(100%)", backdropFilter: BACKDROP_FILTER_COMPOSE };
869
+ const v = isArbitrary ? `grayscale(${value})` : value === "0" ? "grayscale(0)" : "grayscale(100%)";
870
+ return { "--kb-backdrop-grayscale": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
855
871
  },
856
872
  "backdrop-hue-rotate": ({ value, negative, isArbitrary }) => {
857
873
  if (!getEffectiveIsWeb()) return null;
@@ -859,9 +875,10 @@ var filterResolvers = {
859
875
  if (isNaN(parseFloat(deg)) && !isArbitrary) return null;
860
876
  return { "--kb-backdrop-hue-rotate": `hue-rotate(${deg})`, backdropFilter: BACKDROP_FILTER_COMPOSE };
861
877
  },
862
- "backdrop-invert": ({ value }) => {
878
+ "backdrop-invert": ({ value, isArbitrary }) => {
863
879
  if (!getEffectiveIsWeb()) return null;
864
- return { "--kb-backdrop-invert": value === "0" ? "invert(0)" : "invert(100%)", backdropFilter: BACKDROP_FILTER_COMPOSE };
880
+ const v = isArbitrary ? `invert(${value})` : value === "0" ? "invert(0)" : "invert(100%)";
881
+ return { "--kb-backdrop-invert": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
865
882
  },
866
883
  "backdrop-opacity": ({ value, isArbitrary }) => {
867
884
  if (!getEffectiveIsWeb()) return null;
@@ -873,9 +890,10 @@ var filterResolvers = {
873
890
  const v = isArbitrary ? `saturate(${value})` : `saturate(${parseFloat(value) / 100})`;
874
891
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-backdrop-saturate": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
875
892
  },
876
- "backdrop-sepia": ({ value }) => {
893
+ "backdrop-sepia": ({ value, isArbitrary }) => {
877
894
  if (!getEffectiveIsWeb()) return null;
878
- return { "--kb-backdrop-sepia": value === "0" ? "sepia(0)" : "sepia(100%)", backdropFilter: BACKDROP_FILTER_COMPOSE };
895
+ const v = isArbitrary ? `sepia(${value})` : value === "0" ? "sepia(0)" : "sepia(100%)";
896
+ return { "--kb-backdrop-sepia": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
879
897
  },
880
898
  "backdrop-filter": ({ value, isArbitrary }) => {
881
899
  if (!getEffectiveIsWeb()) return null;
@@ -945,8 +963,8 @@ var filterResolvers = {
945
963
 
946
964
  // src/core/resolvers/layout.ts
947
965
  var _standalone = null;
948
- function buildStandalone() {
949
- const web = getEffectiveIsWeb();
966
+ var _standaloneWeb = null;
967
+ function buildStandalone(web) {
950
968
  return {
951
969
  // Display
952
970
  // React Native only supports display:'flex'|'none'. Setting 'flex' explicitly is
@@ -961,7 +979,6 @@ function buildStandalone() {
961
979
  "inline-block": { display: web ? "inline-block" : "flex" },
962
980
  inline: { display: web ? "inline" : "flex" },
963
981
  grid: web ? { display: "grid" } : null,
964
- grd: web ? { display: "grid" } : null,
965
982
  "inline-flex": { display: web ? "inline-flex" : "flex" },
966
983
  "inline-grid": { display: web ? "inline-grid" : "flex" },
967
984
  hidden: { display: "none" },
@@ -1313,7 +1330,11 @@ function buildStandalone() {
1313
1330
  };
1314
1331
  }
1315
1332
  function getStandalone() {
1316
- if (!_standalone) _standalone = buildStandalone();
1333
+ const web = getEffectiveIsWeb();
1334
+ if (!_standalone || _standaloneWeb !== web) {
1335
+ _standalone = buildStandalone(web);
1336
+ _standaloneWeb = web;
1337
+ }
1317
1338
  return _standalone;
1318
1339
  }
1319
1340
  var layoutResolvers = {
@@ -2938,9 +2959,21 @@ function getDefaultFontFamily() {
2938
2959
  }
2939
2960
  var _styleEl = null;
2940
2961
  var _ruleIndexByKey = /* @__PURE__ */ new Map();
2962
+ var _sheetKeys = [];
2963
+ var _ruleOrderByKey = /* @__PURE__ */ new Map();
2964
+ function findInsertionIndex(order) {
2965
+ let lo = 0, hi = _sheetKeys.length;
2966
+ while (lo < hi) {
2967
+ const mid = lo + hi >>> 1;
2968
+ if (_ruleOrderByKey.get(_sheetKeys[mid]) > order) hi = mid;
2969
+ else lo = mid + 1;
2970
+ }
2971
+ return lo;
2972
+ }
2941
2973
  function evictInjectedRule(rule) {
2942
2974
  const idx = _ruleIndexByKey.get(rule);
2943
2975
  _ruleIndexByKey.delete(rule);
2976
+ _ruleOrderByKey.delete(rule);
2944
2977
  if (idx === void 0) return;
2945
2978
  const sheet = _styleEl?.sheet;
2946
2979
  if (!sheet) return;
@@ -2949,6 +2982,7 @@ function evictInjectedRule(rule) {
2949
2982
  } catch {
2950
2983
  return;
2951
2984
  }
2985
+ _sheetKeys.splice(idx, 1);
2952
2986
  for (const [key, i] of _ruleIndexByKey) {
2953
2987
  if (i > idx) _ruleIndexByKey.set(key, i - 1);
2954
2988
  }
@@ -2968,15 +3002,20 @@ function getStyleEl() {
2968
3002
  document.head.appendChild(_styleEl);
2969
3003
  return _styleEl;
2970
3004
  }
2971
- function injectRule(rule) {
3005
+ function injectRule(rule, order) {
2972
3006
  if (isRuntimeCSSDisabled()) return;
2973
3007
  if (_injectedRules.get(rule)) return;
2974
3008
  try {
2975
3009
  const sheet = getStyleEl().sheet;
2976
3010
  if (sheet) {
2977
- const idx = sheet.cssRules.length;
3011
+ const idx = findInsertionIndex(order);
2978
3012
  sheet.insertRule(rule, idx);
3013
+ _sheetKeys.splice(idx, 0, rule);
3014
+ for (const [key, i] of _ruleIndexByKey) {
3015
+ if (i >= idx) _ruleIndexByKey.set(key, i + 1);
3016
+ }
2979
3017
  _ruleIndexByKey.set(rule, idx);
3018
+ _ruleOrderByKey.set(rule, order);
2980
3019
  _injectedRules.set(rule, true);
2981
3020
  }
2982
3021
  } catch {
@@ -3168,16 +3207,23 @@ function buildClassCSSRules(cls, bucketKey, styles, darkMode, important, screens
3168
3207
  rules.push(rule);
3169
3208
  return rules;
3170
3209
  }
3171
- function injectClassRule(cls, bucketKey, styles, darkMode, important) {
3210
+ function injectClassRule(cls, bucketKey, styles, darkMode, important, order) {
3172
3211
  const cssRules = buildClassCSSRules(cls, bucketKey, styles, darkMode, important, getGlobalScreens());
3173
- for (const r of cssRules) injectRule(r);
3212
+ for (const r of cssRules) injectRule(r, order);
3213
+ return cssRules;
3174
3214
  }
3175
3215
  function resolve(classString, theme, darkMode = "attribute") {
3176
3216
  const cache = getThemeCache(theme);
3177
- const cacheKey = `${classString}::${darkMode}`;
3217
+ const cacheKey = `${classString}::${darkMode}::${getEffectiveIsWeb() ? "web" : "native"}`;
3178
3218
  const cached = cache.get(cacheKey);
3179
- if (cached) return cached;
3219
+ if (cached) {
3220
+ if (isWeb) {
3221
+ for (const { rule, order } of cached.rules) injectRule(rule, order);
3222
+ }
3223
+ return cached.result;
3224
+ }
3180
3225
  const result = {};
3226
+ const rules = [];
3181
3227
  const onWeb = isWeb;
3182
3228
  for (const parsed of parseClasses(expandModeAwareColorClasses(classString, theme.colors))) {
3183
3229
  const styles = resolveUtility(parsed, theme);
@@ -3191,10 +3237,13 @@ function resolve(classString, theme, darkMode = "attribute") {
3191
3237
  if (!result[bucketKey]) result[bucketKey] = {};
3192
3238
  Object.assign(result[bucketKey], styles);
3193
3239
  if (onWeb) {
3194
- injectClassRule(parsed.original, bucketKey, styles, darkMode, parsed.important);
3240
+ const order = getModifierOrder(bucketKey);
3241
+ for (const r of injectClassRule(parsed.original, bucketKey, styles, darkMode, parsed.important, order)) {
3242
+ rules.push({ rule: r, order });
3243
+ }
3195
3244
  }
3196
3245
  }
3197
- cache.set(cacheKey, result);
3246
+ cache.set(cacheKey, { result, rules });
3198
3247
  return result;
3199
3248
  }
3200
3249
  function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
@@ -3248,6 +3297,8 @@ function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new
3248
3297
  function clearCache() {
3249
3298
  _injectedRules.clear();
3250
3299
  _ruleIndexByKey.clear();
3300
+ _ruleOrderByKey.clear();
3301
+ _sheetKeys.length = 0;
3251
3302
  if (_styleEl) {
3252
3303
  _styleEl.remove();
3253
3304
  _styleEl = null;
@@ -3275,12 +3326,15 @@ function deepMerge(base, override) {
3275
3326
  }
3276
3327
  return result;
3277
3328
  }
3278
- var configStore = { resolved: null, listeners: /* @__PURE__ */ new Set(), customVariants: {} };
3329
+ var configStore = { resolved: null, listeners: /* @__PURE__ */ new Set() };
3279
3330
  function getConfig() {
3280
3331
  if (configStore.resolved) return configStore.resolved;
3281
3332
  configStore.resolved = buildConfig({});
3282
3333
  return configStore.resolved;
3283
3334
  }
3335
+ function resetConfig() {
3336
+ configStore.resolved = null;
3337
+ }
3284
3338
  function resolveColorRefs(colors) {
3285
3339
  const MAX_DEPTH = 5;
3286
3340
  function resolveChain(raw, side) {
@@ -3314,10 +3368,9 @@ function resolveColorRefs(colors) {
3314
3368
  const a = opacity > 1 ? opacity / 100 : opacity;
3315
3369
  return hexToRgba(baseHex, a);
3316
3370
  }
3317
- const lastDash = ref.lastIndexOf("-");
3318
- if (lastDash <= 0) return null;
3319
- const name = ref.slice(0, lastDash);
3320
- const shade = ref.slice(lastDash + 1);
3371
+ const split = splitColorShadeRef(ref);
3372
+ if (!split) return null;
3373
+ const { name, shade } = split;
3321
3374
  if (!/^\d+$/.test(shade)) return null;
3322
3375
  const entry = colors[name];
3323
3376
  if (!entry || typeof entry !== "object" || isModeAwareColor(entry)) return null;
@@ -3379,7 +3432,6 @@ function buildConfig(userConfig) {
3379
3432
  clearPluginUtilities();
3380
3433
  clearPluginModifiers();
3381
3434
  }
3382
- for (const k of Object.keys(configStore.customVariants)) delete configStore.customVariants[k];
3383
3435
  const pluginAPI = makePluginAPI(resolved.theme);
3384
3436
  for (const plugin of resolved.plugins) {
3385
3437
  plugin(pluginAPI);
@@ -3408,8 +3460,6 @@ function makePluginAPI(theme) {
3408
3460
  },
3409
3461
  addVariant(name, selectorOrDef) {
3410
3462
  const def = typeof selectorOrDef === "string" ? _selectorToModifierDef(selectorOrDef) : selectorOrDef;
3411
- const selector = typeof selectorOrDef === "string" ? selectorOrDef : selectorOrDef.pseudo ?? selectorOrDef.mediaQuery ?? selectorOrDef.ancestorSelector ?? null;
3412
- if (selector !== null && selector !== name) customVariants[name] = selector;
3413
3463
  registerModifier(name, def);
3414
3464
  },
3415
3465
  theme(path, defaultValue) {
@@ -3442,7 +3492,6 @@ function initConfig(userConfig) {
3442
3492
  if (configStore._src === userConfig) return;
3443
3493
  updateConfig(userConfig);
3444
3494
  }
3445
- var customVariants = configStore.customVariants;
3446
3495
 
3447
3496
  // src/core/darkModeStore.ts
3448
3497
  var store2 = { isDark: false, notifiedIsDark: false, subscribers: /* @__PURE__ */ new Set() };
@@ -3553,6 +3602,10 @@ function stripWebOnlyProps(s) {
3553
3602
  delete s.position;
3554
3603
  }
3555
3604
  }
3605
+ function composeNativeStyle(computed, userStyle) {
3606
+ if (!userStyle) return computed;
3607
+ return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
3608
+ }
3556
3609
 
3557
3610
  // src/web-substitute.ts
3558
3611
  var _rnToHtml = {
@@ -3609,18 +3662,17 @@ function getWebTag(type, props) {
3609
3662
  _cache.set(obj, tag);
3610
3663
  return tag;
3611
3664
  }
3612
- function getImpliedRNStyle(webTag, resolvedBase) {
3665
+ function getImpliedRNClasses(webTag, resolvedBase) {
3613
3666
  if (!webTag || !resolvedBase) return void 0;
3614
- const compensation = {};
3615
- if (resolvedBase.position === void 0) compensation.position = "relative";
3667
+ const classes = [];
3668
+ if (resolvedBase.position === void 0) classes.push("relative");
3616
3669
  const explicitDisplay = resolvedBase.display;
3617
3670
  const hasFlexItemProps = "flexGrow" in resolvedBase || "flexShrink" in resolvedBase || "flex" in resolvedBase || "gap" in resolvedBase || "columnGap" in resolvedBase || "rowGap" in resolvedBase;
3618
3671
  const willBeFlex = explicitDisplay === "flex" || explicitDisplay === void 0 && hasFlexItemProps;
3619
- if (willBeFlex) {
3620
- if (explicitDisplay === void 0) compensation.display = "flex";
3621
- if (resolvedBase.flexDirection === void 0) compensation.flexDirection = "column";
3672
+ if (willBeFlex && (explicitDisplay === void 0 || resolvedBase.flexDirection === void 0)) {
3673
+ classes.push("flex-col");
3622
3674
  }
3623
- return Object.keys(compensation).length > 0 ? compensation : void 0;
3675
+ return classes.length > 0 ? classes.join(" ") : void 0;
3624
3676
  }
3625
3677
  var _rnOnlyProps = /* @__PURE__ */ new Set([
3626
3678
  // Interaction
@@ -3664,8 +3716,9 @@ var _rnOnlyProps = /* @__PURE__ */ new Set([
3664
3716
  "lineBreakStrategyIOS",
3665
3717
  // TextInput
3666
3718
  "multiline",
3667
- "resizeMode",
3668
- // handled separately
3719
+ // NOTE: 'resizeMode' (Image's prop, not TextInput's) deliberately does NOT
3720
+ // go here — it's handled below in the isImage branch (mapped to CSS
3721
+ // object-fit). Blacklisting it here would make that branch unreachable.
3669
3722
  "blurOnSubmit",
3670
3723
  "clearButtonMode",
3671
3724
  "clearTextOnFocus",
@@ -3791,7 +3844,7 @@ function transformToWebProps(originalName, tag, props) {
3791
3844
  continue;
3792
3845
  }
3793
3846
  if (k === "keyboardType") {
3794
- if (!("type" in props) && !("secureTextEntry" in props)) {
3847
+ if (!("type" in props) && !props.secureTextEntry) {
3795
3848
  const mapped = _keyboardTypeMap[v];
3796
3849
  if (mapped) out.type = mapped;
3797
3850
  }
@@ -3807,7 +3860,7 @@ function transformToWebProps(originalName, tag, props) {
3807
3860
  }
3808
3861
  }
3809
3862
  if (isScrollable && k === "horizontal") {
3810
- if (v) pendingStyle = { display: "flex", flexDirection: "row", overflowX: "auto" };
3863
+ if (v) pendingStyle = { ...pendingStyle ?? {}, display: "flex", flexDirection: "row", overflowX: "auto" };
3811
3864
  continue;
3812
3865
  }
3813
3866
  if (k === "pointerEvents") {
@@ -3927,6 +3980,7 @@ var InteractiveWrapper = forwardRef(
3927
3980
  const needsWidth = hasResponsiveBuckets(resolvedStyle);
3928
3981
  const width = useConditionalWidth(needsWidth && !isWebPlatform);
3929
3982
  const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
3983
+ const screens = getGlobalScreens();
3930
3984
  const [pressed, setPressed] = useState(false);
3931
3985
  const [hovered, setHovered] = useState(false);
3932
3986
  const [focused, setFocused] = useState(false);
@@ -3970,10 +4024,10 @@ var InteractiveWrapper = forwardRef(
3970
4024
  if (isNonStringComponent) stripWebOnlyProps(s);
3971
4025
  return s;
3972
4026
  },
3973
- [resolvedStyle, isDark, pressed, hovered, focused, width, disabled, checked, isNonStringComponent]
4027
+ [resolvedStyle, isDark, pressed, hovered, focused, width, screens, disabled, checked, isNonStringComponent]
3974
4028
  );
3975
4029
  const skipComputedInline = isWebPlatform;
3976
- const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
4030
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : composeNativeStyle(computedStyle, styleProp);
3977
4031
  const componentProps = {
3978
4032
  ref,
3979
4033
  ...restForComponent,
@@ -4043,6 +4097,7 @@ export {
4043
4097
  flatten,
4044
4098
  clearCache,
4045
4099
  getConfig,
4100
+ resetConfig,
4046
4101
  buildConfig,
4047
4102
  onConfigChange,
4048
4103
  updateConfig,
@@ -4060,9 +4115,10 @@ export {
4060
4115
  chain,
4061
4116
  stripInternalMarkers,
4062
4117
  stripWebOnlyProps,
4118
+ composeNativeStyle,
4063
4119
  registerWebElement,
4064
4120
  getWebTag,
4065
- getImpliedRNStyle,
4121
+ getImpliedRNClasses,
4066
4122
  transformToWebProps,
4067
4123
  InteractiveWrapper
4068
4124
  };
@@ -9,6 +9,22 @@
9
9
  * Plugin authors can register custom modifiers via registerModifier().
10
10
  */
11
11
  interface ModifierDef {
12
+ /**
13
+ * Cascade priority for CSS rule ORDER — NOT specificity. Two rules that
14
+ * differ only by modifier (e.g. `.hover\:bg-blue-6:hover` and
15
+ * `.focus\:bg-red-6:focus`) have equal CSS specificity, so when both
16
+ * conditions are true at once (hovering AND focused), the winner is
17
+ * whichever rule appears LATER in the stylesheet — CSS's normal same-
18
+ * specificity tiebreak. Without a fixed priority, "later" would depend on
19
+ * encounter order (whichever class the app happens to render/scan first),
20
+ * making the winner effectively random and inconsistent across reloads/
21
+ * builds. `order` fixes that: rules are emitted/injected sorted by this
22
+ * value (ascending — higher wins ties), regardless of source order, so
23
+ * e.g. `disabled:` always beats `hover:` on the same element no matter
24
+ * which one was written first in the className or rendered first in the
25
+ * app. Omit for the default (0). See getModifierOrder() below.
26
+ */
27
+ order?: number;
12
28
  /** CSS pseudo-class or pseudo-element appended to the selector (e.g. ':hover', '::before') */
13
29
  pseudo?: string;
14
30
  /** Ancestor selector prefix INCLUDING trailing space (e.g. '.group:hover ', '.peer:focus ~ ') */
@@ -1247,14 +1263,6 @@ declare function updateConfig(userConfig: FrameworkConfig): void;
1247
1263
  * - Fast Refresh DOES re-run when kbach.config.js changes (new module → new object)
1248
1264
  */
1249
1265
  declare function initConfig(userConfig: FrameworkConfig): void;
1250
- /**
1251
- * Custom variants registered via plugins (modifier name → CSS selector template).
1252
- * A direct reference to configStore.customVariants rather than a copy — the
1253
- * object itself is only ever mutated in place (keys added in addVariant()
1254
- * above, cleared in buildConfig()), never reassigned, so this binding always
1255
- * reflects the current set.
1256
- */
1257
- declare const customVariants: Record<string, string>;
1258
1266
 
1259
1267
  /**
1260
1268
  * Module-level dark-mode singleton.
@@ -1335,4 +1343,4 @@ declare function subscribeGlobalWidth(listener: WidthListener): () => void;
1335
1343
  */
1336
1344
  declare function getActiveBreakpoints(width?: number, screens?: Record<string, number>): Set<string>;
1337
1345
 
1338
- export { BASE_RESET, type ColorShades, type ColorValue, type DarkMode, type DefaultColorName, type DefaultSpacingKey, type FrameworkConfig, LRUCache, type ParsedClass, type Platform, type PluginAPI, RESET_STYLE_ID, type ResolvedConfig, type ResolvedStyle, type StyleValue, type ThemeColors, type ThemeConfig, type ThemeMode, type ThemeSpacing, buildConfig, clearCache, clearPluginModifiers, customVariants, defaultColors, defaultTheme, disableRuntimeCSS, escapeCSSSelector, expandModeAwareColorClasses, flatten, generateClassCSS, generateKbachTypesDts, getActiveBreakpoints, getAllModifierNames, getBuiltinStandaloneNames, getBuiltinUtilityPrefixes, getConfig, getDefaultFontFamily, getEffectiveIsWeb, getGlobalDarkMode, getGlobalScreens, getGlobalWidth, getInteractiveModifiers, getModeModifiers, getModifier, getResponsiveModifiers, initConfig, isKnownModifier, isKnownUtility, isModeAwareColor, isNative, isRuntimeCSSDisabled, isWeb, kbachWarn, matchModifier, normalizeClassString, onConfigChange, parseClass, parseClasses, parseHexRgb, registerModifier, resetConfig, resolve, resolveColor, resolveSizing, resolveSpacing, resolveUtility, setDefaultFontFamily, setGlobalDarkMode, setGlobalWidth, setResolveTarget, splitClassTokens, subscribeGlobalDarkMode, subscribeGlobalWidth, syncGlobalDarkMode, syncGlobalScreens, syncGlobalWidth, toNativeValue, updateConfig };
1346
+ export { BASE_RESET, type ColorShades, type ColorValue, type DarkMode, type DefaultColorName, type DefaultSpacingKey, type FrameworkConfig, LRUCache, type ParsedClass, type Platform, type PluginAPI, RESET_STYLE_ID, type ResolvedConfig, type ResolvedStyle, type StyleValue, type ThemeColors, type ThemeConfig, type ThemeMode, type ThemeSpacing, buildConfig, clearCache, clearPluginModifiers, defaultColors, defaultTheme, disableRuntimeCSS, escapeCSSSelector, expandModeAwareColorClasses, flatten, generateClassCSS, generateKbachTypesDts, getActiveBreakpoints, getAllModifierNames, getBuiltinStandaloneNames, getBuiltinUtilityPrefixes, getConfig, getDefaultFontFamily, getEffectiveIsWeb, getGlobalDarkMode, getGlobalScreens, getGlobalWidth, getInteractiveModifiers, getModeModifiers, getModifier, getResponsiveModifiers, initConfig, isKnownModifier, isKnownUtility, isModeAwareColor, isNative, isRuntimeCSSDisabled, isWeb, kbachWarn, matchModifier, normalizeClassString, onConfigChange, parseClass, parseClasses, parseHexRgb, registerModifier, resetConfig, resolve, resolveColor, resolveSizing, resolveSpacing, resolveUtility, setDefaultFontFamily, setGlobalDarkMode, setGlobalWidth, setResolveTarget, splitClassTokens, subscribeGlobalDarkMode, subscribeGlobalWidth, syncGlobalDarkMode, syncGlobalScreens, syncGlobalWidth, toNativeValue, updateConfig };