@barocss/kit 0.5.0 → 0.7.0

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.
package/dist/index.d.ts CHANGED
@@ -181,6 +181,8 @@ export declare type AstNode = {
181
181
 
182
182
  export declare function comment(text: string, source?: string): AstNode;
183
183
 
184
+ export declare function compareKeys(a: RuleKey, b: RuleKey): number;
185
+
184
186
  export declare interface Config {
185
187
  prefix?: string;
186
188
  cssVarPrefix?: string;
@@ -217,6 +219,15 @@ export declare type AstNode = {
217
219
  * clear caches that belong to another context.
218
220
  */
219
221
  clearCacheOnContextChange?: boolean;
222
+ /**
223
+ * #287: static custom utilities, the runtime mirror of a stylesheet's static `@utility name { ... }`.
224
+ * Name → declarations (property → value; CSS custom properties allowed). Each one is registered on
225
+ * this context only; variants and `!` apply as usual. A name equal to a built-in extends it like
226
+ * `@utility` in Tailwind 4: the built-in declarations are emitted first, then the custom ones (a later
227
+ * duplicate property wins). An entry with an invalid name, property or value is skipped whole.
228
+ * @example utilities: { 'max-w-app': { 'max-width': '72rem', 'margin-inline': 'auto' } }
229
+ */
230
+ utilities?: CustomUtilities;
220
231
  [key: string]: unknown;
221
232
  }
222
233
 
@@ -235,6 +246,12 @@ export declare type AstNode = {
235
246
 
236
247
  export declare function createContext(configObj: Config): Context;
237
248
 
249
+ /** #287: static custom utilities by class name. */
250
+ export declare type CustomUtilities = Record<string, CustomUtilityDeclarations>;
251
+
252
+ /** #287: declarations of one static custom utility (property → value). */
253
+ export declare type CustomUtilityDeclarations = Record<string, string | number>;
254
+
238
255
  export declare function decl(prop: string, value: string | [string, string][], source?: string): AstNode;
239
256
 
240
257
  export declare type DeclPath = PathNode[];
@@ -268,20 +285,6 @@ export declare type AstNode = {
268
285
 
269
286
  export declare function functionalModifier(match: ModifierRegistration['match'], modifySelector: ModifierRegistration['modifySelector'], wrap?: ModifierRegistration['wrap'], options?: Partial<ModifierRegistration>, ctx?: Context): void;
270
287
 
271
- /**
272
- * functionalUtility: A helper that registers dynamic utilities (theme, arbitrary, custom, negative, fraction, etc.) directly
273
- *
274
- * Example:
275
- * functionalUtility({
276
- * name: 'z',
277
- * supportsNegative: true,
278
- * themeKeys: ['--z-index'],
279
- * handleBareValue: ({ value }) => isPositiveInteger(value) ? value : null,
280
- * handle: (value) => [decl('z-index', value)],
281
- * description: 'z-index utility',
282
- * category: 'layout',
283
- * });
284
- */
285
288
  export declare function functionalUtility(opts: FunctionalUtilityOptions, ctx?: Context): void;
286
289
 
287
290
  export declare type FunctionalUtilityExtra = {
@@ -420,6 +423,12 @@ export declare type AstNode = {
420
423
  * handleNegativeBareValue: ({ value }) => isPositiveInteger(value) ? value : null,
421
424
  * ```
422
425
  */
426
+ /**
427
+ * #261: the utility uses the spacing scale, so a named `theme.spacing` key (`p-gutter`) resolves to
428
+ * `var(--spacing-<key>)` (negative: `calc(var(--spacing-<key>) * -1)`), as in Tailwind 4. Only tried
429
+ * after the bare-value handler rejects the value, so built-in keywords keep precedence.
430
+ */
431
+ spacingKeys?: boolean;
423
432
  handleNegativeBareValue?: (args: {
424
433
  value: string;
425
434
  ctx: Context;
@@ -552,6 +561,13 @@ export declare type AstNode = {
552
561
 
553
562
  export declare function getUtility(ctx?: Context): UtilityRegistration[];
554
563
 
564
+ /**
565
+ * #273: true when an emitted selector or at-rule prelude contains a comment opener or closer outside a CSS escape.
566
+ * Backslash-escape pairs are skipped, so an escaped `\/` or `\*` from a class name never counts. Used by the
567
+ * serializer on the final, composed string, where adjacent pieces that were each safe alone can join into one.
568
+ */
569
+ export declare function hasCommentDelimiter(text: string): boolean;
570
+
555
571
  /**
556
572
  * #224: true when a utility value (or a whole utility token) cannot change the structure of the declaration block it
557
573
  * is pasted into. Rejects, outside quotes: `{`, `}`, `;`, unbalanced or mismatched ()/[], and a quote left open.
@@ -771,6 +787,13 @@ export declare type AstNode = {
771
787
  * @param cls - The CSS class name to mark as processed
772
788
  */
773
789
  markProcessed(cls: string): void;
790
+ /**
791
+ * Forgets that a class was processed, so a later request generates it again
792
+ * (used when the browser runtime reclaims an unused class's rules, #269).
793
+ *
794
+ * @param cls - The CSS class name to forget
795
+ */
796
+ unmarkProcessed(cls: string): void;
774
797
  /**
775
798
  * Process classes synchronously and update BrowserRuntime cache
776
799
  * This method is used by ChangeDetector for scan operations
@@ -973,6 +996,27 @@ export declare type AstNode = {
973
996
 
974
997
  export declare function rule(selector: string, nodes: AstNode[], source?: string): AstNode;
975
998
 
999
+ /**
1000
+ * Tailwind-compatible cascade order for runtime-inserted rules (#254); shared by @barocss/server (#267).
1001
+ *
1002
+ * The runtime discovers classes in DOM order, so without sorting `lg:px-8`
1003
+ * seen before `sm:px-6` would land earlier and lose at >= 1024px. Each rule
1004
+ * gets a sort key derived from its leading `@media` / `@container` preludes:
1005
+ *
1006
+ * 0 base, state media (hover), motion/contrast, unknown
1007
+ * 1 max-* breakpoints (larger width first)
1008
+ * 2 min-* breakpoints (smaller width first)
1009
+ * 3 @max-* container queries (larger width first)
1010
+ * 4 @min-* container queries (smaller width first)
1011
+ * 5 orientation, dark (prefers-color-scheme), print, forced-colors
1012
+ *
1013
+ * Nested at-rules (e.g. `sm:dark:`) contribute one key pair per level, so
1014
+ * `sm:` < `sm:dark:` < `md:`. Equal keys keep discovery order.
1015
+ */
1016
+ export declare type RuleKey = number[];
1017
+
1018
+ export declare function ruleSortKey(rule: string): RuleKey;
1019
+
976
1020
  /** Internal hook for caches owned by contexts. */
977
1021
  export declare function setContextCacheReset(reset: () => void): void;
978
1022
 
@@ -1087,6 +1131,9 @@ export declare type AstNode = {
1087
1131
  */
1088
1132
  export declare function tokenize(className: string): Token[];
1089
1133
 
1134
+ /** Index after the last key <= `key` (stable upper bound) in sorted `keys`. */
1135
+ export declare function upperBound(keys: RuleKey[], key: RuleKey): number;
1136
+
1090
1137
  /**
1091
1138
  * Utility cache management
1092
1139
  */
package/dist/index.js CHANGED
@@ -337,6 +337,11 @@ function staticUtility(name, decls, opts, ctx) {
337
337
  priority: opts?.priority
338
338
  }, ctx);
339
339
  }
340
+ function spacingKeyValue(ctx, key, negative) {
341
+ if (key === "px" || !/^[a-zA-Z][\w-]*$/.test(key) || ctx.theme("spacing", key) == null) return null;
342
+ const ref = `var(--spacing-${key})`;
343
+ return negative ? `calc(${ref} * -1)` : ref;
344
+ }
340
345
  function functionalUtility(opts, ctx) {
341
346
  registerUtility({
342
347
  name: opts.name,
@@ -405,14 +410,17 @@ function functionalUtility(opts, ctx) {
405
410
  if (opts.supportsFraction && /^-?\d+\/\d+$/.test(value)) {
406
411
  finalValue = value;
407
412
  }
413
+ const spacingKey = opts.spacingKeys ? spacingKeyValue(ctx2, String(finalValue).replace(/^-/, ""), !!parsedUtility.negative) : null;
408
414
  if (parsedUtility.negative && opts.supportsNegative && opts.handleNegativeBareValue) {
409
- const bare = opts.handleNegativeBareValue({ value: String(finalValue).replace(/^-/, ""), ctx: ctx2, token, extra });
415
+ const bare = opts.handleNegativeBareValue({ value: String(finalValue).replace(/^-/, ""), ctx: ctx2, token, extra }) ?? spacingKey;
410
416
  if (bare == null) return [];
411
417
  finalValue = bare;
412
418
  } else if (opts.handleBareValue) {
413
- const bare = opts.handleBareValue({ value: finalValue, ctx: ctx2, token, extra });
419
+ const bare = opts.handleBareValue({ value: finalValue, ctx: ctx2, token, extra }) ?? spacingKey;
414
420
  if (bare == null) return [];
415
421
  finalValue = bare;
422
+ } else if (spacingKey) {
423
+ finalValue = spacingKey;
416
424
  } else if (!/^-?(\d|\.\d)/.test(String(finalValue))) {
417
425
  return [];
418
426
  }
@@ -547,14 +555,23 @@ function parseClassName(className, ctx) {
547
555
  if (cache.has(className)) {
548
556
  return cache.get(className);
549
557
  }
550
- let important = false;
551
558
  let realClassName = className;
552
- if (className.startsWith("!")) {
559
+ const classPrefix = ctx ? configuredClassPrefix(ctx) : "";
560
+ if (classPrefix) {
561
+ if (!className.startsWith(classPrefix + ":")) {
562
+ const none = { modifiers: [], utility: null };
563
+ cache.set(className, none);
564
+ return none;
565
+ }
566
+ realClassName = className.slice(classPrefix.length + 1);
567
+ }
568
+ let important = false;
569
+ if (realClassName.startsWith("!")) {
553
570
  important = true;
554
- realClassName = className.slice(1);
555
- } else if (className.length > 1 && className.endsWith("!")) {
571
+ realClassName = realClassName.slice(1);
572
+ } else if (realClassName.length > 1 && realClassName.endsWith("!")) {
556
573
  important = true;
557
- realClassName = className.slice(0, -1);
574
+ realClassName = realClassName.slice(0, -1);
558
575
  }
559
576
  const tokens = tokenize(realClassName);
560
577
  const result = parseTokens(tokens, ctx);
@@ -564,6 +581,10 @@ function parseClassName(className, ctx) {
564
581
  cache.set(className, result);
565
582
  return result;
566
583
  }
584
+ function configuredClassPrefix(ctx) {
585
+ const configured = ctx.config("prefix");
586
+ return typeof configured === "string" && /^[a-z]+$/.test(configured) ? configured : "";
587
+ }
567
588
  function parseTokens(tokens, ctx) {
568
589
  const modifiers = [];
569
590
  let utility = null;
@@ -623,6 +644,18 @@ function isSafeVariantToken(value) {
623
644
  function hasCommentToken(value) {
624
645
  return value.includes("/*") || value.includes("*/");
625
646
  }
647
+ function hasCommentDelimiter(text) {
648
+ for (let i = 0; i < text.length - 1; i++) {
649
+ const c = text[i];
650
+ if (c === "\\") {
651
+ i++;
652
+ continue;
653
+ }
654
+ const n = text[i + 1];
655
+ if (c === "/" && n === "*" || c === "*" && n === "/") return true;
656
+ }
657
+ return false;
658
+ }
626
659
  function isStructureSafeValue(value) {
627
660
  if (hasCommentToken(value)) return false;
628
661
  return isSafeVariantValue(value, true);
@@ -770,6 +803,7 @@ function parseUtility(value, ctx) {
770
803
  priority
771
804
  };
772
805
  }
806
+ const isSafePrelude = (text) => !hasCommentDelimiter(String(text ?? ""));
773
807
  const isSafeDecl = (prop, value) => isStructureSafeValue(String(prop)) && isStructureSafeValue(String(value ?? ""));
774
808
  const importantPrefix = "!important";
775
809
  function astToCss(ast, baseSelector, opts, _indent = "") {
@@ -833,6 +867,7 @@ function astToCss(ast, baseSelector, opts, _indent = "") {
833
867
  }).join(", ");
834
868
  }
835
869
  }
870
+ if (!isSafePrelude(selector)) return "";
836
871
  if (minify) {
837
872
  const css = `${indent}${selector}{${astToCss(
838
873
  node.nodes,
@@ -857,6 +892,7 @@ ${astToCss(
857
892
  }
858
893
  }
859
894
  case "style-rule": {
895
+ if (!isSafePrelude(node.selector)) return "";
860
896
  if (minify) {
861
897
  const css = `${indent}${node.selector} {${astToCss(
862
898
  node.nodes,
@@ -881,6 +917,7 @@ ${astToCss(
881
917
  }
882
918
  }
883
919
  case "at-rule": {
920
+ if (!isSafePrelude(node.name) || !isSafePrelude(node.params)) return "";
884
921
  if (minify) {
885
922
  const css = `${indent}@${node.name} ${node.params}{${astToCss(
886
923
  node.nodes,
@@ -934,7 +971,7 @@ function rootToCss(nodes, opts) {
934
971
  if (isSafeDecl(node.prop, node.value)) {
935
972
  list.push(minify ? `${node.prop}:${node.value};` : `${node.prop}: ${node.value};`);
936
973
  }
937
- } else if (node.type === "at-rule") {
974
+ } else if (node.type === "at-rule" && isSafePrelude(node.name) && isSafePrelude(node.params)) {
938
975
  if (minify) {
939
976
  const body = node.nodes.filter((child) => child.type === "decl" && isSafeDecl(child.prop, child.value)).map((child) => child.type === "decl" ? `${child.prop}:${child.value};` : "").join("");
940
977
  list.push(`@${node.name} ${node.params}{${body}}`);
@@ -1071,24 +1108,41 @@ function animationToCssVars(animations) {
1071
1108
  }
1072
1109
  return result;
1073
1110
  }
1074
- function keyframesToCss(keyframes) {
1075
- if (!keyframes) return "";
1076
- let css = "";
1077
- for (const name in keyframes) {
1078
- const frames = keyframes[name];
1079
- css += `@keyframes ${name} {
1111
+ const COMMENT_OR_BLOCK = /\/\*|\*\/|[{};]/;
1112
+ function keyframesBlock(name, frames) {
1113
+ if (!name || COMMENT_OR_BLOCK.test(name) || /\s/.test(name) || !frames || typeof frames !== "object") return "";
1114
+ let body = "";
1115
+ for (const [step, props] of Object.entries(frames)) {
1116
+ if (COMMENT_OR_BLOCK.test(step) || !props || typeof props !== "object") return "";
1117
+ let decls = "";
1118
+ for (const [prop, value] of Object.entries(props)) {
1119
+ const v2 = String(value);
1120
+ if (COMMENT_OR_BLOCK.test(prop) || COMMENT_OR_BLOCK.test(v2)) return "";
1121
+ decls += ` ${prop}: ${v2};
1080
1122
  `;
1081
- for (const step in frames) {
1082
- css += ` ${step} {`;
1083
- const props = frames[step];
1084
- for (const prop in props) {
1085
- css += ` ${prop}: ${props[prop]};`;
1086
- }
1087
- css += " }\n";
1088
1123
  }
1089
- css += "}\n";
1124
+ body += ` ${step} {
1125
+ ${decls} }
1126
+ `;
1090
1127
  }
1091
- return css;
1128
+ return `@keyframes ${name} {
1129
+ ${body}}`;
1130
+ }
1131
+ function referencedKeyframes(css, ctx) {
1132
+ if (!css.includes("animation")) return [];
1133
+ const all = ctx.theme("keyframes");
1134
+ if (!all || typeof all !== "object") return [];
1135
+ const names = /* @__PURE__ */ new Set();
1136
+ for (const m of css.matchAll(/(?:^|[\s;{])animation(?:-name)?\s*:\s*([^;}]+)/g)) {
1137
+ const value = m[1].replace(/var\(--animate-([\w-]+)\)/g, (whole, key) => {
1138
+ const v2 = ctx.theme("animations", key) ?? ctx.theme("animation", key);
1139
+ return typeof v2 === "string" ? v2 : whole;
1140
+ });
1141
+ for (const word of value.split(/[\s,()]+/)) {
1142
+ if (word && Object.prototype.hasOwnProperty.call(all, word)) names.add(word);
1143
+ }
1144
+ }
1145
+ return [...names].map((n) => keyframesBlock(n, all[n])).filter(Boolean);
1092
1146
  }
1093
1147
  function transitionTimingFunctionToCssVars(transition) {
1094
1148
  const result = {};
@@ -1156,7 +1210,7 @@ function themeToCssVarsAll(theme) {
1156
1210
  ...borderRadiusToCssVars(theme.borderRadius),
1157
1211
  ...zIndexToCssVars(theme.zIndex),
1158
1212
  ...opacityToCssVars(theme.opacity),
1159
- ...animationToCssVars(theme.animations),
1213
+ ...animationToCssVars({ ...theme.animations, ...theme.animation }),
1160
1214
  ...transitionTimingFunctionToCssVars(theme.transitionTimingFunction),
1161
1215
  ...transitionDurationToCssVars(theme.transitionDuration),
1162
1216
  ...transitionDelayToCssVars(theme.transitionDelay),
@@ -1165,8 +1219,13 @@ function themeToCssVarsAll(theme) {
1165
1219
  // keyframes handled separately
1166
1220
  };
1167
1221
  }
1222
+ function isSelfReferencingVar(name, value) {
1223
+ if (typeof value !== "string") return false;
1224
+ const m = /^var\(\s*(--[\w-]+)\s*(?:,[\s\S]*)?\)$/.exec(value.trim());
1225
+ return !!m && m[1] === name.trim();
1226
+ }
1168
1227
  function toCssVarsBlock(vars, extra = "") {
1169
- return ":root,:host {\n" + Object.entries(vars).map(([k, v2]) => ` ${k}: ${v2};`).join("\n") + "\n}\n" + extra + "\n";
1228
+ return ":root,:host {\n" + Object.entries(vars).filter(([k, v2]) => !isSelfReferencingVar(k, v2)).map(([k, v2]) => ` ${k}: ${v2};`).join("\n") + "\n}\n" + extra + "\n";
1170
1229
  }
1171
1230
  const BARO_VAR = /--baro-/g;
1172
1231
  const PREFIXED_KEYS = /* @__PURE__ */ new Set(["prop", "value", "params", "selector", "nodes", "items"]);
@@ -1521,7 +1580,11 @@ function generateCss(classList, ctx, opts) {
1521
1580
  }
1522
1581
  return result;
1523
1582
  }).join(opts?.minify ? "" : "\n");
1524
- const rootRules = [...new Set(allAtRootNodes.filter((node) => node.type === "at-rule").map((node) => rootToCss([node], { minify: opts?.minify })))];
1583
+ const rootRules = [.../* @__PURE__ */ new Set([
1584
+ ...allAtRootNodes.filter((node) => node.type === "at-rule").map((node) => rootToCss([node], { minify: opts?.minify })),
1585
+ // #274: the @keyframes the class rules reference, once per sheet.
1586
+ ...referencedKeyframes(results, ctx)
1587
+ ])];
1525
1588
  const rootDeclarations = [...new Set(allAtRootNodes.filter((node) => node.type === "decl").map((node) => rootToCss([node], { minify: opts?.minify })).filter((decl2) => decl2 !== ""))];
1526
1589
  const rootCss = [
1527
1590
  ...rootRules,
@@ -1579,6 +1642,7 @@ function generateCssRules(classList, ctx, opts) {
1579
1642
  const css = rootToCss([node]);
1580
1643
  rootCssList.push(css);
1581
1644
  }
1645
+ rootCssList.push(...referencedKeyframes(cssList.join("\n"), ctx));
1582
1646
  return {
1583
1647
  cls,
1584
1648
  ast: allCleanAst,
@@ -1841,6 +1905,15 @@ class IncrementalParser {
1841
1905
  markProcessed(cls) {
1842
1906
  this.processedClasses.add(cls);
1843
1907
  }
1908
+ /**
1909
+ * Forgets that a class was processed, so a later request generates it again
1910
+ * (used when the browser runtime reclaims an unused class's rules, #269).
1911
+ *
1912
+ * @param cls - The CSS class name to forget
1913
+ */
1914
+ unmarkProcessed(cls) {
1915
+ this.processedClasses.delete(cls);
1916
+ }
1844
1917
  /**
1845
1918
  * Process classes synchronously and update BrowserRuntime cache
1846
1919
  * This method is used by ChangeDetector for scan operations
@@ -1860,6 +1933,48 @@ class IncrementalParser {
1860
1933
  return Array.from(this.processedClasses);
1861
1934
  }
1862
1935
  }
1936
+ const customUtilityName = /^[A-Za-z_][A-Za-z0-9_-]*$/;
1937
+ const customUtilityProp = /^(--[A-Za-z0-9_-]+|-?[A-Za-z][A-Za-z0-9-]*)$/;
1938
+ function validateCustomUtility(name, decls) {
1939
+ if (typeof name !== "string" || !customUtilityName.test(name)) return null;
1940
+ if (!decls || typeof decls !== "object" || Array.isArray(decls)) return null;
1941
+ const out = [];
1942
+ for (const [prop, raw2] of Object.entries(decls)) {
1943
+ if (typeof raw2 !== "string" && typeof raw2 !== "number") return null;
1944
+ const value = String(raw2).trim();
1945
+ if (!customUtilityProp.test(prop) || !value || !isStructureSafeValue(value) || hasCommentDelimiter(value)) return null;
1946
+ out.push([prop, value]);
1947
+ }
1948
+ return out.length ? out : null;
1949
+ }
1950
+ function registerCustomUtilities(ctx, utilities) {
1951
+ if (!utilities || typeof utilities !== "object" || Array.isArray(utilities)) return;
1952
+ const list = getUtility(ctx);
1953
+ const builtins = [...list];
1954
+ const before = list.length;
1955
+ for (const [name, decls] of Object.entries(utilities)) {
1956
+ const safe = validateCustomUtility(name, decls);
1957
+ if (!safe) {
1958
+ debugWarn(`[BAROCSS] Ignoring invalid custom utility "${name}"`);
1959
+ continue;
1960
+ }
1961
+ const shadowed = builtins.filter((u) => u.match(name));
1962
+ registerUtility({
1963
+ name,
1964
+ category: "custom",
1965
+ match: (className) => className === name,
1966
+ handler: (value, c, token) => {
1967
+ let base = [];
1968
+ for (const reg of shadowed) {
1969
+ base = reg.handler(value, c, token, reg) || [];
1970
+ if (base.length > 0) break;
1971
+ }
1972
+ return [...base, ...safe.map(([prop, v]) => decl(prop, v))];
1973
+ }
1974
+ }, ctx);
1975
+ }
1976
+ if (list.length > before) list.unshift(...list.splice(before));
1977
+ }
1863
1978
  const preflightMinimalCSS = `
1864
1979
  /* BaroCSS Preflight - Minimal Reset */
1865
1980
  /* ================================= */
@@ -2645,7 +2760,6 @@ function getPreflightCSS(level = true) {
2645
2760
  return "";
2646
2761
  }
2647
2762
  const defaultConfig = {
2648
- prefix: "barocss-",
2649
2763
  darkMode: "media"
2650
2764
  // same as default
2651
2765
  };
@@ -2747,9 +2861,7 @@ function resolveTheme(config) {
2747
2861
  }
2748
2862
  function themeToCssVars(theme) {
2749
2863
  const vars = themeToCssVarsAll(theme);
2750
- const result = toCssVarsBlock(vars, `
2751
- ${keyframesToCss(theme.keyframes || {})}
2752
- `);
2864
+ const result = toCssVarsBlock(vars);
2753
2865
  return result;
2754
2866
  }
2755
2867
  function createContext(configObj) {
@@ -2803,6 +2915,7 @@ function createContext(configObj) {
2803
2915
  }
2804
2916
  };
2805
2917
  initializeContextState(ctx, getUtility(), getModifier());
2918
+ registerCustomUtilities(ctx, configObj.utilities);
2806
2919
  return ctx;
2807
2920
  }
2808
2921
  function jsonToAst(input, ctx) {
@@ -3387,6 +3500,7 @@ staticUtility("snap-proximity", [["--baro-scroll-snap-strictness", "proximity"]]
3387
3500
  ].forEach(([name, prop]) => {
3388
3501
  functionalUtility({
3389
3502
  name: `scroll-${name}`,
3503
+ spacingKeys: true,
3390
3504
  prop,
3391
3505
  supportsArbitrary: true,
3392
3506
  supportsCustomProperty: true,
@@ -3414,6 +3528,7 @@ staticUtility("snap-proximity", [["--baro-scroll-snap-strictness", "proximity"]]
3414
3528
  ].forEach(([name, prop]) => {
3415
3529
  functionalUtility({
3416
3530
  name: `scroll-${name}`,
3531
+ spacingKeys: true,
3417
3532
  prop,
3418
3533
  supportsArbitrary: true,
3419
3534
  supportsCustomProperty: true,
@@ -3572,10 +3687,12 @@ staticUtility("animate-bounce", [["animation", "var(--animate-bounce)"]], { cate
3572
3687
  staticUtility("animate-none", [["animation", "none"]], { category: "transitions" });
3573
3688
  functionalUtility({
3574
3689
  name: "animate",
3575
- prop: "animation",
3690
+ // #274: theme.animations (and Tailwind's theme.animation) names, e.g. theme.extend.animation.wiggle.
3691
+ themeKeys: ["animations", "animation"],
3576
3692
  supportsArbitrary: true,
3577
3693
  supportsCustomProperty: true,
3578
- handle: (value, ctx, token) => {
3694
+ handle: (value, ctx, token, extra) => {
3695
+ if (extra?.realThemeValue) return [decl("animation", `var(--animate-${extra.realThemeValue})`)];
3579
3696
  if (token.customProperty) {
3580
3697
  return [decl("animation", `var(${value})`)];
3581
3698
  }
@@ -4947,6 +5064,7 @@ staticUtility("sticky", [["position", "sticky"]], { category: "layout" });
4947
5064
  staticUtility(`-${name}-px`, [[prop, "-1px"]], { category: "layout" });
4948
5065
  functionalUtility({
4949
5066
  name,
5067
+ spacingKeys: true,
4950
5068
  prop,
4951
5069
  supportsNegative: true,
4952
5070
  supportsFraction: true,
@@ -4977,6 +5095,7 @@ staticUtility("invisible", [["visibility", "hidden"]], { category: "layout" });
4977
5095
  staticUtility("collapse", [["visibility", "collapse"]], { category: "layout" });
4978
5096
  functionalUtility({
4979
5097
  name: "gap-x",
5098
+ spacingKeys: true,
4980
5099
  prop: "column-gap",
4981
5100
  supportsArbitrary: true,
4982
5101
  // gap-x-[10vw]
@@ -4993,6 +5112,7 @@ functionalUtility({
4993
5112
  });
4994
5113
  functionalUtility({
4995
5114
  name: "gap-y",
5115
+ spacingKeys: true,
4996
5116
  prop: "row-gap",
4997
5117
  supportsArbitrary: true,
4998
5118
  // gap-y-[10vw]
@@ -5009,6 +5129,7 @@ functionalUtility({
5009
5129
  });
5010
5130
  functionalUtility({
5011
5131
  name: "gap",
5132
+ spacingKeys: true,
5012
5133
  prop: "gap",
5013
5134
  supportsArbitrary: true,
5014
5135
  // gap-[10vw]
@@ -5526,6 +5647,7 @@ functionalUtility({
5526
5647
  ].forEach(([name, prop]) => {
5527
5648
  staticUtility(`${name}-px`, [[prop, "1px"]], { category: "spacing" });
5528
5649
  functionalUtility({
5650
+ spacingKeys: true,
5529
5651
  name,
5530
5652
  prop,
5531
5653
  supportsArbitrary: true,
@@ -5550,6 +5672,7 @@ functionalUtility({
5550
5672
  staticUtility(`${name}-px`, [[prop, "1px"]], { category: "spacing" });
5551
5673
  staticUtility(`-${name}-px`, [[prop, "-1px"]], { category: "spacing" });
5552
5674
  functionalUtility({
5675
+ spacingKeys: true,
5553
5676
  name,
5554
5677
  prop,
5555
5678
  supportsNegative: true,
@@ -5580,6 +5703,7 @@ const SPACE_SELECTOR = ":where(& > :not(:last-child))";
5580
5703
  () => rule(SPACE_SELECTOR, [decl(rev, "1")])
5581
5704
  ], { category: "spacing" });
5582
5705
  functionalUtility({
5706
+ spacingKeys: true,
5583
5707
  name,
5584
5708
  supportsNegative: true,
5585
5709
  supportsArbitrary: true,
@@ -5629,6 +5753,7 @@ const SPACE_SELECTOR = ":where(& > :not(:last-child))";
5629
5753
  staticUtility(name, [["width", value]]);
5630
5754
  });
5631
5755
  functionalUtility({
5756
+ spacingKeys: true,
5632
5757
  name: "w",
5633
5758
  prop: "width",
5634
5759
  supportsArbitrary: true,
@@ -5661,6 +5786,7 @@ functionalUtility({
5661
5786
  staticUtility(name, [["width", w], ["height", h]]);
5662
5787
  });
5663
5788
  functionalUtility({
5789
+ spacingKeys: true,
5664
5790
  name: "size",
5665
5791
  supportsArbitrary: true,
5666
5792
  supportsCustomProperty: true,
@@ -5700,6 +5826,7 @@ functionalUtility({
5700
5826
  staticUtility(name, [["height", value]]);
5701
5827
  });
5702
5828
  functionalUtility({
5829
+ spacingKeys: true,
5703
5830
  name: "h",
5704
5831
  prop: "height",
5705
5832
  supportsArbitrary: true,
@@ -5730,6 +5857,7 @@ functionalUtility({
5730
5857
  staticUtility(name, [["min-height", value]], { category: "sizing" });
5731
5858
  });
5732
5859
  functionalUtility({
5860
+ spacingKeys: true,
5733
5861
  name: "min-h",
5734
5862
  prop: "min-height",
5735
5863
  supportsArbitrary: true,
@@ -5760,6 +5888,7 @@ functionalUtility({
5760
5888
  staticUtility(name, [["max-height", value]], { category: "sizing" });
5761
5889
  });
5762
5890
  functionalUtility({
5891
+ spacingKeys: true,
5763
5892
  name: "max-h",
5764
5893
  prop: "max-height",
5765
5894
  supportsArbitrary: true,
@@ -5806,6 +5935,7 @@ functionalUtility({
5806
5935
  staticUtility(name, [["min-width", value]], { category: "sizing" });
5807
5936
  });
5808
5937
  functionalUtility({
5938
+ spacingKeys: true,
5809
5939
  name: "min-w",
5810
5940
  prop: "min-width",
5811
5941
  supportsArbitrary: true,
@@ -5841,6 +5971,7 @@ functionalUtility({
5841
5971
  staticUtility(name, [["max-width", value]], { category: "sizing" });
5842
5972
  });
5843
5973
  functionalUtility({
5974
+ spacingKeys: true,
5844
5975
  name: "max-w",
5845
5976
  prop: "max-width",
5846
5977
  supportsArbitrary: true,
@@ -8264,6 +8395,51 @@ functionalModifier(
8264
8395
  },
8265
8396
  void 0
8266
8397
  );
8398
+ const LEADING_AT = /^\s*@(media|container)\s+([^{]*)\{/;
8399
+ const LATE_MEDIA = /prefers-color-scheme|\bprint\b|forced-colors|orientation/;
8400
+ const MIN_W = /(?:min-width\s*:\s*|width\s*>=?\s*)([\d.]+)(px|rem|em)?/;
8401
+ const MAX_W = /(?:max-width\s*:\s*|width\s*<=?\s*)([\d.]+)(px|rem|em)?/;
8402
+ function toPx(n, unit) {
8403
+ const v = parseFloat(n);
8404
+ return unit === "rem" || unit === "em" ? v * 16 : v;
8405
+ }
8406
+ function preludeKey(kind, prelude) {
8407
+ const container = kind === "container";
8408
+ const min = MIN_W.exec(prelude);
8409
+ if (min) return [container ? 4 : 2, toPx(min[1], min[2])];
8410
+ const max = MAX_W.exec(prelude);
8411
+ if (max) return [container ? 3 : 1, -toPx(max[1], max[2])];
8412
+ if (!container && LATE_MEDIA.test(prelude)) return [5, 0];
8413
+ return [0, 0];
8414
+ }
8415
+ function ruleSortKey(rule2) {
8416
+ const key = [];
8417
+ let rest = rule2;
8418
+ let m;
8419
+ while (m = LEADING_AT.exec(rest)) {
8420
+ const [g, v] = preludeKey(m[1], m[2]);
8421
+ key.push(g, v);
8422
+ rest = rest.slice(m[0].length);
8423
+ }
8424
+ return key;
8425
+ }
8426
+ function compareKeys(a, b) {
8427
+ const n = Math.min(a.length, b.length);
8428
+ for (let i = 0; i < n; i++) {
8429
+ if (a[i] !== b[i]) return a[i] - b[i];
8430
+ }
8431
+ return a.length - b.length;
8432
+ }
8433
+ function upperBound(keys, key) {
8434
+ let lo = 0;
8435
+ let hi = keys.length;
8436
+ while (lo < hi) {
8437
+ const mid = lo + hi >> 1;
8438
+ if (compareKeys(keys[mid], key) <= 0) lo = mid + 1;
8439
+ else hi = mid;
8440
+ }
8441
+ return lo;
8442
+ }
8267
8443
  export {
8268
8444
  AstCache,
8269
8445
  IncrementalParser,
@@ -8279,6 +8455,7 @@ export {
8279
8455
  clearAstCache,
8280
8456
  collectDeclPaths,
8281
8457
  comment,
8458
+ compareKeys,
8282
8459
  configGetter,
8283
8460
  createContext,
8284
8461
  decl,
@@ -8296,6 +8473,7 @@ export {
8296
8473
  getModifier,
8297
8474
  getPreflightCSS,
8298
8475
  getUtility,
8476
+ hasCommentDelimiter,
8299
8477
  hasCommentToken,
8300
8478
  hasPreset,
8301
8479
  isDebug,
@@ -8317,6 +8495,7 @@ export {
8317
8495
  resolveTheme,
8318
8496
  rootToCss,
8319
8497
  rule,
8498
+ ruleSortKey,
8320
8499
  setContextCacheReset,
8321
8500
  setDebug,
8322
8501
  staticModifier,
@@ -8325,6 +8504,7 @@ export {
8325
8504
  themeGetter,
8326
8505
  themeToCssVars,
8327
8506
  tokenize,
8507
+ upperBound,
8328
8508
  utilityCache
8329
8509
  };
8330
8510
  //# sourceMappingURL=index.js.map