@kbach/react 0.6.37 → 0.6.40

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.
@@ -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 };
@@ -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 = {
@@ -1966,6 +1986,18 @@ var effectResolvers = {
1966
1986
  if (!isArbitrary && isNaN(parseFloat(value))) return null;
1967
1987
  const ms = isArbitrary ? value : `${value}ms`;
1968
1988
  return { transitionDelay: ms };
1989
+ },
1990
+ ease: ({ value, isArbitrary }) => {
1991
+ if (!getEffectiveIsWeb()) return null;
1992
+ if (isArbitrary) return { transitionTimingFunction: value };
1993
+ const presets = {
1994
+ linear: "linear",
1995
+ in: "cubic-bezier(0.4, 0, 1, 1)",
1996
+ out: "cubic-bezier(0, 0, 0.2, 1)",
1997
+ "in-out": "cubic-bezier(0.4, 0, 0.2, 1)"
1998
+ };
1999
+ const v = presets[value];
2000
+ return v !== void 0 ? { transitionTimingFunction: v } : null;
1969
2001
  }
1970
2002
  };
1971
2003
 
@@ -3015,9 +3047,21 @@ function getDefaultFontFamily() {
3015
3047
  }
3016
3048
  var _styleEl = null;
3017
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
+ }
3018
3061
  function evictInjectedRule(rule) {
3019
3062
  const idx = _ruleIndexByKey.get(rule);
3020
3063
  _ruleIndexByKey.delete(rule);
3064
+ _ruleOrderByKey.delete(rule);
3021
3065
  if (idx === void 0) return;
3022
3066
  const sheet = _styleEl?.sheet;
3023
3067
  if (!sheet) return;
@@ -3026,6 +3070,7 @@ function evictInjectedRule(rule) {
3026
3070
  } catch {
3027
3071
  return;
3028
3072
  }
3073
+ _sheetKeys.splice(idx, 1);
3029
3074
  for (const [key, i] of _ruleIndexByKey) {
3030
3075
  if (i > idx) _ruleIndexByKey.set(key, i - 1);
3031
3076
  }
@@ -3045,15 +3090,20 @@ function getStyleEl() {
3045
3090
  document.head.appendChild(_styleEl);
3046
3091
  return _styleEl;
3047
3092
  }
3048
- function injectRule(rule) {
3093
+ function injectRule(rule, order) {
3049
3094
  if (isRuntimeCSSDisabled()) return;
3050
3095
  if (_injectedRules.get(rule)) return;
3051
3096
  try {
3052
3097
  const sheet = getStyleEl().sheet;
3053
3098
  if (sheet) {
3054
- const idx = sheet.cssRules.length;
3099
+ const idx = findInsertionIndex(order);
3055
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
+ }
3056
3105
  _ruleIndexByKey.set(rule, idx);
3106
+ _ruleOrderByKey.set(rule, order);
3057
3107
  _injectedRules.set(rule, true);
3058
3108
  }
3059
3109
  } catch {
@@ -3245,9 +3295,10 @@ function buildClassCSSRules(cls, bucketKey, styles, darkMode, important, screens
3245
3295
  rules.push(rule);
3246
3296
  return rules;
3247
3297
  }
3248
- function injectClassRule(cls, bucketKey, styles, darkMode, important) {
3298
+ function injectClassRule(cls, bucketKey, styles, darkMode, important, order) {
3249
3299
  const cssRules = buildClassCSSRules(cls, bucketKey, styles, darkMode, important, getGlobalScreens());
3250
- for (const r of cssRules) injectRule(r);
3300
+ for (const r of cssRules) injectRule(r, order);
3301
+ return cssRules;
3251
3302
  }
3252
3303
  function generateClassCSS(classString, theme, darkMode = "attribute", screens = {}) {
3253
3304
  const rules = [];
@@ -3261,10 +3312,16 @@ function generateClassCSS(classString, theme, darkMode = "attribute", screens =
3261
3312
  }
3262
3313
  function resolve(classString, theme, darkMode = "attribute") {
3263
3314
  const cache = getThemeCache(theme);
3264
- const cacheKey = `${classString}::${darkMode}`;
3315
+ const cacheKey = `${classString}::${darkMode}::${getEffectiveIsWeb() ? "web" : "native"}`;
3265
3316
  const cached = cache.get(cacheKey);
3266
- 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
+ }
3267
3323
  const result = {};
3324
+ const rules = [];
3268
3325
  const onWeb = isWeb;
3269
3326
  for (const parsed of parseClasses(expandModeAwareColorClasses(classString, theme.colors))) {
3270
3327
  const styles = resolveUtility(parsed, theme);
@@ -3278,10 +3335,13 @@ function resolve(classString, theme, darkMode = "attribute") {
3278
3335
  if (!result[bucketKey]) result[bucketKey] = {};
3279
3336
  Object.assign(result[bucketKey], styles);
3280
3337
  if (onWeb) {
3281
- 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
+ }
3282
3342
  }
3283
3343
  }
3284
- cache.set(cacheKey, result);
3344
+ cache.set(cacheKey, { result, rules });
3285
3345
  return result;
3286
3346
  }
3287
3347
  function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
@@ -3335,6 +3395,8 @@ function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new
3335
3395
  function clearCache() {
3336
3396
  _injectedRules.clear();
3337
3397
  _ruleIndexByKey.clear();
3398
+ _ruleOrderByKey.clear();
3399
+ _sheetKeys.length = 0;
3338
3400
  if (_styleEl) {
3339
3401
  _styleEl.remove();
3340
3402
  _styleEl = null;
@@ -3362,7 +3424,7 @@ function deepMerge(base, override) {
3362
3424
  }
3363
3425
  return result;
3364
3426
  }
3365
- var configStore = { resolved: null, listeners: /* @__PURE__ */ new Set(), customVariants: {} };
3427
+ var configStore = { resolved: null, listeners: /* @__PURE__ */ new Set() };
3366
3428
  function getConfig() {
3367
3429
  if (configStore.resolved) return configStore.resolved;
3368
3430
  configStore.resolved = buildConfig({});
@@ -3404,10 +3466,9 @@ function resolveColorRefs(colors) {
3404
3466
  const a = opacity > 1 ? opacity / 100 : opacity;
3405
3467
  return hexToRgba(baseHex, a);
3406
3468
  }
3407
- const lastDash = ref.lastIndexOf("-");
3408
- if (lastDash <= 0) return null;
3409
- const name = ref.slice(0, lastDash);
3410
- const shade = ref.slice(lastDash + 1);
3469
+ const split = splitColorShadeRef(ref);
3470
+ if (!split) return null;
3471
+ const { name, shade } = split;
3411
3472
  if (!/^\d+$/.test(shade)) return null;
3412
3473
  const entry = colors[name];
3413
3474
  if (!entry || typeof entry !== "object" || isModeAwareColor(entry)) return null;
@@ -3469,7 +3530,6 @@ function buildConfig(userConfig) {
3469
3530
  clearPluginUtilities();
3470
3531
  clearPluginModifiers();
3471
3532
  }
3472
- for (const k of Object.keys(configStore.customVariants)) delete configStore.customVariants[k];
3473
3533
  const pluginAPI = makePluginAPI(resolved.theme);
3474
3534
  for (const plugin of resolved.plugins) {
3475
3535
  plugin(pluginAPI);
@@ -3498,8 +3558,6 @@ function makePluginAPI(theme) {
3498
3558
  },
3499
3559
  addVariant(name, selectorOrDef) {
3500
3560
  const def = typeof selectorOrDef === "string" ? _selectorToModifierDef(selectorOrDef) : selectorOrDef;
3501
- const selector = typeof selectorOrDef === "string" ? selectorOrDef : selectorOrDef.pseudo ?? selectorOrDef.mediaQuery ?? selectorOrDef.ancestorSelector ?? null;
3502
- if (selector !== null && selector !== name) customVariants[name] = selector;
3503
3561
  registerModifier(name, def);
3504
3562
  },
3505
3563
  theme(path, defaultValue) {
@@ -3532,7 +3590,6 @@ function initConfig(userConfig) {
3532
3590
  if (configStore._src === userConfig) return;
3533
3591
  updateConfig(userConfig);
3534
3592
  }
3535
- var customVariants = configStore.customVariants;
3536
3593
 
3537
3594
  // src/core/darkModeStore.ts
3538
3595
  var store2 = { isDark: false, notifiedIsDark: false, subscribers: /* @__PURE__ */ new Set() };
@@ -3560,7 +3617,6 @@ function subscribeGlobalDarkMode(callback) {
3560
3617
  buildConfig,
3561
3618
  clearCache,
3562
3619
  clearPluginModifiers,
3563
- customVariants,
3564
3620
  defaultColors,
3565
3621
  defaultTheme,
3566
3622
  disableRuntimeCSS,