@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.
@@ -26,7 +26,6 @@ __export(index_exports, {
26
26
  buildConfig: () => buildConfig,
27
27
  clearCache: () => clearCache,
28
28
  clearPluginModifiers: () => clearPluginModifiers,
29
- customVariants: () => customVariants,
30
29
  defaultColors: () => defaultColors,
31
30
  defaultTheme: () => defaultTheme,
32
31
  disableRuntimeCSS: () => disableRuntimeCSS,
@@ -89,6 +88,11 @@ module.exports = __toCommonJS(index_exports);
89
88
  function isModeAwareColor(v) {
90
89
  return typeof v === "object" && v !== null && "light" in v && "dark" in v;
91
90
  }
91
+ function splitColorShadeRef(ref) {
92
+ const lastDash = ref.lastIndexOf("-");
93
+ if (lastDash <= 0) return null;
94
+ return { name: ref.slice(0, lastDash), shade: ref.slice(lastDash + 1) };
95
+ }
92
96
 
93
97
  // src/core/platform.ts
94
98
  var isWeb = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
@@ -165,64 +169,64 @@ var LRUCache = class {
165
169
  // src/core/registry.ts
166
170
  var BUILTIN_MODIFIERS = {
167
171
  // ── Mode ─────────────────────────────────────────────────────────────────
168
- dark: { darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
169
- "not-light": { darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
170
- light: { darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
171
- "not-dark": { darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
172
+ dark: { order: 60, darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
173
+ "not-light": { order: 60, darkScheme: "dark", jsBehavior: "mode", jsMatch: (d) => d },
174
+ light: { order: 60, darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
175
+ "not-dark": { order: 60, darkScheme: "light", jsBehavior: "mode", jsMatch: (d) => !d },
172
176
  // ── Interactive — JS state + CSS pseudo ───────────────────────────────────
173
- hover: { pseudo: ":hover", jsBehavior: "interactive", jsMatch: (_, s) => !!s.hover },
174
- "not-hover": { pseudo: ":not(:hover)", jsBehavior: "interactive", jsMatch: (_, s) => !s.hover },
175
- focus: { pseudo: ":focus", jsBehavior: "interactive", jsMatch: (_, s) => !!s.focus },
176
- "not-focus": { pseudo: ":not(:focus)", jsBehavior: "interactive", jsMatch: (_, s) => !s.focus },
177
- active: { pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.active || !!s.pressed },
178
- "not-active": { pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.active && !s.pressed },
179
- pressed: { pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.pressed || !!s.active },
180
- "not-pressed": { pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.pressed && !s.active },
181
- disabled: { pseudo: ":disabled", jsBehavior: "interactive", jsMatch: (_, s) => !!s.disabled },
182
- "not-disabled": { pseudo: ":not(:disabled)", jsBehavior: "interactive", jsMatch: (_, s) => !s.disabled },
183
- checked: { pseudo: ":checked", jsBehavior: "interactive", jsMatch: (_, s) => !!s.checked },
184
- "not-checked": { pseudo: ":not(:checked)", jsBehavior: "interactive", jsMatch: (_, s) => !s.checked },
185
- visited: { pseudo: ":visited", jsBehavior: "interactive", jsMatch: (_, s) => !!s.visited },
186
- "not-visited": { pseudo: ":not(:visited)", jsBehavior: "interactive", jsMatch: (_, s) => !s.visited },
187
- placeholder: { pseudo: "::placeholder", jsBehavior: "interactive", jsMatch: (_, s) => !!s.placeholder },
177
+ hover: { order: 10, pseudo: ":hover", jsBehavior: "interactive", jsMatch: (_, s) => !!s.hover },
178
+ "not-hover": { order: 10, pseudo: ":not(:hover)", jsBehavior: "interactive", jsMatch: (_, s) => !s.hover },
179
+ focus: { order: 20, pseudo: ":focus", jsBehavior: "interactive", jsMatch: (_, s) => !!s.focus },
180
+ "not-focus": { order: 20, pseudo: ":not(:focus)", jsBehavior: "interactive", jsMatch: (_, s) => !s.focus },
181
+ active: { order: 30, pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.active || !!s.pressed },
182
+ "not-active": { order: 30, pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.active && !s.pressed },
183
+ pressed: { order: 30, pseudo: ":active", jsBehavior: "interactive", jsMatch: (_, s) => !!s.pressed || !!s.active },
184
+ "not-pressed": { order: 30, pseudo: ":not(:active)", jsBehavior: "interactive", jsMatch: (_, s) => !s.pressed && !s.active },
185
+ disabled: { order: 40, pseudo: ":disabled", jsBehavior: "interactive", jsMatch: (_, s) => !!s.disabled },
186
+ "not-disabled": { order: 40, pseudo: ":not(:disabled)", jsBehavior: "interactive", jsMatch: (_, s) => !s.disabled },
187
+ checked: { order: 35, pseudo: ":checked", jsBehavior: "interactive", jsMatch: (_, s) => !!s.checked },
188
+ "not-checked": { order: 35, pseudo: ":not(:checked)", jsBehavior: "interactive", jsMatch: (_, s) => !s.checked },
189
+ visited: { order: 5, pseudo: ":visited", jsBehavior: "interactive", jsMatch: (_, s) => !!s.visited },
190
+ "not-visited": { order: 5, pseudo: ":not(:visited)", jsBehavior: "interactive", jsMatch: (_, s) => !s.visited },
191
+ placeholder: { order: 0, pseudo: "::placeholder", jsBehavior: "interactive", jsMatch: (_, s) => !!s.placeholder },
188
192
  // ── CSS-only pseudo-classes (structural) ──────────────────────────────────
189
- first: { pseudo: ":first-child", jsBehavior: "css-only", forcesImportant: true },
190
- last: { pseudo: ":last-child", jsBehavior: "css-only", forcesImportant: true },
191
- odd: { pseudo: ":nth-child(odd)", jsBehavior: "css-only", forcesImportant: true },
192
- even: { pseudo: ":nth-child(even)", jsBehavior: "css-only", forcesImportant: true },
193
- only: { pseudo: ":only-child", jsBehavior: "css-only", forcesImportant: true },
194
- "focus-within": { pseudo: ":focus-within", jsBehavior: "css-only", forcesImportant: true },
195
- "focus-visible": { pseudo: ":focus-visible", jsBehavior: "css-only", forcesImportant: true },
193
+ first: { order: 0, pseudo: ":first-child", jsBehavior: "css-only", forcesImportant: true },
194
+ last: { order: 0, pseudo: ":last-child", jsBehavior: "css-only", forcesImportant: true },
195
+ odd: { order: 0, pseudo: ":nth-child(odd)", jsBehavior: "css-only", forcesImportant: true },
196
+ even: { order: 0, pseudo: ":nth-child(even)", jsBehavior: "css-only", forcesImportant: true },
197
+ only: { order: 0, pseudo: ":only-child", jsBehavior: "css-only", forcesImportant: true },
198
+ "focus-within": { order: 15, pseudo: ":focus-within", jsBehavior: "css-only", forcesImportant: true },
199
+ "focus-visible": { order: 25, pseudo: ":focus-visible", jsBehavior: "css-only", forcesImportant: true },
196
200
  // ── CSS-only pseudo-elements ───────────────────────────────────────────────
197
- before: { pseudo: "::before", jsBehavior: "css-only" },
198
- after: { pseudo: "::after", jsBehavior: "css-only" },
199
- selection: { pseudo: "::selection", jsBehavior: "css-only" },
200
- "first-letter": { pseudo: "::first-letter", jsBehavior: "css-only" },
201
- "first-line": { pseudo: "::first-line", jsBehavior: "css-only" },
202
- marker: { pseudo: "::marker", jsBehavior: "css-only" },
201
+ before: { order: 0, pseudo: "::before", jsBehavior: "css-only" },
202
+ after: { order: 0, pseudo: "::after", jsBehavior: "css-only" },
203
+ selection: { order: 0, pseudo: "::selection", jsBehavior: "css-only" },
204
+ "first-letter": { order: 0, pseudo: "::first-letter", jsBehavior: "css-only" },
205
+ "first-line": { order: 0, pseudo: "::first-line", jsBehavior: "css-only" },
206
+ marker: { order: 0, pseudo: "::marker", jsBehavior: "css-only" },
203
207
  // ── Group / peer ancestor selectors (CSS-only) ─────────────────────────────
204
- "group-hover": { ancestorSelector: ".group:hover ", jsBehavior: "css-only", forcesImportant: true },
205
- "group-focus": { ancestorSelector: ".group:focus ", jsBehavior: "css-only", forcesImportant: true },
206
- "peer-hover": { ancestorSelector: ".peer:hover ~ ", jsBehavior: "css-only", forcesImportant: true },
207
- "peer-focus": { ancestorSelector: ".peer:focus ~ ", jsBehavior: "css-only", forcesImportant: true },
208
+ "group-hover": { order: 10, ancestorSelector: ".group:hover ", jsBehavior: "css-only", forcesImportant: true },
209
+ "group-focus": { order: 20, ancestorSelector: ".group:focus ", jsBehavior: "css-only", forcesImportant: true },
210
+ "peer-hover": { order: 10, ancestorSelector: ".peer:hover ~ ", jsBehavior: "css-only", forcesImportant: true },
211
+ "peer-focus": { order: 20, ancestorSelector: ".peer:focus ~ ", jsBehavior: "css-only", forcesImportant: true },
208
212
  // ── Responsive — both CSS (@media min-width) and JS (breakpoints set) ──────
209
- sm: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("sm") },
210
- md: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("md") },
211
- lg: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("lg") },
212
- xl: { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("xl") },
213
- "2xl": { isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("2xl") },
213
+ sm: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("sm") },
214
+ md: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("md") },
215
+ lg: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("lg") },
216
+ xl: { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("xl") },
217
+ "2xl": { order: 50, isResponsive: true, jsBehavior: "responsive", jsMatch: (_, __, bp) => bp.has("2xl") },
214
218
  // ── Print media (CSS-only) ─────────────────────────────────────────────────
215
- print: { mediaQuery: "print", jsBehavior: "css-only", forcesImportant: true },
219
+ print: { order: 70, mediaQuery: "print", jsBehavior: "css-only", forcesImportant: true },
216
220
  // ── Orientation / accessibility media (CSS-only) ───────────────────────────
217
- landscape: { mediaQuery: "(orientation: landscape)", jsBehavior: "css-only", forcesImportant: true },
218
- portrait: { mediaQuery: "(orientation: portrait)", jsBehavior: "css-only", forcesImportant: true },
219
- "motion-reduce": { mediaQuery: "(prefers-reduced-motion: reduce)", jsBehavior: "css-only", forcesImportant: true },
220
- "motion-safe": { mediaQuery: "(prefers-reduced-motion: no-preference)", jsBehavior: "css-only", forcesImportant: true },
221
- "contrast-more": { mediaQuery: "(prefers-contrast: more)", jsBehavior: "css-only", forcesImportant: true },
222
- "contrast-less": { mediaQuery: "(prefers-contrast: less)", jsBehavior: "css-only", forcesImportant: true },
221
+ landscape: { order: 70, mediaQuery: "(orientation: landscape)", jsBehavior: "css-only", forcesImportant: true },
222
+ portrait: { order: 70, mediaQuery: "(orientation: portrait)", jsBehavior: "css-only", forcesImportant: true },
223
+ "motion-reduce": { order: 70, mediaQuery: "(prefers-reduced-motion: reduce)", jsBehavior: "css-only", forcesImportant: true },
224
+ "motion-safe": { order: 70, mediaQuery: "(prefers-reduced-motion: no-preference)", jsBehavior: "css-only", forcesImportant: true },
225
+ "contrast-more": { order: 70, mediaQuery: "(prefers-contrast: more)", jsBehavior: "css-only", forcesImportant: true },
226
+ "contrast-less": { order: 70, mediaQuery: "(prefers-contrast: less)", jsBehavior: "css-only", forcesImportant: true },
223
227
  // ── Directionality (CSS-only) ──────────────────────────────────────────────
224
- rtl: { dirSelector: '[dir="rtl"] ', jsBehavior: "css-only", forcesImportant: true },
225
- ltr: { dirSelector: '[dir="ltr"] ', jsBehavior: "css-only", forcesImportant: true }
228
+ rtl: { order: 80, dirSelector: '[dir="rtl"] ', jsBehavior: "css-only", forcesImportant: true },
229
+ ltr: { order: 80, dirSelector: '[dir="ltr"] ', jsBehavior: "css-only", forcesImportant: true }
226
230
  };
227
231
  var _pluginModifiers = {};
228
232
  var _allNames = null;
@@ -259,6 +263,8 @@ function getNamedGroupPeerModifier(name) {
259
263
  const pseudo = trigger === "hover" ? ":hover" : ":focus";
260
264
  const combinator = kind === "group" ? " " : " ~ ";
261
265
  const def = {
266
+ // Matches the plain group-hover/group-focus/peer-hover/peer-focus order below.
267
+ order: trigger === "hover" ? 10 : 20,
262
268
  ancestorSelector: `.${kind}\\/${escapedName}${pseudo}${combinator}`,
263
269
  jsBehavior: "css-only",
264
270
  forcesImportant: true
@@ -297,6 +303,15 @@ function getResponsiveModifiers() {
297
303
  }
298
304
  return _responsiveNames;
299
305
  }
306
+ function getModifierOrder(bucketKey) {
307
+ if (bucketKey === "base") return -1;
308
+ let max = 0;
309
+ for (const mod of bucketKey.split(":")) {
310
+ const order = getModifier(mod)?.order ?? 0;
311
+ if (order > max) max = order;
312
+ }
313
+ return max;
314
+ }
300
315
  function matchModifier(name, isDark, state, breakpoints) {
301
316
  return getModifier(name)?.jsMatch?.(isDark, state, breakpoints) ?? false;
302
317
  }
@@ -319,13 +334,11 @@ function resolveColor(value, colors, isArbitrary) {
319
334
  else if (isModeAwareColor(entry)) hex = pickModeAwareFallback(entry);
320
335
  else if (typeof entry === "object" && "6" in entry) hex = pickModeAwareFallback(entry["6"]);
321
336
  } else {
322
- const lastDash = colorPart.lastIndexOf("-");
323
- if (lastDash > 0) {
324
- const colorName = colorPart.slice(0, lastDash);
325
- const shade = colorPart.slice(lastDash + 1);
326
- const scale = colors[colorName];
327
- if (scale && typeof scale === "object" && !isModeAwareColor(scale) && shade in scale) {
328
- const shadeVal = scale[shade];
337
+ const split = splitColorShadeRef(colorPart);
338
+ if (split) {
339
+ const scale = colors[split.name];
340
+ if (scale && typeof scale === "object" && !isModeAwareColor(scale) && split.shade in scale) {
341
+ const shadeVal = scale[split.shade];
329
342
  hex = shadeVal !== void 0 ? pickModeAwareFallback(shadeVal) : null;
330
343
  }
331
344
  }
@@ -466,6 +479,7 @@ function resolveSpacing(value, negative, spacing, isArbitrary) {
466
479
  if (negative) {
467
480
  if (typeof resolved === "number") return -resolved;
468
481
  if (typeof resolved === "string") {
482
+ if (resolved.startsWith("-")) return resolved.slice(1);
469
483
  if (/^\d/.test(resolved)) return `-${resolved}`;
470
484
  if (/^(calc|var|min|max|clamp|env)\s*\(/.test(resolved)) return `calc(-1 * (${resolved}))`;
471
485
  }
@@ -644,7 +658,7 @@ function makeBorderSideResolver(widthProp, colorProp) {
644
658
  if (!value) return { [widthProp]: 1 };
645
659
  if (isArbitrary) {
646
660
  const w2 = toNativeValue(value);
647
- if (typeof w2 === "number") return { [widthProp]: w2 };
661
+ if (typeof w2 === "number") return { [widthProp]: getEffectiveIsWeb() ? value : w2 };
648
662
  return { [colorProp]: value };
649
663
  }
650
664
  const color = resolveColor(value, colors, false);
@@ -665,7 +679,7 @@ var borderResolvers = {
665
679
  if (!value) return { borderWidth: borderWidth["DEFAULT"] ?? 1 };
666
680
  if (isArbitrary) {
667
681
  const w2 = toNativeValue(value);
668
- if (typeof w2 === "number") return { borderWidth: w2 };
682
+ if (typeof w2 === "number") return { borderWidth: getEffectiveIsWeb() ? value : w2 };
669
683
  return { borderColor: value };
670
684
  }
671
685
  const color = resolveColor(value, colors, false);
@@ -864,9 +878,9 @@ var filterResolvers = {
864
878
  const v = isArbitrary ? `contrast(${value})` : `contrast(${parseFloat(value) / 100})`;
865
879
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-contrast": v, filter: FILTER_COMPOSE };
866
880
  },
867
- grayscale: ({ value }) => {
881
+ grayscale: ({ value, isArbitrary }) => {
868
882
  if (!getEffectiveIsWeb()) return null;
869
- const v = value === "0" ? "grayscale(0)" : "grayscale(100%)";
883
+ const v = isArbitrary ? `grayscale(${value})` : value === "0" ? "grayscale(0)" : "grayscale(100%)";
870
884
  return { "--kb-grayscale": v, filter: FILTER_COMPOSE };
871
885
  },
872
886
  "hue-rotate": ({ value, negative, isArbitrary }) => {
@@ -875,9 +889,9 @@ var filterResolvers = {
875
889
  if (isNaN(parseFloat(deg)) && !isArbitrary) return null;
876
890
  return { "--kb-hue-rotate": `hue-rotate(${deg})`, filter: FILTER_COMPOSE };
877
891
  },
878
- invert: ({ value }) => {
892
+ invert: ({ value, isArbitrary }) => {
879
893
  if (!getEffectiveIsWeb()) return null;
880
- const v = value === "0" ? "invert(0)" : "invert(100%)";
894
+ const v = isArbitrary ? `invert(${value})` : value === "0" ? "invert(0)" : "invert(100%)";
881
895
  return { "--kb-invert": v, filter: FILTER_COMPOSE };
882
896
  },
883
897
  saturate: ({ value, isArbitrary }) => {
@@ -885,9 +899,9 @@ var filterResolvers = {
885
899
  const v = isArbitrary ? `saturate(${value})` : `saturate(${parseFloat(value) / 100})`;
886
900
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-saturate": v, filter: FILTER_COMPOSE };
887
901
  },
888
- sepia: ({ value }) => {
902
+ sepia: ({ value, isArbitrary }) => {
889
903
  if (!getEffectiveIsWeb()) return null;
890
- const v = value === "0" ? "sepia(0)" : "sepia(100%)";
904
+ const v = isArbitrary ? `sepia(${value})` : value === "0" ? "sepia(0)" : "sepia(100%)";
891
905
  return { "--kb-sepia": v, filter: FILTER_COMPOSE };
892
906
  },
893
907
  "drop-shadow": ({ value, isArbitrary }) => {
@@ -938,9 +952,10 @@ var filterResolvers = {
938
952
  const v = isArbitrary ? `contrast(${value})` : `contrast(${parseFloat(value) / 100})`;
939
953
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-backdrop-contrast": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
940
954
  },
941
- "backdrop-grayscale": ({ value }) => {
955
+ "backdrop-grayscale": ({ value, isArbitrary }) => {
942
956
  if (!getEffectiveIsWeb()) return null;
943
- return { "--kb-backdrop-grayscale": value === "0" ? "grayscale(0)" : "grayscale(100%)", backdropFilter: BACKDROP_FILTER_COMPOSE };
957
+ const v = isArbitrary ? `grayscale(${value})` : value === "0" ? "grayscale(0)" : "grayscale(100%)";
958
+ return { "--kb-backdrop-grayscale": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
944
959
  },
945
960
  "backdrop-hue-rotate": ({ value, negative, isArbitrary }) => {
946
961
  if (!getEffectiveIsWeb()) return null;
@@ -948,9 +963,10 @@ var filterResolvers = {
948
963
  if (isNaN(parseFloat(deg)) && !isArbitrary) return null;
949
964
  return { "--kb-backdrop-hue-rotate": `hue-rotate(${deg})`, backdropFilter: BACKDROP_FILTER_COMPOSE };
950
965
  },
951
- "backdrop-invert": ({ value }) => {
966
+ "backdrop-invert": ({ value, isArbitrary }) => {
952
967
  if (!getEffectiveIsWeb()) return null;
953
- return { "--kb-backdrop-invert": value === "0" ? "invert(0)" : "invert(100%)", backdropFilter: BACKDROP_FILTER_COMPOSE };
968
+ const v = isArbitrary ? `invert(${value})` : value === "0" ? "invert(0)" : "invert(100%)";
969
+ return { "--kb-backdrop-invert": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
954
970
  },
955
971
  "backdrop-opacity": ({ value, isArbitrary }) => {
956
972
  if (!getEffectiveIsWeb()) return null;
@@ -962,9 +978,10 @@ var filterResolvers = {
962
978
  const v = isArbitrary ? `saturate(${value})` : `saturate(${parseFloat(value) / 100})`;
963
979
  return isNaN(parseFloat(value)) && !isArbitrary ? null : { "--kb-backdrop-saturate": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
964
980
  },
965
- "backdrop-sepia": ({ value }) => {
981
+ "backdrop-sepia": ({ value, isArbitrary }) => {
966
982
  if (!getEffectiveIsWeb()) return null;
967
- return { "--kb-backdrop-sepia": value === "0" ? "sepia(0)" : "sepia(100%)", backdropFilter: BACKDROP_FILTER_COMPOSE };
983
+ const v = isArbitrary ? `sepia(${value})` : value === "0" ? "sepia(0)" : "sepia(100%)";
984
+ return { "--kb-backdrop-sepia": v, backdropFilter: BACKDROP_FILTER_COMPOSE };
968
985
  },
969
986
  "backdrop-filter": ({ value, isArbitrary }) => {
970
987
  if (!getEffectiveIsWeb()) return null;
@@ -1034,8 +1051,8 @@ var filterResolvers = {
1034
1051
 
1035
1052
  // src/core/resolvers/layout.ts
1036
1053
  var _standalone = null;
1037
- function buildStandalone() {
1038
- const web = getEffectiveIsWeb();
1054
+ var _standaloneWeb = null;
1055
+ function buildStandalone(web) {
1039
1056
  return {
1040
1057
  // Display
1041
1058
  // React Native only supports display:'flex'|'none'. Setting 'flex' explicitly is
@@ -1050,7 +1067,6 @@ function buildStandalone() {
1050
1067
  "inline-block": { display: web ? "inline-block" : "flex" },
1051
1068
  inline: { display: web ? "inline" : "flex" },
1052
1069
  grid: web ? { display: "grid" } : null,
1053
- grd: web ? { display: "grid" } : null,
1054
1070
  "inline-flex": { display: web ? "inline-flex" : "flex" },
1055
1071
  "inline-grid": { display: web ? "inline-grid" : "flex" },
1056
1072
  hidden: { display: "none" },
@@ -1402,7 +1418,11 @@ function buildStandalone() {
1402
1418
  };
1403
1419
  }
1404
1420
  function getStandalone() {
1405
- if (!_standalone) _standalone = buildStandalone();
1421
+ const web = getEffectiveIsWeb();
1422
+ if (!_standalone || _standaloneWeb !== web) {
1423
+ _standalone = buildStandalone(web);
1424
+ _standaloneWeb = web;
1425
+ }
1406
1426
  return _standalone;
1407
1427
  }
1408
1428
  var layoutResolvers = {
@@ -3027,9 +3047,21 @@ function getDefaultFontFamily() {
3027
3047
  }
3028
3048
  var _styleEl = null;
3029
3049
  var _ruleIndexByKey = /* @__PURE__ */ new Map();
3050
+ var _sheetKeys = [];
3051
+ var _ruleOrderByKey = /* @__PURE__ */ new Map();
3052
+ function findInsertionIndex(order) {
3053
+ let lo = 0, hi = _sheetKeys.length;
3054
+ while (lo < hi) {
3055
+ const mid = lo + hi >>> 1;
3056
+ if (_ruleOrderByKey.get(_sheetKeys[mid]) > order) hi = mid;
3057
+ else lo = mid + 1;
3058
+ }
3059
+ return lo;
3060
+ }
3030
3061
  function evictInjectedRule(rule) {
3031
3062
  const idx = _ruleIndexByKey.get(rule);
3032
3063
  _ruleIndexByKey.delete(rule);
3064
+ _ruleOrderByKey.delete(rule);
3033
3065
  if (idx === void 0) return;
3034
3066
  const sheet = _styleEl?.sheet;
3035
3067
  if (!sheet) return;
@@ -3038,6 +3070,7 @@ function evictInjectedRule(rule) {
3038
3070
  } catch {
3039
3071
  return;
3040
3072
  }
3073
+ _sheetKeys.splice(idx, 1);
3041
3074
  for (const [key, i] of _ruleIndexByKey) {
3042
3075
  if (i > idx) _ruleIndexByKey.set(key, i - 1);
3043
3076
  }
@@ -3057,15 +3090,20 @@ function getStyleEl() {
3057
3090
  document.head.appendChild(_styleEl);
3058
3091
  return _styleEl;
3059
3092
  }
3060
- function injectRule(rule) {
3093
+ function injectRule(rule, order) {
3061
3094
  if (isRuntimeCSSDisabled()) return;
3062
3095
  if (_injectedRules.get(rule)) return;
3063
3096
  try {
3064
3097
  const sheet = getStyleEl().sheet;
3065
3098
  if (sheet) {
3066
- const idx = sheet.cssRules.length;
3099
+ const idx = findInsertionIndex(order);
3067
3100
  sheet.insertRule(rule, idx);
3101
+ _sheetKeys.splice(idx, 0, rule);
3102
+ for (const [key, i] of _ruleIndexByKey) {
3103
+ if (i >= idx) _ruleIndexByKey.set(key, i + 1);
3104
+ }
3068
3105
  _ruleIndexByKey.set(rule, idx);
3106
+ _ruleOrderByKey.set(rule, order);
3069
3107
  _injectedRules.set(rule, true);
3070
3108
  }
3071
3109
  } catch {
@@ -3257,9 +3295,10 @@ function buildClassCSSRules(cls, bucketKey, styles, darkMode, important, screens
3257
3295
  rules.push(rule);
3258
3296
  return rules;
3259
3297
  }
3260
- function injectClassRule(cls, bucketKey, styles, darkMode, important) {
3298
+ function injectClassRule(cls, bucketKey, styles, darkMode, important, order) {
3261
3299
  const cssRules = buildClassCSSRules(cls, bucketKey, styles, darkMode, important, getGlobalScreens());
3262
- for (const r of cssRules) injectRule(r);
3300
+ for (const r of cssRules) injectRule(r, order);
3301
+ return cssRules;
3263
3302
  }
3264
3303
  function generateClassCSS(classString, theme, darkMode = "attribute", screens = {}) {
3265
3304
  const rules = [];
@@ -3273,10 +3312,16 @@ function generateClassCSS(classString, theme, darkMode = "attribute", screens =
3273
3312
  }
3274
3313
  function resolve(classString, theme, darkMode = "attribute") {
3275
3314
  const cache = getThemeCache(theme);
3276
- const cacheKey = `${classString}::${darkMode}`;
3315
+ const cacheKey = `${classString}::${darkMode}::${getEffectiveIsWeb() ? "web" : "native"}`;
3277
3316
  const cached = cache.get(cacheKey);
3278
- if (cached) return cached;
3317
+ if (cached) {
3318
+ if (isWeb) {
3319
+ for (const { rule, order } of cached.rules) injectRule(rule, order);
3320
+ }
3321
+ return cached.result;
3322
+ }
3279
3323
  const result = {};
3324
+ const rules = [];
3280
3325
  const onWeb = isWeb;
3281
3326
  for (const parsed of parseClasses(expandModeAwareColorClasses(classString, theme.colors))) {
3282
3327
  const styles = resolveUtility(parsed, theme);
@@ -3290,10 +3335,13 @@ function resolve(classString, theme, darkMode = "attribute") {
3290
3335
  if (!result[bucketKey]) result[bucketKey] = {};
3291
3336
  Object.assign(result[bucketKey], styles);
3292
3337
  if (onWeb) {
3293
- injectClassRule(parsed.original, bucketKey, styles, darkMode, parsed.important);
3338
+ const order = getModifierOrder(bucketKey);
3339
+ for (const r of injectClassRule(parsed.original, bucketKey, styles, darkMode, parsed.important, order)) {
3340
+ rules.push({ rule: r, order });
3341
+ }
3294
3342
  }
3295
3343
  }
3296
- cache.set(cacheKey, result);
3344
+ cache.set(cacheKey, { result, rules });
3297
3345
  return result;
3298
3346
  }
3299
3347
  function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
@@ -3347,6 +3395,8 @@ function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new
3347
3395
  function clearCache() {
3348
3396
  _injectedRules.clear();
3349
3397
  _ruleIndexByKey.clear();
3398
+ _ruleOrderByKey.clear();
3399
+ _sheetKeys.length = 0;
3350
3400
  if (_styleEl) {
3351
3401
  _styleEl.remove();
3352
3402
  _styleEl = null;
@@ -3374,7 +3424,7 @@ function deepMerge(base, override) {
3374
3424
  }
3375
3425
  return result;
3376
3426
  }
3377
- var configStore = { resolved: null, listeners: /* @__PURE__ */ new Set(), customVariants: {} };
3427
+ var configStore = { resolved: null, listeners: /* @__PURE__ */ new Set() };
3378
3428
  function getConfig() {
3379
3429
  if (configStore.resolved) return configStore.resolved;
3380
3430
  configStore.resolved = buildConfig({});
@@ -3416,10 +3466,9 @@ function resolveColorRefs(colors) {
3416
3466
  const a = opacity > 1 ? opacity / 100 : opacity;
3417
3467
  return hexToRgba(baseHex, a);
3418
3468
  }
3419
- const lastDash = ref.lastIndexOf("-");
3420
- if (lastDash <= 0) return null;
3421
- const name = ref.slice(0, lastDash);
3422
- const shade = ref.slice(lastDash + 1);
3469
+ const split = splitColorShadeRef(ref);
3470
+ if (!split) return null;
3471
+ const { name, shade } = split;
3423
3472
  if (!/^\d+$/.test(shade)) return null;
3424
3473
  const entry = colors[name];
3425
3474
  if (!entry || typeof entry !== "object" || isModeAwareColor(entry)) return null;
@@ -3481,7 +3530,6 @@ function buildConfig(userConfig) {
3481
3530
  clearPluginUtilities();
3482
3531
  clearPluginModifiers();
3483
3532
  }
3484
- for (const k of Object.keys(configStore.customVariants)) delete configStore.customVariants[k];
3485
3533
  const pluginAPI = makePluginAPI(resolved.theme);
3486
3534
  for (const plugin of resolved.plugins) {
3487
3535
  plugin(pluginAPI);
@@ -3510,8 +3558,6 @@ function makePluginAPI(theme) {
3510
3558
  },
3511
3559
  addVariant(name, selectorOrDef) {
3512
3560
  const def = typeof selectorOrDef === "string" ? _selectorToModifierDef(selectorOrDef) : selectorOrDef;
3513
- const selector = typeof selectorOrDef === "string" ? selectorOrDef : selectorOrDef.pseudo ?? selectorOrDef.mediaQuery ?? selectorOrDef.ancestorSelector ?? null;
3514
- if (selector !== null && selector !== name) customVariants[name] = selector;
3515
3561
  registerModifier(name, def);
3516
3562
  },
3517
3563
  theme(path, defaultValue) {
@@ -3544,7 +3590,6 @@ function initConfig(userConfig) {
3544
3590
  if (configStore._src === userConfig) return;
3545
3591
  updateConfig(userConfig);
3546
3592
  }
3547
- var customVariants = configStore.customVariants;
3548
3593
 
3549
3594
  // src/core/darkModeStore.ts
3550
3595
  var store2 = { isDark: false, notifiedIsDark: false, subscribers: /* @__PURE__ */ new Set() };
@@ -3572,7 +3617,6 @@ function subscribeGlobalDarkMode(callback) {
3572
3617
  buildConfig,
3573
3618
  clearCache,
3574
3619
  clearPluginModifiers,
3575
- customVariants,
3576
3620
  defaultColors,
3577
3621
  defaultTheme,
3578
3622
  disableRuntimeCSS,
package/dist/index.js CHANGED
@@ -184,6 +184,7 @@ var _mountedProviderCount = 0;
184
184
  var _warnedMultipleProviders = false;
185
185
  var _mountedConfigOverrideCount = 0;
186
186
  var _warnedConfigOverride = false;
187
+ var _warnedMediaModeToggle = false;
187
188
  function loadPersistedMode() {
188
189
  try {
189
190
  if (import_core2.isWeb) return localStorage.getItem(STORAGE_KEY);
@@ -262,10 +263,13 @@ function ThemeProvider({
262
263
  return () => {
263
264
  _mountedProviderCount--;
264
265
  if (configOverride) _mountedConfigOverrideCount--;
266
+ if (_prevConfigOverrideRef.current !== void 0 && _mountedProviderCount === 0) {
267
+ (0, import_core2.resetConfig)();
268
+ }
265
269
  };
266
270
  }, []);
267
271
  const [webWidth, setWebWidth] = (0, import_react5.useState)(0);
268
- const effectiveWidth = windowWidthProp ?? (import_core2.isWeb ? webWidth : 0);
272
+ const effectiveWidth = (0, import_core2.getEffectiveIsWeb)() ? webWidth : windowWidthProp ?? 0;
269
273
  const numericScreens = (0, import_react5.useMemo)(() => {
270
274
  const out = {};
271
275
  for (const [k, v] of Object.entries(resolvedConfig.theme.screens ?? {})) {
@@ -309,7 +313,7 @@ function ThemeProvider({
309
313
  getSystemScheme,
310
314
  getSystemSchemeServerSnapshot
311
315
  );
312
- const systemScheme = import_core2.isWeb ? webScheme : colorScheme === "dark" ? "dark" : "light";
316
+ const systemScheme = (0, import_core2.getEffectiveIsWeb)() ? webScheme : colorScheme === "dark" ? "dark" : "light";
313
317
  const [mode, _setMode] = (0, import_react5.useState)(defaultMode);
314
318
  (0, import_react5.useEffect)(() => {
315
319
  if (disablePersistence) return;
@@ -328,9 +332,15 @@ function ThemeProvider({
328
332
  return () => window.removeEventListener("storage", onStorage);
329
333
  }, [disablePersistence]);
330
334
  const setMode = (0, import_react5.useCallback)((next) => {
335
+ if (process.env.NODE_ENV !== "production" && import_core2.isWeb && resolvedConfig.darkMode === "media" && !_warnedMediaModeToggle) {
336
+ _warnedMediaModeToggle = true;
337
+ (0, import_core2.kbachWarn)(
338
+ 'setMode()/toggle() was called while darkMode is "media" \u2014 the resolved mode/isDark from useTheme()/useIsDark() will update, but the actual CSS stays driven by prefers-color-scheme and will not change to match. Use darkMode: "attribute" or "class" if the app needs manual toggling.'
339
+ );
340
+ }
331
341
  _setMode(next);
332
342
  if (!disablePersistence) persistMode(next);
333
- }, [disablePersistence]);
343
+ }, [disablePersistence, resolvedConfig.darkMode]);
334
344
  const toggle = (0, import_react5.useCallback)(() => {
335
345
  setMode(mode === "dark" || mode === "system" && systemScheme === "dark" ? "light" : "dark");
336
346
  }, [mode, systemScheme, setMode]);
@@ -499,6 +509,10 @@ function stripWebOnlyProps(s) {
499
509
  delete s.position;
500
510
  }
501
511
  }
512
+ function composeNativeStyle(computed, userStyle) {
513
+ if (!userStyle) return computed;
514
+ return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
515
+ }
502
516
 
503
517
  // src/web-substitute.ts
504
518
  var _rnToHtml = {
@@ -555,18 +569,17 @@ function getWebTag(type, props) {
555
569
  _cache.set(obj, tag);
556
570
  return tag;
557
571
  }
558
- function getImpliedRNStyle(webTag, resolvedBase) {
572
+ function getImpliedRNClasses(webTag, resolvedBase) {
559
573
  if (!webTag || !resolvedBase) return void 0;
560
- const compensation = {};
561
- if (resolvedBase.position === void 0) compensation.position = "relative";
574
+ const classes = [];
575
+ if (resolvedBase.position === void 0) classes.push("relative");
562
576
  const explicitDisplay = resolvedBase.display;
563
577
  const hasFlexItemProps = "flexGrow" in resolvedBase || "flexShrink" in resolvedBase || "flex" in resolvedBase || "gap" in resolvedBase || "columnGap" in resolvedBase || "rowGap" in resolvedBase;
564
578
  const willBeFlex = explicitDisplay === "flex" || explicitDisplay === void 0 && hasFlexItemProps;
565
- if (willBeFlex) {
566
- if (explicitDisplay === void 0) compensation.display = "flex";
567
- if (resolvedBase.flexDirection === void 0) compensation.flexDirection = "column";
579
+ if (willBeFlex && (explicitDisplay === void 0 || resolvedBase.flexDirection === void 0)) {
580
+ classes.push("flex-col");
568
581
  }
569
- return Object.keys(compensation).length > 0 ? compensation : void 0;
582
+ return classes.length > 0 ? classes.join(" ") : void 0;
570
583
  }
571
584
  var _rnOnlyProps = /* @__PURE__ */ new Set([
572
585
  // Interaction
@@ -610,8 +623,9 @@ var _rnOnlyProps = /* @__PURE__ */ new Set([
610
623
  "lineBreakStrategyIOS",
611
624
  // TextInput
612
625
  "multiline",
613
- "resizeMode",
614
- // handled separately
626
+ // NOTE: 'resizeMode' (Image's prop, not TextInput's) deliberately does NOT
627
+ // go here — it's handled below in the isImage branch (mapped to CSS
628
+ // object-fit). Blacklisting it here would make that branch unreachable.
615
629
  "blurOnSubmit",
616
630
  "clearButtonMode",
617
631
  "clearTextOnFocus",
@@ -737,7 +751,7 @@ function transformToWebProps(originalName, tag, props) {
737
751
  continue;
738
752
  }
739
753
  if (k === "keyboardType") {
740
- if (!("type" in props) && !("secureTextEntry" in props)) {
754
+ if (!("type" in props) && !props.secureTextEntry) {
741
755
  const mapped = _keyboardTypeMap[v];
742
756
  if (mapped) out.type = mapped;
743
757
  }
@@ -753,7 +767,7 @@ function transformToWebProps(originalName, tag, props) {
753
767
  }
754
768
  }
755
769
  if (isScrollable && k === "horizontal") {
756
- if (v) pendingStyle = { display: "flex", flexDirection: "row", overflowX: "auto" };
770
+ if (v) pendingStyle = { ...pendingStyle ?? {}, display: "flex", flexDirection: "row", overflowX: "auto" };
757
771
  continue;
758
772
  }
759
773
  if (k === "pointerEvents") {
@@ -856,23 +870,22 @@ function styled(Component, baseClasses = "") {
856
870
  if (!isWebPlatform) setFocused(false);
857
871
  }), [onBlur, isWebPlatform]);
858
872
  const combined = extraClasses ? `${baseClasses} ${extraClasses}` : baseClasses;
859
- const resolved = (0, import_react6.useMemo)(() => (0, import_core7.resolve)(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
873
+ const firstPassResolved = (0, import_react6.useMemo)(() => (0, import_core7.resolve)(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
874
+ const webTag = isWebPlatform ? getWebTag(Component, rest) : null;
875
+ const impliedClasses = getImpliedRNClasses(webTag, firstPassResolved.base);
876
+ const finalClassStr = impliedClasses ? `${combined} ${impliedClasses}` : combined;
877
+ const resolved = (0, import_react6.useMemo)(() => (0, import_core7.resolve)(finalClassStr, config.theme, config.darkMode), [finalClassStr, config.theme, config.darkMode]);
860
878
  const hasInteractive = (0, import_react6.useMemo)(() => hasInteractiveBuckets(resolved), [resolved]);
861
879
  const needsWidth = hasResponsiveBuckets(resolved);
862
880
  const width = useConditionalWidth(needsWidth && !isWebPlatform);
863
881
  const breakpoints = needsWidth ? (0, import_core7.getActiveBreakpoints)(width) : EMPTY_BREAKPOINTS;
882
+ const screens = (0, import_core7.getGlobalScreens)();
864
883
  const computedStyle = (0, import_react6.useMemo)(
865
884
  () => isWebPlatform ? {} : (0, import_core7.flatten)(resolved, isDark, { pressed, hover: hovered, focus: focused, disabled, checked }, breakpoints),
866
- [resolved, isDark, pressed, hovered, focused, disabled, checked, width]
885
+ [resolved, isDark, pressed, hovered, focused, disabled, checked, width, screens]
867
886
  // eslint-disable-line react-hooks/exhaustive-deps
868
887
  );
869
- const webTag = isWebPlatform ? getWebTag(Component, rest) : null;
870
- const finalStyle = isWebPlatform ? (() => {
871
- const flatStyleProp = (Array.isArray(styleProp) ? Object.assign({}, ...styleProp) : styleProp) ?? void 0;
872
- const compensation = getImpliedRNStyle(webTag, resolved.base);
873
- if (!compensation) return flatStyleProp;
874
- return flatStyleProp ? { ...compensation, ...flatStyleProp } : compensation;
875
- })() : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
888
+ const finalStyle = isWebPlatform ? (Array.isArray(styleProp) ? Object.assign({}, ...styleProp) : styleProp) ?? void 0 : composeNativeStyle(computedStyle, styleProp);
876
889
  const effectiveComponent = webTag ?? Component;
877
890
  const componentName2 = Component.displayName ?? Component.name ?? "";
878
891
  const effectiveRest = webTag && componentName2 ? transformToWebProps(componentName2, webTag, rest) : rest;
@@ -881,7 +894,7 @@ function styled(Component, baseClasses = "") {
881
894
  ...effectiveRest,
882
895
  style: finalStyle,
883
896
  // className lets injected CSS rules (group-hover:, before:, print:, etc.) match the element.
884
- ...isWebPlatform && combined ? { className: (0, import_core7.normalizeClassString)(combined) } : {},
897
+ ...isWebPlatform && finalClassStr ? { className: (0, import_core7.normalizeClassString)(finalClassStr) } : {},
885
898
  // Only attach state-tracking handlers when interactive modifiers are present.
886
899
  // Always forward user-provided handlers to avoid silently swallowing them.
887
900
  // Gated on isNative, not isWeb: isWeb is false during SSR too (no `window` there),
@@ -1012,6 +1025,7 @@ var InteractiveWrapper = (0, import_react9.forwardRef)(
1012
1025
  const needsWidth = hasResponsiveBuckets(resolvedStyle);
1013
1026
  const width = useConditionalWidth(needsWidth && !isWebPlatform);
1014
1027
  const breakpoints = needsWidth ? (0, import_core10.getActiveBreakpoints)(width) : EMPTY_BREAKPOINTS;
1028
+ const screens = (0, import_core10.getGlobalScreens)();
1015
1029
  const [pressed, setPressed] = (0, import_react9.useState)(false);
1016
1030
  const [hovered, setHovered] = (0, import_react9.useState)(false);
1017
1031
  const [focused, setFocused] = (0, import_react9.useState)(false);
@@ -1055,10 +1069,10 @@ var InteractiveWrapper = (0, import_react9.forwardRef)(
1055
1069
  if (isNonStringComponent) stripWebOnlyProps(s);
1056
1070
  return s;
1057
1071
  },
1058
- [resolvedStyle, isDark, pressed, hovered, focused, width, disabled, checked, isNonStringComponent]
1072
+ [resolvedStyle, isDark, pressed, hovered, focused, width, screens, disabled, checked, isNonStringComponent]
1059
1073
  );
1060
1074
  const skipComputedInline = isWebPlatform;
1061
- const finalStyle = skipComputedInline ? styleProp ?? void 0 : styleProp ? Array.isArray(styleProp) ? [computedStyle, ...styleProp] : [computedStyle, styleProp] : computedStyle;
1075
+ const finalStyle = skipComputedInline ? styleProp ?? void 0 : composeNativeStyle(computedStyle, styleProp);
1062
1076
  const componentProps = {
1063
1077
  ref,
1064
1078
  ...restForComponent,