rainbowindex 0.6.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +543 -0
  2. package/LICENSE +16 -17
  3. package/NOTICE.md +106 -0
  4. package/README.md +219 -65
  5. package/dist/browser.d.ts +4 -2
  6. package/dist/browser.mjs +12 -4
  7. package/dist/chunk-2T7V5XLK.mjs +912 -0
  8. package/dist/chunk-6OORICWF.mjs +16 -0
  9. package/dist/{chunk-KSNYSR3C.mjs → chunk-FJOZJIKB.mjs} +2499 -329
  10. package/dist/chunk-L56IRO7A.mjs +491 -0
  11. package/dist/chunk-PZDVDEZJ.mjs +196 -0
  12. package/dist/{chunk-3LWJTLOJ.mjs → chunk-RC6DDE4L.mjs} +23 -15
  13. package/dist/chunk-TQJYVQPE.mjs +217 -0
  14. package/dist/chunk-W756NVYI.mjs +33 -0
  15. package/dist/chunk-WBESS2ZD.mjs +598 -0
  16. package/dist/{chunk-3HRMFZGE.mjs → chunk-X66Z2YHT.mjs} +2 -1
  17. package/dist/{chunk-6U4IOFOS.mjs → chunk-XQGSG2HK.mjs} +199 -555
  18. package/dist/cli.mjs +1077 -123
  19. package/dist/{index-DSgpB6bS.d.ts → context-DcBtnnan.d.ts} +47 -103
  20. package/dist/editor.d.ts +71 -433
  21. package/dist/editor.mjs +51 -368
  22. package/dist/eslint.d.ts +16 -0
  23. package/dist/eslint.mjs +32 -0
  24. package/dist/{index-4Kyaq3IZ.d.ts → imports-C9esHd5Q.d.ts} +78 -84
  25. package/dist/index-CNqdL5U0.d.ts +56 -0
  26. package/dist/index-Czx-EUwh.d.ts +138 -0
  27. package/dist/index-DW8YSxTz.d.ts +104 -0
  28. package/dist/index.d.ts +46 -5
  29. package/dist/index.mjs +33 -9
  30. package/dist/oxlint.d.ts +21 -3
  31. package/dist/oxlint.mjs +19 -1
  32. package/dist/recipe.d.ts +111 -0
  33. package/dist/recipe.mjs +71 -0
  34. package/dist/safelist-CH3_PywB.d.ts +43 -0
  35. package/dist/session-CMaskdB7.d.ts +543 -0
  36. package/dist/tailwind.css +644 -0
  37. package/dist/theme-CIZiGlce.d.ts +115 -0
  38. package/dist/vite.d.ts +10 -1
  39. package/dist/vite.mjs +266 -118
  40. package/package.json +27 -5
  41. package/dist/chunk-WK6S4HTC.mjs +0 -1921
  42. package/dist/chunk-ZR7XJMUN.mjs +0 -251
  43. package/dist/safelist-CGCtF-Fr.d.ts +0 -96
@@ -1,12 +1,20 @@
1
1
  import {
2
- BORDER_DIR_PROPS,
3
- BUILTIN_STATIC_KEYS,
4
- COLOR_FUNCTION_ALTERNATION,
5
2
  CORNER_SHAPE_KEYWORDS,
6
3
  DEFAULT_COLORS,
7
4
  DEFAULT_CORNER_SCALE,
8
5
  DEFAULT_DARK_CONFIG,
9
6
  DEFAULT_SUPERELLIPSE_SCALE,
7
+ checkPaletteContrast,
8
+ generateAllColorVariables,
9
+ generateThemeOverrides,
10
+ isValidColorSuffix
11
+ } from "./chunk-L56IRO7A.mjs";
12
+ import {
13
+ BACKDROP_FILTER_COMPOSED,
14
+ BORDER_DIR_PROPS,
15
+ BUILTIN_STATIC_KEYS,
16
+ COLOR_FUNCTION_ALTERNATION,
17
+ FILTER_COMPOSED,
10
18
  GAP_MAP,
11
19
  INSET_MAP,
12
20
  IS_DEV,
@@ -23,13 +31,12 @@ import {
23
31
  devWarn,
24
32
  isFontFamilyValue,
25
33
  isMaskRadialSizeValue,
26
- isValidColorSuffix,
27
34
  registerColorNames,
28
35
  registerCustomFontFamilies,
29
36
  registerCustomTextSizes,
30
37
  registerCustomUtility,
31
38
  snapshotCompilationContext
32
- } from "./chunk-6U4IOFOS.mjs";
39
+ } from "./chunk-XQGSG2HK.mjs";
33
40
 
34
41
  // src/shared.ts
35
42
  function isRIDebug() {
@@ -130,6 +137,7 @@ var PARSER_ONLY_STATICS = [
130
137
  "h-max",
131
138
  "h-fit",
132
139
  "min-w-0",
140
+ "min-w-auto",
133
141
  "min-w-full",
134
142
  "min-w-min",
135
143
  "min-w-max",
@@ -141,6 +149,7 @@ var PARSER_ONLY_STATICS = [
141
149
  "max-w-fit",
142
150
  "max-w-prose",
143
151
  "min-h-0",
152
+ "min-h-auto",
144
153
  "min-h-full",
145
154
  "min-h-screen",
146
155
  "min-h-min",
@@ -508,18 +517,16 @@ function findVariantColon(input) {
508
517
  for (let i = 0; i < input.length; i++) {
509
518
  const ch = input[i];
510
519
  if (ch === "\\" && i + 1 < input.length) {
511
- const next = input[i + 1];
512
- if (isHexDigit(next)) {
513
- let hexLen = 0;
514
- while (hexLen < 6 && i + 1 + hexLen < input.length && isHexDigit(input[i + 1 + hexLen]))
515
- hexLen++;
516
- i += hexLen;
517
- if (i + 1 < input.length && /\s/.test(input[i + 1])) i++;
518
- } else {
519
- i++;
520
- }
520
+ i = endOfEscape(input, i);
521
521
  continue;
522
522
  }
523
+ if (depth > 0 && (ch === "'" || ch === '"')) {
524
+ const close = endOfQuoted(input, i);
525
+ if (close !== -1) {
526
+ i = close;
527
+ continue;
528
+ }
529
+ }
523
530
  if (ch === "[" || ch === "(") depth++;
524
531
  else if (ch === "]" || ch === ")") depth--;
525
532
  else if (ch === ":" && depth === 0) {
@@ -530,17 +537,74 @@ function findVariantColon(input) {
530
537
  }
531
538
  return -1;
532
539
  }
540
+ function endOfEscape(s, i) {
541
+ if (i + 1 >= s.length) return i;
542
+ if (!isHexDigit(s[i + 1])) return i + 1;
543
+ let hexLen = 0;
544
+ while (hexLen < 6 && i + 1 + hexLen < s.length && isHexDigit(s[i + 1 + hexLen])) hexLen++;
545
+ let end = i + hexLen;
546
+ if (end + 1 < s.length && /\s/.test(s[end + 1])) end++;
547
+ return end;
548
+ }
549
+ function endOfQuoted(s, i) {
550
+ const quote = s[i];
551
+ for (let j = i + 1; j < s.length; j++) {
552
+ if (s[j] === "\\" && j + 1 < s.length) {
553
+ j++;
554
+ continue;
555
+ }
556
+ if (s[j] === quote) return j;
557
+ }
558
+ return -1;
559
+ }
560
+ function findVariantBracketEnd(s, start) {
561
+ let depth = 0;
562
+ for (let i = start; i < s.length; i++) {
563
+ const ch = s[i];
564
+ if (ch === "\\") {
565
+ i = endOfEscape(s, i);
566
+ continue;
567
+ }
568
+ if (ch === "'" || ch === '"') {
569
+ const close = endOfQuoted(s, i);
570
+ if (close !== -1) i = close;
571
+ continue;
572
+ }
573
+ if (ch === "[") depth++;
574
+ else if (ch === "]") {
575
+ depth--;
576
+ if (depth === 0) return i;
577
+ }
578
+ }
579
+ return -1;
580
+ }
533
581
  function isHexDigit(ch) {
534
582
  const c = ch.charCodeAt(0);
535
583
  return c >= 48 && c <= 57 || c >= 65 && c <= 70 || c >= 97 && c <= 102;
536
584
  }
537
- var VARIANT_PREFIX_RE = /^[a-z0-9][a-z0-9-]*(\[[^\]]*\])?$/;
585
+ var VARIANT_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
586
+ var VARIANT_MODIFIER_RE = /^\/[a-zA-Z0-9_-]+$/;
538
587
  function isVariantPrefix(s) {
539
588
  if (!s) return false;
540
589
  if (s.startsWith("[") && s.endsWith("]")) return true;
541
590
  if (s.startsWith("@")) return true;
542
591
  if (s === "*" || s === "**") return true;
543
- return VARIANT_PREFIX_RE.test(s);
592
+ let name = s;
593
+ let tail = "";
594
+ const bracketStart = s.indexOf("[");
595
+ if (bracketStart !== -1) {
596
+ const bracketEnd = findVariantBracketEnd(s, bracketStart);
597
+ if (bracketEnd === -1) return false;
598
+ name = s.slice(0, bracketStart);
599
+ tail = s.slice(bracketEnd + 1);
600
+ } else {
601
+ const slash = s.indexOf("/");
602
+ if (slash !== -1) {
603
+ name = s.slice(0, slash);
604
+ tail = s.slice(slash);
605
+ }
606
+ }
607
+ return VARIANT_NAME_RE.test(name) && (tail === "" || VARIANT_MODIFIER_RE.test(tail));
544
608
  }
545
609
  function findMatchingBracket(input, start) {
546
610
  let depth = 0;
@@ -682,6 +746,53 @@ function diagnosticFromWarning(message, span) {
682
746
  };
683
747
  }
684
748
 
749
+ // src/css/escape.ts
750
+ function escapeSelector(raw) {
751
+ let out = null;
752
+ let runStart = 0;
753
+ for (let i = 0; i < raw.length; i++) {
754
+ const code = raw.charCodeAt(i);
755
+ if (code >= 97 && code <= 122 || // a-z
756
+ code >= 65 && code <= 90 || // A-Z
757
+ code === 45 || // -
758
+ code === 95 || // _
759
+ code >= 128) {
760
+ continue;
761
+ }
762
+ if (code >= 48 && code <= 57 && // 0-9
763
+ i !== 0 && !(i === 1 && raw.charCodeAt(0) === 45)) {
764
+ continue;
765
+ }
766
+ let escaped;
767
+ if (code === 0) {
768
+ escaped = "\uFFFD";
769
+ } else if (code >= 48 && code <= 57) {
770
+ escaped = `\\${code.toString(16)} `;
771
+ } else if (code === 9 || code === 10 || code === 12 || code === 13) {
772
+ escaped = `\\${raw[i]}`;
773
+ } else if (code < 32 || code === 127) {
774
+ escaped = `\\${code.toString(16)} `;
775
+ } else {
776
+ escaped = `\\${raw[i]}`;
777
+ }
778
+ if (out === null) out = [];
779
+ out.push(raw.slice(runStart, i), escaped);
780
+ runStart = i + 1;
781
+ }
782
+ if (out === null) return raw;
783
+ out.push(raw.slice(runStart));
784
+ return out.join("");
785
+ }
786
+
787
+ // src/utilities/markers.ts
788
+ var RELATIONAL_NAME_RE = /^[a-zA-Z0-9_-]+$/;
789
+ var MARKER_ROOTS = /* @__PURE__ */ new Set(["group", "peer"]);
790
+ function isMarkerUtility(utility) {
791
+ const slash = utility.indexOf("/");
792
+ if (slash === -1) return MARKER_ROOTS.has(utility);
793
+ return MARKER_ROOTS.has(utility.slice(0, slash)) && RELATIONAL_NAME_RE.test(utility.slice(slash + 1));
794
+ }
795
+
685
796
  // src/engine/variants.ts
686
797
  var MAX_CUSTOM_VARIANT_SELECTOR_LENGTH = 500;
687
798
  var SAFE_CSS_LENGTH_RE = /^\d+(\.\d+)?(px|em|rem|ch|vw|vh|svw|svh|dvw|dvh|cqw|cqh)$/;
@@ -814,10 +925,49 @@ function sanitizeVariantBracket(inner) {
814
925
  return cleaned;
815
926
  }
816
927
  var SAFE_AT_RULE_RE = /^@(?:media|supports|container|layer)\b/;
817
- function resolveRelationalVariant(inner, anchor, combinator) {
928
+ function splitRelationalName(rest, kind) {
929
+ let depth = 0;
930
+ for (let i = rest.length - 1; i >= 0; i--) {
931
+ const ch = rest[i];
932
+ if (ch === "]" || ch === ")") depth++;
933
+ else if (ch === "[" || ch === "(") depth--;
934
+ else if (ch === "/" && depth === 0) {
935
+ const name = rest.slice(i + 1);
936
+ if (!RELATIONAL_NAME_RE.test(name)) return { state: rest, anchor: null };
937
+ return { state: rest.slice(0, i), anchor: `.${escapeSelector(`${kind}/${name}`)}` };
938
+ }
939
+ }
940
+ return { state: rest, anchor: `.${kind}` };
941
+ }
942
+ function breakpointValue(name, theme) {
943
+ if (!Object.hasOwn(theme.breakpoints, name)) return null;
944
+ const value = theme.breakpoints[name];
945
+ return SAFE_CSS_LENGTH_RE.test(value) ? value : null;
946
+ }
947
+ function widthBound(rest, theme) {
948
+ if (rest.startsWith("[") && rest.endsWith("]")) {
949
+ const v = rest.slice(1, -1);
950
+ return SAFE_CSS_LENGTH_RE.test(v) ? v : null;
951
+ }
952
+ return breakpointValue(rest, theme);
953
+ }
954
+ var MAX_VARIANT_NESTING = 6;
955
+ function plainSelectorOf(w) {
956
+ if (!w?.selectorSuffix) return null;
957
+ if (w.replaceAmpersand || w.atRule || w.startingStyle || w.alternate) return null;
958
+ return w.selectorSuffix;
959
+ }
960
+ function resolveRelationalVariant(inner, anchor, combinator, resolveInner) {
818
961
  if (inner.startsWith("[") && inner.endsWith("]")) {
819
962
  const sel = sanitizeVariantBracket(inner.slice(1, -1));
820
- return sel ? { selectorSuffix: `${anchor}:is(${sel})${combinator}`, replaceAmpersand: true } : null;
963
+ if (!sel) return null;
964
+ if (sel.includes("&")) {
965
+ return {
966
+ selectorSuffix: `${sel.replaceAll("&", anchor)}${combinator}`,
967
+ replaceAmpersand: true
968
+ };
969
+ }
970
+ return { selectorSuffix: `${anchor}:is(${sel})${combinator}`, replaceAmpersand: true };
821
971
  }
822
972
  if (Object.hasOwn(PSEUDO_CLASSES, inner)) {
823
973
  return {
@@ -825,6 +975,10 @@ function resolveRelationalVariant(inner, anchor, combinator) {
825
975
  replaceAmpersand: true
826
976
  };
827
977
  }
978
+ const nested = plainSelectorOf(resolveInner(inner));
979
+ if (nested !== null) {
980
+ return { selectorSuffix: `${anchor}${nested}${combinator}`, replaceAmpersand: true };
981
+ }
828
982
  return void 0;
829
983
  }
830
984
  var ATTR_NAME_RE = /^[a-z][a-z0-9-]*$/i;
@@ -905,6 +1059,26 @@ function listVariants(theme) {
905
1059
  out.push(...VARIANT_PATTERNS);
906
1060
  return out;
907
1061
  }
1062
+ function appearanceVariant(variant, strategy) {
1063
+ const media = MEDIA_VARIANTS[variant].wrapper;
1064
+ if (strategy.kind === "media") return media;
1065
+ if (strategy.kind === "selector") {
1066
+ const sel = strategy.selector;
1067
+ return Object.freeze(
1068
+ variant === "dark" ? { selectorSuffix: `:where(${sel}, ${sel} *) &`, replaceAmpersand: true } : { selectorSuffix: `:where(:not(${sel}, ${sel} *)) &`, replaceAmpersand: true }
1069
+ );
1070
+ }
1071
+ const overridden = variant === "dark" ? "light" : "dark";
1072
+ return Object.freeze({
1073
+ selectorSuffix: `:where(html[data-appearance="${variant}"]) &`,
1074
+ replaceAmpersand: true,
1075
+ alternate: Object.freeze({
1076
+ selectorSuffix: `:where(html:not([data-appearance="${overridden}"])) &`,
1077
+ replaceAmpersand: true,
1078
+ atRule: media.atRule
1079
+ })
1080
+ });
1081
+ }
908
1082
  function resolveVariant(variant, theme, customVariantMap, memo) {
909
1083
  if (!memo) return resolveVariantUncached(variant, theme, customVariantMap);
910
1084
  const cached = memo.get(variant);
@@ -913,72 +1087,91 @@ function resolveVariant(variant, theme, customVariantMap, memo) {
913
1087
  memo.set(variant, wrapper);
914
1088
  return wrapper;
915
1089
  }
916
- function resolveVariantUncached(variant, theme, customVariantMap) {
1090
+ function resolveVariantUncached(variant, theme, customVariantMap, depth = 0) {
1091
+ const inner = (v) => depth >= MAX_VARIANT_NESTING ? null : resolveVariantUncached(v, theme, customVariantMap, depth + 1);
917
1092
  if (variant.startsWith("[") && variant.endsWith("]")) {
918
1093
  return resolveArbitraryVariant(variant);
919
1094
  }
920
- if (Object.hasOwn(theme.breakpoints, variant)) {
921
- const bp = theme.breakpoints[variant];
922
- if (!SAFE_CSS_LENGTH_RE.test(bp)) return null;
923
- return { atRule: `@media (min-width: ${bp})` };
1095
+ {
1096
+ const bp = breakpointValue(variant, theme);
1097
+ if (bp !== null) return { atRule: `@media (min-width: ${bp})` };
924
1098
  }
925
- if (variant.startsWith("min-[") && variant.endsWith("]")) {
926
- const v = variant.slice(5, -1);
927
- return SAFE_CSS_LENGTH_RE.test(v) ? { atRule: `@media (width >= ${v})` } : null;
1099
+ if (variant.startsWith("min-")) {
1100
+ const v = widthBound(variant.slice(4), theme);
1101
+ if (v !== null) return { atRule: `@media (width >= ${v})` };
928
1102
  }
929
- if (variant.startsWith("max-[") && variant.endsWith("]")) {
930
- const v = variant.slice(5, -1);
931
- return SAFE_CSS_LENGTH_RE.test(v) ? { atRule: `@media (width < ${v})` } : null;
1103
+ if (variant.startsWith("max-")) {
1104
+ const v = widthBound(variant.slice(4), theme);
1105
+ if (v !== null) return { atRule: `@media (width < ${v})` };
932
1106
  }
933
1107
  if (variant.startsWith("@")) {
934
1108
  const rest = variant.slice(1);
935
1109
  const slashIdx = rest.indexOf("/");
936
- if (slashIdx !== -1) {
937
- const containerName = rest.slice(0, slashIdx);
938
- const bp = rest.slice(slashIdx + 1);
939
- if (Object.hasOwn(theme.breakpoints, bp) && CSS_CUSTOM_IDENT_RE.test(containerName)) {
940
- const bpVal = theme.breakpoints[bp];
941
- if (!SAFE_CSS_LENGTH_RE.test(bpVal)) return null;
942
- return { atRule: `@container ${containerName} (min-width: ${bpVal})` };
1110
+ const named = slashIdx !== -1 && CSS_CUSTOM_IDENT_RE.test(rest.slice(0, slashIdx));
1111
+ if (slashIdx === -1 || named) {
1112
+ const scope = named ? `${rest.slice(0, slashIdx)} ` : "";
1113
+ const size = slashIdx === -1 ? rest : rest.slice(slashIdx + 1);
1114
+ const bp = breakpointValue(size, theme);
1115
+ if (bp !== null) return { atRule: `@container ${scope}(min-width: ${bp})` };
1116
+ if (size.startsWith("min-")) {
1117
+ const v = breakpointValue(size.slice(4), theme);
1118
+ if (v !== null) return { atRule: `@container ${scope}(width >= ${v})` };
1119
+ }
1120
+ if (size.startsWith("max-")) {
1121
+ const v = breakpointValue(size.slice(4), theme);
1122
+ if (v !== null) return { atRule: `@container ${scope}(width < ${v})` };
943
1123
  }
944
- }
945
- if (Object.hasOwn(theme.breakpoints, rest)) {
946
- const bpVal = theme.breakpoints[rest];
947
- if (!SAFE_CSS_LENGTH_RE.test(bpVal)) return null;
948
- return { atRule: `@container (min-width: ${bpVal})` };
949
1124
  }
950
1125
  }
951
1126
  if (Object.hasOwn(PSEUDO_CLASS_WRAPPERS, variant)) {
952
1127
  return PSEUDO_CLASS_WRAPPERS[variant];
953
1128
  }
954
1129
  if (variant.startsWith("group-")) {
955
- const r = resolveRelationalVariant(variant.slice(6), ".group", " &");
1130
+ const { state, anchor } = splitRelationalName(variant.slice(6), "group");
1131
+ if (anchor === null) return null;
1132
+ const r = resolveRelationalVariant(state, anchor, " &", inner);
956
1133
  if (r !== void 0) return r;
957
1134
  }
958
1135
  if (variant.startsWith("peer-")) {
959
- const r = resolveRelationalVariant(variant.slice(5), ".peer", " ~ &");
1136
+ const { state, anchor } = splitRelationalName(variant.slice(5), "peer");
1137
+ if (anchor === null) return null;
1138
+ const r = resolveRelationalVariant(state, anchor, " ~ &", inner);
960
1139
  if (r !== void 0) return r;
961
1140
  }
962
1141
  if (variant.startsWith("in-[") && variant.endsWith("]")) {
963
1142
  const sel = sanitizeVariantBracket(variant.slice(4, -1));
964
- return sel ? { selectorSuffix: `:where(${sel}) &`, replaceAmpersand: true } : null;
1143
+ return sel && !sel.includes("&") ? { selectorSuffix: `:where(${sel}) &`, replaceAmpersand: true } : null;
1144
+ }
1145
+ if (variant.startsWith("in-")) {
1146
+ const nested = plainSelectorOf(inner(variant.slice(3)));
1147
+ if (nested !== null) {
1148
+ return { selectorSuffix: `:where(*${nested}) &`, replaceAmpersand: true };
1149
+ }
965
1150
  }
966
1151
  if (Object.hasOwn(PSEUDO_ELEMENT_WRAPPERS, variant)) {
967
1152
  return PSEUDO_ELEMENT_WRAPPERS[variant];
968
1153
  }
1154
+ if (variant === "dark" || variant === "light") {
1155
+ return appearanceVariant(variant, theme.darkConfig.variant);
1156
+ }
969
1157
  if (Object.hasOwn(MEDIA_VARIANTS, variant)) return MEDIA_VARIANTS[variant].wrapper;
970
1158
  if (Object.hasOwn(SPECIAL_SELECTORS, variant)) return SPECIAL_SELECTORS[variant].wrapper;
971
1159
  if (variant.startsWith("supports-[") && variant.endsWith("]")) {
972
- const inner = decodeArbitraryValue(variant.slice(10, -1));
973
- if (!inner || inner.length > MAX_CUSTOM_VARIANT_SELECTOR_LENGTH || /[{}]/.test(inner))
1160
+ const inner2 = decodeArbitraryValue(variant.slice(10, -1));
1161
+ if (!inner2 || inner2.length > MAX_CUSTOM_VARIANT_SELECTOR_LENGTH || /[{}]/.test(inner2))
974
1162
  return null;
975
- return { atRule: `@supports ${inner.startsWith("(") ? inner : `(${inner})`}` };
1163
+ return { atRule: `@supports ${inner2.startsWith("(") ? inner2 : `(${inner2})`}` };
976
1164
  }
977
1165
  for (const [prefix, fn] of NTH_VARIANTS) {
978
1166
  if (variant.startsWith(prefix) && variant.endsWith("]")) {
979
1167
  const arg = variant.slice(prefix.length, -1).trim();
980
1168
  return /^(odd|even|[-+\dn\s]+)$/i.test(arg) ? { selectorSuffix: `:${fn}(${arg})` } : null;
981
1169
  }
1170
+ const bare = prefix.slice(0, -1);
1171
+ if (variant.startsWith(bare)) {
1172
+ const arg = variant.slice(bare.length);
1173
+ if (/^\d+$/.test(arg)) return { selectorSuffix: `:${fn}(${arg})` };
1174
+ }
982
1175
  }
983
1176
  if (variant.startsWith("data-")) {
984
1177
  const r = resolveAttributeVariant(variant, "data", "");
@@ -989,17 +1182,34 @@ function resolveVariantUncached(variant, theme, customVariantMap) {
989
1182
  if (r !== void 0) return r;
990
1183
  }
991
1184
  if (variant.startsWith("has-[") && variant.endsWith("]")) {
992
- const inner = sanitizeVariantBracket(variant.slice(5, -1));
993
- if (inner) return { selectorSuffix: `:has(${inner})` };
1185
+ const sel = sanitizeVariantBracket(variant.slice(5, -1));
1186
+ if (sel && !sel.includes("&")) return { selectorSuffix: `:has(${sel})` };
1187
+ }
1188
+ if (variant.startsWith("has-")) {
1189
+ const nested = plainSelectorOf(inner(variant.slice(4)));
1190
+ if (nested !== null) return { selectorSuffix: `:has(${nested})` };
994
1191
  }
995
1192
  if (variant.startsWith("not-")) {
996
- const inner = variant.slice(4);
997
- if (inner.startsWith("[") && inner.endsWith("]")) {
998
- const sel = sanitizeVariantBracket(inner.slice(1, -1));
999
- if (sel) return { selectorSuffix: `:not(${sel})` };
1000
- }
1001
- if (Object.hasOwn(PSEUDO_CLASSES, inner)) {
1002
- return { selectorSuffix: `:not(${PSEUDO_CLASSES[inner].suffix})` };
1193
+ const rest = variant.slice(4);
1194
+ if (rest.startsWith("[") && rest.endsWith("]")) {
1195
+ const sel = sanitizeVariantBracket(rest.slice(1, -1));
1196
+ if (sel && !sel.includes("&")) return { selectorSuffix: `:not(${sel})` };
1197
+ }
1198
+ if (Object.hasOwn(PSEUDO_CLASSES, rest)) {
1199
+ return { selectorSuffix: `:not(${PSEUDO_CLASSES[rest].suffix})` };
1200
+ }
1201
+ const nestedWrapper = inner(rest);
1202
+ const nested = plainSelectorOf(nestedWrapper);
1203
+ if (nested !== null) return { selectorSuffix: `:not(${nested})` };
1204
+ const atRule = nestedWrapper?.atRule;
1205
+ if (atRule && !nestedWrapper.selectorSuffix && !nestedWrapper.alternate) {
1206
+ const spaceIdx = atRule.indexOf(" ");
1207
+ if (spaceIdx !== -1) {
1208
+ const name = atRule.slice(0, spaceIdx);
1209
+ if (name === "@media" || name === "@supports") {
1210
+ return { atRule: `${name} not ${atRule.slice(spaceIdx + 1)}` };
1211
+ }
1212
+ }
1003
1213
  }
1004
1214
  }
1005
1215
  const cv = customVariantMap?.get(variant);
@@ -1038,24 +1248,31 @@ function splitSelectorList(selector) {
1038
1248
  results.push(selector.slice(start).trim());
1039
1249
  return results.filter(Boolean);
1040
1250
  }
1251
+ var MEDIA_WORLD = 1;
1252
+ function applyOneWrapper(applied, w) {
1253
+ let selector = applied.selector;
1254
+ if (w.selectorSuffix) {
1255
+ const branches = splitSelectorList(selector);
1256
+ const suffix = w.selectorSuffix;
1257
+ selector = w.replaceAmpersand ? branches.map((b) => suffix.replace(/&/g, b)).join(", ") : branches.map((b) => b + suffix).join(", ");
1258
+ }
1259
+ return {
1260
+ selector,
1261
+ atRules: w.atRule ? [...applied.atRules, w.atRule] : applied.atRules,
1262
+ startingStyle: applied.startingStyle || w.startingStyle === true
1263
+ };
1264
+ }
1041
1265
  function applyVariantWrappers(baseSelector, wrappers) {
1042
- let selector = baseSelector;
1043
- const atRules = [];
1044
- let startingStyle = false;
1045
- for (const w of wrappers) {
1046
- if (w.selectorSuffix) {
1047
- const branches = splitSelectorList(selector);
1048
- const suffix = w.selectorSuffix;
1049
- selector = w.replaceAmpersand ? branches.map((b) => suffix.replace(/&/g, b)).join(", ") : branches.map((b) => b + suffix).join(", ");
1050
- }
1051
- if (w.atRule) {
1052
- atRules.push(w.atRule);
1053
- }
1054
- if (w.startingStyle) {
1055
- startingStyle = true;
1266
+ const worlds = wrappers.some((w) => w.alternate) ? 2 : 1;
1267
+ const out = [];
1268
+ for (let world = 0; world < worlds; world++) {
1269
+ let branch = { selector: baseSelector, atRules: [], startingStyle: false };
1270
+ for (const w of wrappers) {
1271
+ branch = applyOneWrapper(branch, world === MEDIA_WORLD ? w.alternate ?? w : w);
1056
1272
  }
1273
+ out.push(branch);
1057
1274
  }
1058
- return { selector, atRules, startingStyle };
1275
+ return out;
1059
1276
  }
1060
1277
 
1061
1278
  // src/engine/suggest.ts
@@ -1114,47 +1331,10 @@ function findClosest(input, candidates, maxDistance) {
1114
1331
  return best;
1115
1332
  }
1116
1333
 
1117
- // src/css/escape.ts
1118
- function escapeSelector(raw) {
1119
- let out = null;
1120
- let runStart = 0;
1121
- for (let i = 0; i < raw.length; i++) {
1122
- const code = raw.charCodeAt(i);
1123
- if (code >= 97 && code <= 122 || // a-z
1124
- code >= 65 && code <= 90 || // A-Z
1125
- code === 45 || // -
1126
- code === 95 || // _
1127
- code >= 128) {
1128
- continue;
1129
- }
1130
- if (code >= 48 && code <= 57 && // 0-9
1131
- i !== 0 && !(i === 1 && raw.charCodeAt(0) === 45)) {
1132
- continue;
1133
- }
1134
- let escaped;
1135
- if (code === 0) {
1136
- escaped = "\uFFFD";
1137
- } else if (code >= 48 && code <= 57) {
1138
- escaped = `\\${code.toString(16)} `;
1139
- } else if (code === 9 || code === 10 || code === 12 || code === 13) {
1140
- escaped = `\\${raw[i]}`;
1141
- } else if (code < 32 || code === 127) {
1142
- escaped = `\\${code.toString(16)} `;
1143
- } else {
1144
- escaped = `\\${raw[i]}`;
1145
- }
1146
- if (out === null) out = [];
1147
- out.push(raw.slice(runStart, i), escaped);
1148
- runStart = i + 1;
1149
- }
1150
- if (out === null) return raw;
1151
- out.push(raw.slice(runStart));
1152
- return out.join("");
1153
- }
1154
-
1155
1334
  // src/css/token-refs.ts
1156
- var COLOR_STOP_REF_RE = /var\(--color-([a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*)(?:-(\d+))?\)/g;
1157
- var SHADOW_VAR_REF_RE = /var\(--shadow-([a-z0-9]+(?:-[a-z0-9]+)*)\)/g;
1335
+ var COLOR_STOP_REF_RE = /var\(--color-([a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*)(?:-(\d+))?\s*[,)]/g;
1336
+ var COLOR_VAR_REF_RE = /var\(--color-([\w-]+)\s*[,)]/g;
1337
+ var SHADOW_VAR_REF_RE = /var\(--shadow-(DEFAULT|[a-z0-9]+(?:-[a-z0-9]+)*)\)/g;
1158
1338
  var TEXT_VAR_REF_RE = /var\(--text-([a-z0-9]+(?:-[a-z0-9]+)*?)(?:-leading)?\)/g;
1159
1339
  var FONT_VAR_REF_RE = /var\(--font-([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\)/g;
1160
1340
  var ANIMATE_VAR_REF_RE = /var\(--animate-([a-z0-9]+(?:-[a-z0-9]+)*)\)/g;
@@ -1179,9 +1359,37 @@ function multi(...pairs) {
1179
1359
  declarations: pairs.map(([property, value]) => ({ property, value }))
1180
1360
  };
1181
1361
  }
1362
+ function hasBalancedDelimiters(value) {
1363
+ const open = [];
1364
+ for (let i = 0; i < value.length; i++) {
1365
+ const ch = value[i];
1366
+ if (ch === "\\") {
1367
+ i++;
1368
+ } else if (ch === '"' || ch === "'") {
1369
+ const quote = ch;
1370
+ i++;
1371
+ while (i < value.length && value[i] !== quote) {
1372
+ if (value[i] === "\\") i++;
1373
+ i++;
1374
+ }
1375
+ if (i >= value.length) return false;
1376
+ } else if (ch === "(" || ch === "[") {
1377
+ open.push(ch);
1378
+ } else if (ch === ")") {
1379
+ if (open.pop() !== "(") return false;
1380
+ } else if (ch === "]") {
1381
+ if (open.pop() !== "[") return false;
1382
+ }
1383
+ }
1384
+ return open.length === 0;
1385
+ }
1182
1386
  function extractArbitrary(value) {
1183
1387
  if (!value) return null;
1184
- return value.startsWith("[") && value.endsWith("]") ? decodeArbitraryValue(value.slice(1, -1)) : null;
1388
+ if (!value.startsWith("[") || !value.endsWith("]")) return null;
1389
+ const inner = value.slice(1, -1);
1390
+ if (inner === "") return null;
1391
+ const decoded = decodeArbitraryValue(inner);
1392
+ return hasBalancedDelimiters(decoded) ? decoded : null;
1185
1393
  }
1186
1394
  function normalizeDecimalToken(value) {
1187
1395
  return value.replaceAll("_", ".");
@@ -1196,6 +1404,44 @@ function spacingLookup(value, negative = false) {
1196
1404
  const expr = `calc(${normalized} * var(--spacing))`;
1197
1405
  return negative ? `calc(${normalized} * var(--spacing) * -1)` : expr;
1198
1406
  }
1407
+ var FRACTIONAL = Object.freeze({
1408
+ "1/2": "50%",
1409
+ "1/3": "33.333333%",
1410
+ "2/3": "66.666667%",
1411
+ "1/4": "25%",
1412
+ "2/4": "50%",
1413
+ "3/4": "75%",
1414
+ "1/5": "20%",
1415
+ "2/5": "40%",
1416
+ "3/5": "60%",
1417
+ "4/5": "80%",
1418
+ "1/6": "16.666667%",
1419
+ "2/6": "33.333333%",
1420
+ "3/6": "50%",
1421
+ "4/6": "66.666667%",
1422
+ "5/6": "83.333333%",
1423
+ "1/12": "8.333333%",
1424
+ "2/12": "16.666667%",
1425
+ "3/12": "25%",
1426
+ "4/12": "33.333333%",
1427
+ "5/12": "41.666667%",
1428
+ "6/12": "50%",
1429
+ "7/12": "58.333333%",
1430
+ "8/12": "66.666667%",
1431
+ "9/12": "75%",
1432
+ "10/12": "83.333333%",
1433
+ "11/12": "91.666667%"
1434
+ });
1435
+ function fractionValue(name) {
1436
+ if (Object.hasOwn(FRACTIONAL, name)) return FRACTIONAL[name];
1437
+ const slash = name.indexOf("/");
1438
+ if (slash === -1) return null;
1439
+ const numerator = name.slice(0, slash);
1440
+ const denominator = name.slice(slash + 1);
1441
+ if (!INTEGER_RE.test(numerator) || !INTEGER_RE.test(denominator)) return null;
1442
+ if (Number(denominator) === 0) return null;
1443
+ return `calc(${numerator} / ${denominator} * 100%)`;
1444
+ }
1199
1445
  function deepFreezeUtilityMap(map) {
1200
1446
  for (const value of Object.values(map)) {
1201
1447
  Object.freeze(value.declarations);
@@ -1452,6 +1698,35 @@ var DIRECTIVE_TYPE_NAMES = [
1452
1698
  "register"
1453
1699
  ];
1454
1700
  var REMOVAL_KEY = "--ri-rm";
1701
+ function legacySyntaxWarning(legacy, canonical, directiveName) {
1702
+ const where = directiveName ? ` in @${directiveName}` : "";
1703
+ return `[RI-1046] \`${legacy}\`${where} is a deprecated spelling \u2014 write \`${canonical}\` instead. The old form is not valid CSS, so a formatter cannot read the file; it still works, and will be removed at 1.0.`;
1704
+ }
1705
+ function hasTopLevelKeyframes(body) {
1706
+ let depth = 0;
1707
+ for (let i = 0; i < body.length; i++) {
1708
+ const ch = body[i];
1709
+ if (ch === '"' || ch === "'") {
1710
+ const quote = ch;
1711
+ i++;
1712
+ while (i < body.length && body[i] !== quote) {
1713
+ if (body[i] === "\\") i++;
1714
+ i++;
1715
+ }
1716
+ continue;
1717
+ }
1718
+ if (ch === "{") depth++;
1719
+ else if (ch === "}") depth = Math.max(0, depth - 1);
1720
+ else if (ch === "@" && depth === 0 && body.startsWith("@keyframes", i)) {
1721
+ const after = body[i + "@keyframes".length];
1722
+ if (after === void 0 || !/[\w-]/.test(after)) return true;
1723
+ }
1724
+ }
1725
+ return false;
1726
+ }
1727
+ function isRemovalValue(value) {
1728
+ return value === "initial";
1729
+ }
1455
1730
  var IDENT_KEY_RE = /^[\w-]+$/;
1456
1731
  var WS_CHAR_RE = /\s/;
1457
1732
  function topLevelIndexOf(str, char) {
@@ -1484,7 +1759,9 @@ function* scanEntries(src, opts) {
1484
1759
  let end = i + 1;
1485
1760
  while (end < src.length && /[\w-]/.test(src[end])) end++;
1486
1761
  const name = src.slice(i + 1, end);
1487
- if (name) yield { key: name, value: "", removal: true, start: i, end };
1762
+ if (name) {
1763
+ yield { key: name, value: "", removal: true, legacyRemoval: true, start: i, end };
1764
+ }
1488
1765
  i = end;
1489
1766
  continue;
1490
1767
  }
@@ -1544,6 +1821,10 @@ function* scanEntries(src, opts) {
1544
1821
  if (value) yield { key: value, value: "", removal: true, start: entryStart, end: i };
1545
1822
  continue;
1546
1823
  }
1824
+ if (isRemovalValue(value)) {
1825
+ yield { key, value: "", removal: true, start: entryStart, end: i };
1826
+ continue;
1827
+ }
1547
1828
  yield { key, value, block, start: entryStart, end: i };
1548
1829
  continue;
1549
1830
  }
@@ -1556,6 +1837,10 @@ function* scanEntries(src, opts) {
1556
1837
  if (value) yield { key: value, value: "", removal: true, start: entryStart, end: i };
1557
1838
  continue;
1558
1839
  }
1840
+ if (isRemovalValue(value)) {
1841
+ yield { key, value: "", removal: true, start: entryStart, end: i };
1842
+ continue;
1843
+ }
1559
1844
  yield { key, value, start: entryStart, end: i };
1560
1845
  }
1561
1846
  }
@@ -1567,7 +1852,9 @@ function parseKeyValueBody(body, warnings, directiveName) {
1567
1852
  const line = raw.trim();
1568
1853
  if (!line) return;
1569
1854
  if (line.startsWith("!")) {
1570
- removals.push(line.slice(1).trim());
1855
+ const name = line.slice(1).trim();
1856
+ removals.push(name);
1857
+ warnings?.push(legacySyntaxWarning(`!${name};`, `${name}: initial;`, directiveName));
1571
1858
  return;
1572
1859
  }
1573
1860
  const colonIdx = line.indexOf(":");
@@ -1578,6 +1865,10 @@ function parseKeyValueBody(body, warnings, directiveName) {
1578
1865
  removals.push(value);
1579
1866
  return;
1580
1867
  }
1868
+ if (key && isRemovalValue(value)) {
1869
+ removals.push(key);
1870
+ return;
1871
+ }
1581
1872
  if (key && !IDENT_KEY_RE.test(key)) {
1582
1873
  warnings?.push(
1583
1874
  `[RI-1035] Invalid @${directiveName ?? "directive"} entry key "${key}" \u2014 keys may only contain letters, numbers, hyphens, and underscores. The entry was skipped.`
@@ -1911,42 +2202,17 @@ var VIEWPORT_SIZES = Object.freeze({
1911
2202
  lvh: "100lvh",
1912
2203
  dvh: "100dvh"
1913
2204
  });
1914
- var FRACTIONAL = Object.freeze({
1915
- "1/2": "50%",
1916
- "1/3": "33.333333%",
1917
- "2/3": "66.666667%",
1918
- "1/4": "25%",
1919
- "2/4": "50%",
1920
- "3/4": "75%",
1921
- "1/5": "20%",
1922
- "2/5": "40%",
1923
- "3/5": "60%",
1924
- "4/5": "80%",
1925
- "1/6": "16.666667%",
1926
- "2/6": "33.333333%",
1927
- "3/6": "50%",
1928
- "4/6": "66.666667%",
1929
- "5/6": "83.333333%",
1930
- "1/12": "8.333333%",
1931
- "2/12": "16.666667%",
1932
- "3/12": "25%",
1933
- "4/12": "33.333333%",
1934
- "5/12": "41.666667%",
1935
- "6/12": "50%",
1936
- "7/12": "58.333333%",
1937
- "8/12": "66.666667%",
1938
- "9/12": "75%",
1939
- "10/12": "83.333333%",
1940
- "11/12": "91.666667%"
1941
- });
1942
2205
  function resolveSizeValue(val, named, negative) {
1943
2206
  const arb = extractArbitrary(val);
1944
2207
  if (arb !== null) return negative ? `calc(${arb} * -1)` : arb;
1945
2208
  if (Object.hasOwn(named, val)) return named[val];
1946
- if (Object.hasOwn(FRACTIONAL, val)) return FRACTIONAL[val];
2209
+ const fraction = fractionValue(val);
2210
+ if (fraction !== null) return fraction;
1947
2211
  return spacingLookup(val, negative);
1948
2212
  }
1949
2213
  var CONTAINER_WIDTHS = Object.freeze({
2214
+ "3xs": "16rem",
2215
+ "2xs": "18rem",
1950
2216
  xs: "20rem",
1951
2217
  sm: "24rem",
1952
2218
  md: "28rem",
@@ -1971,7 +2237,11 @@ var MAX_W_NAMED = Object.freeze({
1971
2237
  ...VIEWPORT_SIZES
1972
2238
  });
1973
2239
  var MIN_W_NAMED = Object.freeze({
2240
+ // `auto` is a min-* value only: `max-width: auto` is not valid CSS, and
2241
+ // Tailwind registers no `max-w-auto` either.
2242
+ auto: "auto",
1974
2243
  "0": "0px",
2244
+ ...CONTAINER_WIDTHS,
1975
2245
  full: "100%",
1976
2246
  min: "min-content",
1977
2247
  max: "max-content",
@@ -1980,6 +2250,7 @@ var MIN_W_NAMED = Object.freeze({
1980
2250
  ...VIEWPORT_SIZES
1981
2251
  });
1982
2252
  var MIN_H_NAMED = Object.freeze({
2253
+ auto: "auto",
1983
2254
  "0": "0px",
1984
2255
  full: "100%",
1985
2256
  min: "min-content",
@@ -1999,6 +2270,15 @@ var MAX_H_NAMED = Object.freeze({
1999
2270
  screen: "100vh",
2000
2271
  ...VIEWPORT_SIZES
2001
2272
  });
2273
+ var SIZING_KEYWORDS = Object.freeze([
2274
+ .../* @__PURE__ */ new Set([
2275
+ ...Object.keys(NAMED_SIZES),
2276
+ ...Object.keys(MIN_W_NAMED),
2277
+ ...Object.keys(MAX_W_NAMED),
2278
+ ...Object.keys(MIN_H_NAMED),
2279
+ ...Object.keys(MAX_H_NAMED)
2280
+ ])
2281
+ ]);
2002
2282
  var CONSTRAINED_SIZE_TABLE = [
2003
2283
  ["min-w-", "min-width", MIN_W_NAMED],
2004
2284
  ["max-w-", "max-width", MAX_W_NAMED],
@@ -2011,7 +2291,8 @@ var CONSTRAINED_SIZE_TABLE = [
2011
2291
  ];
2012
2292
  function resolveConstrainedSize(val, named, property) {
2013
2293
  if (Object.hasOwn(named, val)) return single(property, named[val]);
2014
- if (Object.hasOwn(FRACTIONAL, val)) return single(property, FRACTIONAL[val]);
2294
+ const fraction = fractionValue(val);
2295
+ if (fraction !== null) return single(property, fraction);
2015
2296
  const arb = extractArbitrary(val);
2016
2297
  if (arb !== null) return single(property, arb);
2017
2298
  const spacingVal = spacingLookup(val, false);
@@ -2031,6 +2312,7 @@ function sizingGenerator(_utility, _value, full, negative, _theme, _warnings) {
2031
2312
  const val = full.slice(2);
2032
2313
  const resolved = resolveSizeValue(val, NAMED_SIZES, negative);
2033
2314
  if (resolved) return single("width", resolved);
2315
+ if (Object.hasOwn(CONTAINER_WIDTHS, val)) return single("width", CONTAINER_WIDTHS[val]);
2034
2316
  }
2035
2317
  if (full.startsWith("h-")) {
2036
2318
  const val = full.slice(2);
@@ -2041,6 +2323,9 @@ function sizingGenerator(_utility, _value, full, negative, _theme, _warnings) {
2041
2323
  const val = full.slice(7);
2042
2324
  const resolved = resolveSizeValue(val, NAMED_SIZES, negative);
2043
2325
  if (resolved) return single("inline-size", resolved);
2326
+ if (Object.hasOwn(CONTAINER_WIDTHS, val)) {
2327
+ return single("inline-size", CONTAINER_WIDTHS[val]);
2328
+ }
2044
2329
  }
2045
2330
  if (full.startsWith("block-")) {
2046
2331
  const val = full.slice(6);
@@ -2397,6 +2682,15 @@ function colorGenerator(utility, value, full, _negative, theme, _warnings, dataT
2397
2682
  if (resolved) return single(cssProperty, resolved);
2398
2683
  }
2399
2684
  }
2685
+ if (full.startsWith("placeholder-")) {
2686
+ const resolved = resolveColor(full.slice(12), theme, dataType);
2687
+ if (resolved) {
2688
+ return {
2689
+ declarations: [{ property: "color", value: resolved }],
2690
+ nestedSelector: "&::placeholder"
2691
+ };
2692
+ }
2693
+ }
2400
2694
  if (full.startsWith("divide-") && !DIVIDE_NON_COLOR_RE.test(full)) {
2401
2695
  const colorName = full.slice(7);
2402
2696
  const resolved = resolveColor(colorName, theme, dataType);
@@ -2618,8 +2912,13 @@ function splitLineHeightModifier(s) {
2618
2912
  }
2619
2913
  return { base: s, modifier: null };
2620
2914
  }
2915
+ function numericLeading(value) {
2916
+ return DECIMAL_RE.test(value) ? spacingLookup(value) : null;
2917
+ }
2621
2918
  function resolveLineHeightModifier(mod, theme) {
2622
2919
  if (Object.hasOwn(theme.leading, mod)) return theme.leading[mod];
2920
+ const numeric = numericLeading(mod);
2921
+ if (numeric !== null) return numeric;
2623
2922
  if (mod.startsWith("(") && mod.endsWith(")")) {
2624
2923
  const inner = mod.slice(1, -1);
2625
2924
  return /^--[a-zA-Z_][\w-]*$/.test(inner) ? `var(${inner})` : null;
@@ -2715,7 +3014,9 @@ function typographyGenerator(_utility, _value, full, negative, theme, warnings,
2715
3014
  if (full.startsWith("text-")) {
2716
3015
  const { base: sizeName, modifier } = splitLineHeightModifier(full.slice(5));
2717
3016
  const lh = modifier !== null ? resolveLineHeightModifier(modifier, theme) : null;
3017
+ const modifierFailed = modifier !== null && lh === null;
2718
3018
  if (Object.hasOwn(theme.text, sizeName)) {
3019
+ if (modifierFailed) return null;
2719
3020
  return multi(
2720
3021
  ["font-size", `var(--text-${sizeName})`],
2721
3022
  ["line-height", lh ?? `var(--text-${sizeName}-leading)`]
@@ -2724,6 +3025,7 @@ function typographyGenerator(_utility, _value, full, negative, theme, warnings,
2724
3025
  if (!isBracketedColor(sizeName)) {
2725
3026
  const arbText = extractArbitrary(sizeName);
2726
3027
  if (arbText) {
3028
+ if (modifierFailed) return null;
2727
3029
  return lh !== null ? multi(["font-size", arbText], ["line-height", lh]) : single("font-size", arbText);
2728
3030
  }
2729
3031
  }
@@ -2772,6 +3074,8 @@ function typographyGenerator(_utility, _value, full, negative, theme, warnings,
2772
3074
  if (Object.hasOwn(theme.leading, name)) {
2773
3075
  return single("line-height", theme.leading[name]);
2774
3076
  }
3077
+ const numeric = numericLeading(name);
3078
+ if (numeric !== null) return single("line-height", numeric);
2775
3079
  const arbLeading = extractArbitrary(name);
2776
3080
  if (arbLeading) return single("line-height", arbLeading);
2777
3081
  if (name === "px") return single("line-height", "1px");
@@ -2910,11 +3214,6 @@ function checkAppliedFontWeights(classes, theme, warnings) {
2910
3214
 
2911
3215
  // src/utilities/layout.ts
2912
3216
  var ASPECT_RATIO_RE = /^(\d+)\/(\d+)$/;
2913
- var CONTAINER_SCALE = Object.freeze({
2914
- "3xs": "16rem",
2915
- "2xs": "18rem",
2916
- ...CONTAINER_WIDTHS
2917
- });
2918
3217
  function resolveGridLine(name, negative) {
2919
3218
  if (name === "auto") return "auto";
2920
3219
  if (INTEGER_RE.test(name)) return negative ? `calc(${name} * -1)` : name;
@@ -2922,14 +3221,6 @@ function resolveGridLine(name, negative) {
2922
3221
  if (arb !== null) return negative ? `calc(${arb} * -1)` : arb;
2923
3222
  return null;
2924
3223
  }
2925
- function fractionToPercent(name) {
2926
- const slash = name.indexOf("/");
2927
- if (slash === -1) return null;
2928
- const num = Number(name.slice(0, slash));
2929
- const den = Number(name.slice(slash + 1));
2930
- if (!Number.isFinite(num) || !Number.isFinite(den) || den <= 0) return null;
2931
- return `${Math.round(num / den * 1e6) / 1e4}%`;
2932
- }
2933
3224
  function resolveGridTemplate(name, property) {
2934
3225
  if (name === "none") return single(property, "none");
2935
3226
  if (name === "subgrid") return single(property, "subgrid");
@@ -3465,7 +3756,7 @@ function resolveColumns(full) {
3465
3756
  const name = full.slice(8);
3466
3757
  if (name === "auto") return single("columns", "auto");
3467
3758
  if (INTEGER_RE.test(name)) return single("columns", name);
3468
- if (Object.hasOwn(CONTAINER_SCALE, name)) return single("columns", CONTAINER_SCALE[name]);
3759
+ if (Object.hasOwn(CONTAINER_WIDTHS, name)) return single("columns", CONTAINER_WIDTHS[name]);
3469
3760
  const arb = extractArbitrary(name);
3470
3761
  if (arb) return single("columns", arb);
3471
3762
  return null;
@@ -3490,19 +3781,18 @@ function resolveBasis(full) {
3490
3781
  const name = full.slice(6);
3491
3782
  const arb = extractArbitrary(name);
3492
3783
  if (arb !== null) return single("flex-basis", arb);
3493
- const pct = fractionToPercent(name);
3784
+ if (Object.hasOwn(CONTAINER_WIDTHS, name)) return single("flex-basis", CONTAINER_WIDTHS[name]);
3785
+ const pct = fractionValue(name);
3494
3786
  if (pct !== null) return single("flex-basis", pct);
3495
- if (DECIMAL_RE.test(name)) {
3496
- const sp = spacingLookup(name, false);
3497
- if (sp) return single("flex-basis", sp);
3498
- }
3787
+ const sp = spacingLookup(name, false);
3788
+ if (sp) return single("flex-basis", sp);
3499
3789
  return null;
3500
3790
  }
3501
3791
  function resolveFlex(full) {
3502
3792
  const name = full.slice(5);
3503
3793
  const arb = extractArbitrary(name);
3504
3794
  if (arb !== null) return single("flex", arb);
3505
- const pct = fractionToPercent(name);
3795
+ const pct = fractionValue(name);
3506
3796
  if (pct !== null) return single("flex", pct);
3507
3797
  if (DECIMAL_RE.test(name)) return single("flex", normalizeDecimalToken(name));
3508
3798
  return null;
@@ -3787,9 +4077,17 @@ function borderGenerator(_utility, _value, full, negative, theme, _warnings, dat
3787
4077
  return multi(["corner-shape", arb], ["--ri-rounded-scale", "1"]);
3788
4078
  }
3789
4079
  }
4080
+ if (full === "rounded") {
4081
+ const r = resolveRadius("DEFAULT", theme);
4082
+ if (r) return single("border-radius", r);
4083
+ }
3790
4084
  if (full.startsWith("rounded-")) {
3791
4085
  const rest = full.slice(8);
3792
4086
  for (const [suffix, prop] of ROUNDED_CORNER_ENTRIES) {
4087
+ if (rest === suffix) {
4088
+ const r2 = resolveRadius("DEFAULT", theme);
4089
+ if (r2) return single(prop, r2);
4090
+ }
3793
4091
  if (rest.startsWith(suffix) && rest.charCodeAt(suffix.length) === 45) {
3794
4092
  const size = rest.slice(suffix.length + 1);
3795
4093
  const r2 = resolveRadius(size, theme);
@@ -3797,6 +4095,10 @@ function borderGenerator(_utility, _value, full, negative, theme, _warnings, dat
3797
4095
  }
3798
4096
  }
3799
4097
  for (const [suffix, props] of ROUNDED_SIDE_ENTRIES) {
4098
+ if (rest === suffix) {
4099
+ const r2 = resolveRadius("DEFAULT", theme);
4100
+ if (r2) return multi(...props.map((p) => [p, r2]));
4101
+ }
3800
4102
  if (rest.startsWith(suffix) && rest.charCodeAt(suffix.length) === 45) {
3801
4103
  const size = rest.slice(suffix.length + 1);
3802
4104
  const r2 = resolveRadius(size, theme);
@@ -3864,6 +4166,143 @@ function borderGenerator(_utility, _value, full, negative, theme, _warnings, dat
3864
4166
  return null;
3865
4167
  }
3866
4168
 
4169
+ // src/css/shadow-color.ts
4170
+ var SHADOW_KEYWORDS = /* @__PURE__ */ new Set([
4171
+ "inset",
4172
+ "inherit",
4173
+ "initial",
4174
+ "revert",
4175
+ "revert-layer",
4176
+ "unset"
4177
+ ]);
4178
+ var MATH_FUNCTIONS2 = /* @__PURE__ */ new Set(["calc", "clamp", "min", "max"]);
4179
+ var COLOR_FUNCTIONS = /* @__PURE__ */ new Set([
4180
+ "color",
4181
+ "color-mix",
4182
+ "contrast-color",
4183
+ "device-cmyk",
4184
+ "hsl",
4185
+ "hsla",
4186
+ "hwb",
4187
+ "lab",
4188
+ "lch",
4189
+ "light-dark",
4190
+ "oklab",
4191
+ "oklch",
4192
+ "rgb",
4193
+ "rgba"
4194
+ ]);
4195
+ var LENGTH_START_RE = /^-?(?:\d+|\.\d+)/;
4196
+ function splitShadowLayers(value) {
4197
+ const layers = [];
4198
+ let depth = 0;
4199
+ let start = 0;
4200
+ for (let i = 0; i < value.length; i++) {
4201
+ const ch = value[i];
4202
+ if (ch === "\\") {
4203
+ i++;
4204
+ continue;
4205
+ }
4206
+ if (ch === "(" || ch === "[") depth++;
4207
+ else if (ch === ")" || ch === "]") depth = Math.max(0, depth - 1);
4208
+ else if (ch === "," && depth === 0) {
4209
+ layers.push(value.slice(start, i));
4210
+ start = i + 1;
4211
+ }
4212
+ }
4213
+ layers.push(value.slice(start));
4214
+ return layers;
4215
+ }
4216
+ function tokenizeLayer(layer) {
4217
+ const tokens = [];
4218
+ let i = 0;
4219
+ while (i < layer.length) {
4220
+ if (/\s/.test(layer[i])) {
4221
+ i++;
4222
+ continue;
4223
+ }
4224
+ const start = i;
4225
+ let depth = 0;
4226
+ let isFunction = false;
4227
+ while (i < layer.length) {
4228
+ const ch = layer[i];
4229
+ if (ch === "\\") {
4230
+ i += 2;
4231
+ continue;
4232
+ }
4233
+ if (ch === "(") {
4234
+ if (depth === 0) isFunction = true;
4235
+ depth++;
4236
+ } else if (ch === ")") {
4237
+ depth = Math.max(0, depth - 1);
4238
+ } else if (depth === 0 && /\s/.test(ch)) {
4239
+ break;
4240
+ }
4241
+ i++;
4242
+ }
4243
+ tokens.push({ start, end: i, text: layer.slice(start, i), isFunction });
4244
+ }
4245
+ return tokens;
4246
+ }
4247
+ function functionName(text) {
4248
+ const open = text.indexOf("(");
4249
+ return open === -1 ? "" : text.slice(0, open).toLowerCase();
4250
+ }
4251
+ function slotLayer(layer, colorVar) {
4252
+ const trimmed = layer.trim();
4253
+ if (trimmed === "") return layer;
4254
+ const bang = /!\s*important\s*$/i.exec(trimmed);
4255
+ const core = bang === null ? trimmed : trimmed.slice(0, bang.index).trimEnd();
4256
+ const suffix = bang === null ? "" : ` ${trimmed.slice(bang.index)}`;
4257
+ if (core === "") return layer;
4258
+ const lead = layer.slice(0, layer.indexOf(trimmed));
4259
+ const trail = layer.slice(lead.length + trimmed.length);
4260
+ const tokens = tokenizeLayer(core);
4261
+ let lengths = 0;
4262
+ let values = 0;
4263
+ let last = null;
4264
+ let hit = null;
4265
+ for (const token of tokens) {
4266
+ if (token.isFunction) {
4267
+ const name = functionName(token.text);
4268
+ if (COLOR_FUNCTIONS.has(name)) {
4269
+ hit = token;
4270
+ break;
4271
+ }
4272
+ if (MATH_FUNCTIONS2.has(name)) {
4273
+ lengths++;
4274
+ continue;
4275
+ }
4276
+ last = token;
4277
+ values++;
4278
+ continue;
4279
+ }
4280
+ if (SHADOW_KEYWORDS.has(token.text.toLowerCase())) continue;
4281
+ if (token.text.startsWith("#")) {
4282
+ hit = token;
4283
+ break;
4284
+ }
4285
+ if (LENGTH_START_RE.test(token.text)) {
4286
+ lengths++;
4287
+ continue;
4288
+ }
4289
+ last = token;
4290
+ values++;
4291
+ }
4292
+ const wrap = (token) => {
4293
+ if (token.text.startsWith(`var(${colorVar}`)) return layer;
4294
+ return lead + core.slice(0, token.start) + `var(${colorVar}, ${token.text})` + core.slice(token.end) + suffix + trail;
4295
+ };
4296
+ if (hit !== null) return wrap(hit);
4297
+ if (lengths < 2) return layer;
4298
+ if (values === 0) return `${lead + core} var(${colorVar}, currentColor)${suffix}${trail}`;
4299
+ if (values === 1 && last !== null) return wrap(last);
4300
+ return layer;
4301
+ }
4302
+ function withShadowColorSlotLayers(value, colorVar) {
4303
+ return splitShadowLayers(value).map((layer) => slotLayer(layer, colorVar).trim());
4304
+ }
4305
+
3867
4306
  // src/utilities/effects/shadows.ts
3868
4307
  var SHADOW_COMPOSITION = "var(--ri-inset-shadow, 0 0 #0000), var(--ri-inset-ring-shadow, 0 0 #0000), var(--ri-ring-offset-shadow, 0 0 #0000), var(--ri-ring-shadow, 0 0 #0000), var(--ri-shadow, 0 0 #0000)";
3869
4308
  function composedShadow(slot, value) {
@@ -3887,58 +4326,103 @@ function resolveColorOrArbitrary(name, colorVar, theme, dataType, onArbitrary) {
3887
4326
  }
3888
4327
  return null;
3889
4328
  }
3890
- function resolveShadowFamily(name, noneValue, colorVar, theme, dataType, wrap) {
3891
- if (name === "none") return wrap(noneValue);
3892
- return resolveColorOrArbitrary(name, colorVar, theme, dataType, wrap);
3893
- }
3894
- function resolveShadow(full, theme, dataType) {
3895
- const name = full === "shadow" ? Object.hasOwn(theme.shadows, "DEFAULT") ? "DEFAULT" : Object.hasOwn(theme.shadows, "md") ? "md" : "" : full.slice(7);
3896
- if (Object.hasOwn(theme.shadows, name))
3897
- return composedShadow("--ri-shadow", `var(--shadow-${name})`);
3898
- if (name === "none") return composedShadow("--ri-shadow", "0 0 #0000");
4329
+ function resolveShadowFamily(name, theme, dataType, family) {
4330
+ if (name === "none") return family.wrap([family.none]);
4331
+ if (family.initial && name === "initial") return single(family.colorVar, "initial");
3899
4332
  return resolveColorOrArbitrary(
3900
4333
  name,
3901
- "--ri-shadow-color",
4334
+ family.colorVar,
3902
4335
  theme,
3903
4336
  dataType,
3904
- (arb) => composedShadow("--ri-shadow", arb)
4337
+ (arb) => family.wrap(withShadowColorSlotLayers(arb, family.colorVar))
3905
4338
  );
3906
4339
  }
3907
- function resolveInsetShadow(full, theme, dataType) {
3908
- return resolveShadowFamily(
3909
- full.slice(13),
3910
- // "inset-shadow-".length
3911
- "inset 0 0 #0000",
3912
- "--ri-inset-shadow-color",
3913
- theme,
3914
- dataType,
3915
- (value) => composedShadow("--ri-inset-shadow", value)
3916
- );
4340
+ var BOX_SHADOW = {
4341
+ none: "0 0 #0000",
4342
+ colorVar: "--ri-shadow-color",
4343
+ initial: true,
4344
+ wrap: (layers) => composedShadow("--ri-shadow", layers.join(", "))
4345
+ };
4346
+ var INSET_SHADOW = {
4347
+ none: "inset 0 0 #0000",
4348
+ colorVar: "--ri-inset-shadow-color",
4349
+ initial: true,
4350
+ wrap: (layers) => composedShadow("--ri-inset-shadow", layers.join(", "))
4351
+ };
4352
+ var TEXT_SHADOW = {
4353
+ none: "none",
4354
+ colorVar: "--ri-text-shadow-color",
4355
+ initial: true,
4356
+ wrap: (layers) => single("text-shadow", layers.join(", "))
4357
+ };
4358
+ function resolveShadow(full, theme, dataType) {
4359
+ const name = full === "shadow" ? Object.hasOwn(theme.shadows, "DEFAULT") ? "DEFAULT" : Object.hasOwn(theme.shadows, "md") ? "md" : "" : full.slice(7);
4360
+ if (Object.hasOwn(theme.shadows, name)) {
4361
+ return BOX_SHADOW.wrap(withShadowColorSlotLayers(theme.shadows[name], BOX_SHADOW.colorVar));
4362
+ }
4363
+ return resolveShadowFamily(name, theme, dataType, BOX_SHADOW);
3917
4364
  }
3918
- function resolveRingFamily(name, slot, colorVar, insetPrefix, theme, dataType) {
3919
- const ring = (width) => composedShadow(slot, `${insetPrefix}0 0 0 ${width} var(${colorVar}, currentColor)`);
4365
+ function resolveInsetShadow(full, theme, dataType) {
4366
+ return resolveShadowFamily(full.slice(13), theme, dataType, INSET_SHADOW);
4367
+ }
4368
+ var RING_INSET_FLAG = "var(--ri-ring-inset, ) ";
4369
+ var RING_OFFSET_WIDTH = "var(--ri-ring-offset-width, 0px)";
4370
+ var RING = {
4371
+ slot: "--ri-ring-shadow",
4372
+ colorVar: "--ri-ring-color",
4373
+ insetPrefix: RING_INSET_FLAG,
4374
+ offsetAware: true
4375
+ };
4376
+ var INSET_RING = {
4377
+ slot: "--ri-inset-ring-shadow",
4378
+ colorVar: "--ri-inset-ring-color",
4379
+ insetPrefix: "inset ",
4380
+ offsetAware: false
4381
+ };
4382
+ function resolveRingFamily(name, theme, dataType, family) {
4383
+ const ring = (width) => {
4384
+ const w = family.offsetAware ? `calc(${width} + ${RING_OFFSET_WIDTH})` : width;
4385
+ return composedShadow(
4386
+ family.slot,
4387
+ `${family.insetPrefix}0 0 0 ${w} var(${family.colorVar}, currentColor)`
4388
+ );
4389
+ };
3920
4390
  if (name === "") return ring("1px");
3921
4391
  if (INTEGER_RE.test(name)) return ring(`${name}px`);
3922
- return resolveColorOrArbitrary(name, colorVar, theme, dataType, (arb) => ring(arb));
4392
+ return resolveColorOrArbitrary(name, family.colorVar, theme, dataType, (arb) => ring(arb));
3923
4393
  }
3924
- function resolveRing(full, theme, dataType) {
3925
- return resolveRingFamily(
3926
- full === "ring" ? "" : full.slice(5),
3927
- "--ri-ring-shadow",
3928
- "--ri-ring-color",
3929
- "",
4394
+ function resolveRingOffset(full, theme, dataType) {
4395
+ const name = full.slice(12);
4396
+ const offset = (width) => multi(
4397
+ ["--ri-ring-offset-width", width],
4398
+ [
4399
+ "--ri-ring-offset-shadow",
4400
+ `${RING_INSET_FLAG}0 0 0 ${RING_OFFSET_WIDTH} var(--ri-ring-offset-color, #fff)`
4401
+ ]
4402
+ );
4403
+ if (INTEGER_RE.test(name)) return offset(`${name}px`);
4404
+ return resolveColorOrArbitrary(
4405
+ name,
4406
+ "--ri-ring-offset-color",
3930
4407
  theme,
3931
- dataType
4408
+ dataType,
4409
+ (arb) => offset(arb)
3932
4410
  );
3933
4411
  }
4412
+ function resolveRing(full, theme, dataType) {
4413
+ const offset = full.startsWith("ring-offset-") ? resolveRingOffset(full, theme, dataType) : null;
4414
+ if (offset) return offset;
4415
+ const ring = resolveRingFamily(full === "ring" ? "" : full.slice(5), theme, dataType, RING);
4416
+ if (ring) return ring;
4417
+ if (full === "ring-inset") return single("--ri-ring-inset", "inset");
4418
+ return null;
4419
+ }
3934
4420
  function resolveInsetRing(full, theme, dataType) {
3935
4421
  return resolveRingFamily(
3936
4422
  full === "inset-ring" ? "" : full.slice(11),
3937
- "--ri-inset-ring-shadow",
3938
- "--ri-inset-ring-color",
3939
- "inset ",
3940
4423
  theme,
3941
- dataType
4424
+ dataType,
4425
+ INSET_RING
3942
4426
  );
3943
4427
  }
3944
4428
  function resolveInset(full, theme, dataType) {
@@ -3948,15 +4432,7 @@ function resolveInset(full, theme, dataType) {
3948
4432
  return null;
3949
4433
  }
3950
4434
  function resolveTextShadow(full, theme, dataType) {
3951
- return resolveShadowFamily(
3952
- full.slice(12),
3953
- // "text-shadow-".length
3954
- "none",
3955
- "--ri-text-shadow-color",
3956
- theme,
3957
- dataType,
3958
- (value) => single("text-shadow", value)
3959
- );
4435
+ return resolveShadowFamily(full.slice(12), theme, dataType, TEXT_SHADOW);
3960
4436
  }
3961
4437
 
3962
4438
  // src/utilities/effects/transitions.ts
@@ -4051,14 +4527,32 @@ function resolveTransition(full) {
4051
4527
  }
4052
4528
 
4053
4529
  // src/utilities/effects/filters.ts
4054
- var FILTER_COMPOSED = "var(--ri-blur, ) var(--ri-brightness, ) var(--ri-contrast, ) var(--ri-grayscale, ) var(--ri-hue-rotate, ) var(--ri-invert, ) var(--ri-saturate, ) var(--ri-sepia, ) var(--ri-drop-shadow, )";
4055
- var BACKDROP_FILTER_COMPOSED = "var(--ri-backdrop-blur, ) var(--ri-backdrop-brightness, ) var(--ri-backdrop-contrast, ) var(--ri-backdrop-grayscale, ) var(--ri-backdrop-hue-rotate, ) var(--ri-backdrop-invert, ) var(--ri-backdrop-saturate, ) var(--ri-backdrop-sepia, ) var(--ri-backdrop-opacity, )";
4530
+ var DROP_SHADOW = {
4531
+ none: "0 0 #0000",
4532
+ colorVar: "--ri-drop-shadow-color",
4533
+ initial: false,
4534
+ // One `drop-shadow()` per layer, space-joined: the function takes a single
4535
+ // shadow, so a two-layer value written as `drop-shadow(a, b)` is invalid and
4536
+ // drops silently.
4537
+ wrap: (layers) => multi(
4538
+ ["--ri-drop-shadow", layers.map((l) => `drop-shadow(${l})`).join(" ")],
4539
+ ["filter", FILTER_COMPOSED]
4540
+ )
4541
+ };
4056
4542
  var FILTER_STATICS = {
4057
4543
  // Filter — grayscale/invert/sepia (bare + numeric) are handled dynamically via
4058
- // FILTER_TABLE (bare100); only the literal `filter-none` reset is static here.
4544
+ // FILTER_TABLE (bare100). Bare `filter` enables the chain and contributes
4545
+ // nothing of its own, which is what makes a v3-era `filter blur-sm` string
4546
+ // work; `filter-none` is the reset.
4547
+ filter: single("filter", FILTER_COMPOSED),
4059
4548
  "filter-none": single("filter", "none"),
4060
- // Backdrop filter
4061
- "backdrop-blur-none": single("backdrop-filter", "none")
4549
+ // Backdrop filter. `backdrop-blur-none` is NOT here: it clears the blur slot
4550
+ // and re-emits the chain, exactly as `blur-none` does, so it composes with
4551
+ // the other backdrop functions instead of erasing them. It used to be
4552
+ // `backdrop-filter: none`, which is the v3 reading — v4.3.3 emits
4553
+ // `--tw-backdrop-blur: ;` followed by the chain, and `backdrop-filter-none`
4554
+ // is the spelling that really does reset everything.
4555
+ "backdrop-filter": single("backdrop-filter", BACKDROP_FILTER_COMPOSED)
4062
4556
  };
4063
4557
  deepFreezeUtilityMap(FILTER_STATICS);
4064
4558
  var FILTER_TABLE = [
@@ -4115,15 +4609,7 @@ function resolveFilter(full, negative, theme, dataType) {
4115
4609
  if (r !== void 0) return r;
4116
4610
  }
4117
4611
  if (full.startsWith("drop-shadow-")) {
4118
- return resolveShadowFamily(
4119
- full.slice(12),
4120
- // "drop-shadow-".length
4121
- "0 0 #0000",
4122
- "--ri-drop-shadow-color",
4123
- theme,
4124
- dataType,
4125
- (value) => multi(["--ri-drop-shadow", `drop-shadow(${value})`], ["filter", FILTER_COMPOSED])
4126
- );
4612
+ return resolveShadowFamily(full.slice(12), theme, dataType, DROP_SHADOW);
4127
4613
  }
4128
4614
  return null;
4129
4615
  }
@@ -4147,6 +4633,12 @@ function resolveBackdropFilter(full, theme, negative) {
4147
4633
  if (full.startsWith("backdrop-blur-")) {
4148
4634
  const name = full.slice(14);
4149
4635
  if (name === "") return null;
4636
+ if (name === "none" && !Object.hasOwn(theme.blur, name)) {
4637
+ return multi(
4638
+ ["--ri-backdrop-blur", "blur(0)"],
4639
+ ["backdrop-filter", BACKDROP_FILTER_COMPOSED]
4640
+ );
4641
+ }
4150
4642
  const blur = resolveBlurValue(
4151
4643
  name,
4152
4644
  theme,
@@ -4181,8 +4673,12 @@ function resolveBackdropFilter(full, theme, negative) {
4181
4673
 
4182
4674
  // src/utilities/effects/transforms.ts
4183
4675
  var TRANSFORM_COMPOSED = "var(--ri-rotate-x, rotateX(0)) var(--ri-rotate-y, rotateY(0)) var(--ri-rotate-z, rotateZ(0)) var(--ri-skew-x, skewX(0)) var(--ri-skew-y, skewY(0))";
4676
+ var TRANSLATE_XYZ = "var(--ri-translate-x, 0) var(--ri-translate-y, 0) var(--ri-translate-z, 0)";
4184
4677
  var TRANSFORM_STATICS = {
4185
- // Transform
4678
+ // Transform. Bare `transform` and `transform-cpu` are the same declaration —
4679
+ // upstream emits them byte-identically; `-cpu` is the v3-era spelling kept
4680
+ // as the explicit opposite of `-gpu`. Do not "dedupe" the pair away.
4681
+ transform: single("transform", TRANSFORM_COMPOSED),
4186
4682
  "transform-none": single("transform", "none"),
4187
4683
  "transform-gpu": single("transform", `translateZ(0) ${TRANSFORM_COMPOSED}`),
4188
4684
  "transform-cpu": single("transform", TRANSFORM_COMPOSED),
@@ -4197,6 +4693,7 @@ var TRANSFORM_STATICS = {
4197
4693
  "transform-view": single("transform-box", "view-box"),
4198
4694
  // Translate reset
4199
4695
  "translate-none": single("translate", "none"),
4696
+ "translate-3d": single("translate", TRANSLATE_XYZ),
4200
4697
  // Rotate reset
4201
4698
  "rotate-none": single("rotate", "none"),
4202
4699
  // Scale reset
@@ -4231,14 +4728,8 @@ function resolveTranslate(full, negative) {
4231
4728
  ) : null;
4232
4729
  }
4233
4730
  if (full.startsWith("translate-z-")) {
4234
- const val = resolveTransformValue(full.slice(12), negative);
4235
- return val ? multi(
4236
- ["--ri-translate-z", val],
4237
- [
4238
- "translate",
4239
- "var(--ri-translate-x, 0) var(--ri-translate-y, 0) var(--ri-translate-z, 0)"
4240
- ]
4241
- ) : null;
4731
+ const val = resolveTransformValue(full.slice(12), negative, false);
4732
+ return val ? multi(["--ri-translate-z", val], ["translate", TRANSLATE_XYZ]) : null;
4242
4733
  }
4243
4734
  if (full.startsWith("translate-")) {
4244
4735
  const val = resolveTransformValue(full.slice(10), negative);
@@ -4378,9 +4869,15 @@ function resolveSkew(full, negative) {
4378
4869
  ["transform", TRANSFORM_COMPOSED]
4379
4870
  );
4380
4871
  }
4381
- function resolveTransformValue(name, negative) {
4382
- if (name === "full") return negative ? "-100%" : "100%";
4383
- if (name === "1/2") return negative ? "-50%" : "50%";
4872
+ function resolveTransformValue(name, negative, percentOk = true) {
4873
+ if (percentOk) {
4874
+ if (name === "full") return negative ? "-100%" : "100%";
4875
+ const fraction = fractionValue(name);
4876
+ if (fraction !== null) {
4877
+ if (!negative) return fraction;
4878
+ return fraction.startsWith("calc(") ? `calc(${fraction} * -1)` : `-${fraction}`;
4879
+ }
4880
+ }
4384
4881
  const arb = extractArbitrary(name);
4385
4882
  if (arb !== null) return negative ? `calc(${arb} * -1)` : arb;
4386
4883
  return spacingLookup(name, negative);
@@ -4972,6 +5469,8 @@ function svgGenerator(_utility, _value, full, _negative, _theme) {
4972
5469
  }
4973
5470
 
4974
5471
  // src/utilities/roots.ts
5472
+ var CONTAINER_KEYS = Object.keys(CONTAINER_WIDTHS);
5473
+ var FRACTION_KEYS = Object.keys(FRACTIONAL);
4975
5474
  var SPACING_SAMPLES = Object.freeze([
4976
5475
  "0",
4977
5476
  "1",
@@ -5001,8 +5500,12 @@ var COLOR_BORDER = [colorGenerator, borderGenerator];
5001
5500
  var EFFECTS = [effectsGenerator];
5002
5501
  var ANIMATION_EFFECTS = [animationGenerator, effectsGenerator];
5003
5502
  var STATICS_ONLY = { kinds: [] };
5004
- var SHADOW_SPEC = { kinds: ["shadow", "color", "special-color"] };
5503
+ var SHADOW_SPEC = {
5504
+ kinds: ["shadow", "color", "special-color"],
5505
+ keywords: ["none", "initial"]
5506
+ };
5005
5507
  var RING_SPEC = { kinds: ["int", "color", "special-color"] };
5508
+ var OUTER_RING_SPEC = { ...RING_SPEC, keywords: ["inset"] };
5006
5509
  var ROTATE_SKEW_SPEC = {
5007
5510
  kinds: ["keywords"],
5008
5511
  keywords: [
@@ -5104,21 +5607,36 @@ var ROOT_GROUPS = [
5104
5607
  resolvers: SPACING,
5105
5608
  spec: { kinds: ["fluid-range"] }
5106
5609
  },
5107
- // Sizing
5610
+ // Sizing. Every root here is offered the whole union of named sizing values
5611
+ // and its own table decides — the enumerator drops a candidate that does not
5612
+ // resolve, so a shared list cannot over-report, while a per-root list can and
5613
+ // did under-report (the logical spellings had none of their own).
5614
+ {
5615
+ roots: ["h", "size", "min", "max"],
5616
+ resolvers: SIZING,
5617
+ spec: { kinds: ["spacing", "fraction"], keywords: SIZING_KEYWORDS }
5618
+ },
5108
5619
  {
5109
- roots: ["w", "h", "size", "min", "max", "min-w"],
5620
+ // The inline axis reads the container ladder; the block axis does not,
5621
+ // which is why these two are not in the row above.
5622
+ roots: ["w", "min-w", "inline", "min-inline", "max-inline"],
5110
5623
  resolvers: SIZING,
5111
- spec: { kinds: ["spacing", "fraction"] }
5624
+ spec: { kinds: ["spacing", "fraction"], keywords: [...CONTAINER_KEYS, ...SIZING_KEYWORDS] }
5112
5625
  },
5113
5626
  {
5114
5627
  roots: ["max-w"],
5115
5628
  resolvers: SIZING,
5116
- spec: { kinds: ["spacing", "fraction", "breakpoint"] }
5629
+ spec: { kinds: ["spacing", "fraction"], keywords: [...CONTAINER_KEYS, ...SIZING_KEYWORDS] }
5117
5630
  },
5118
5631
  {
5119
- roots: ["min-h", "max-h"],
5632
+ // `min-block` / `max-block` are roots in their own right, the way
5633
+ // `min-inline` / `max-inline` are. Without them these fell through to the
5634
+ // bare `min` / `max` roots, where the value would have had to be spelled
5635
+ // `block-full` — so the whole logical block axis was unenumerable while
5636
+ // resolving perfectly well.
5637
+ roots: ["min-h", "max-h", "min-block", "max-block"],
5120
5638
  resolvers: SIZING,
5121
- spec: { kinds: ["spacing", "fraction"] }
5639
+ spec: { kinds: ["spacing", "fraction"], keywords: SIZING_KEYWORDS }
5122
5640
  },
5123
5641
  // Typography
5124
5642
  {
@@ -5234,7 +5752,7 @@ var ROOT_GROUPS = [
5234
5752
  spec: { kinds: ["color", "special-color", "int"] }
5235
5753
  },
5236
5754
  {
5237
- roots: ["accent", "caret", "fill"],
5755
+ roots: ["accent", "caret", "fill", "placeholder"],
5238
5756
  resolvers: COLOR_BORDER_EFFECTS_SVG,
5239
5757
  spec: { kinds: ["color", "special-color"] }
5240
5758
  },
@@ -5403,7 +5921,7 @@ var ROOT_GROUPS = [
5403
5921
  {
5404
5922
  roots: ["columns"],
5405
5923
  resolvers: LAYOUT,
5406
- spec: { kinds: ["breakpoint", "int"] }
5924
+ spec: { kinds: ["int"], keywords: CONTAINER_KEYS }
5407
5925
  },
5408
5926
  {
5409
5927
  roots: ["sr"],
@@ -5423,7 +5941,7 @@ var ROOT_GROUPS = [
5423
5941
  {
5424
5942
  roots: ["basis"],
5425
5943
  resolvers: LAYOUT,
5426
- spec: { kinds: ["spacing", "fraction", "breakpoint"] }
5944
+ spec: { kinds: ["spacing", "fraction"], keywords: CONTAINER_KEYS }
5427
5945
  },
5428
5946
  // caret/accent color values are registered on the color-bearing row above.
5429
5947
  {
@@ -5455,7 +5973,7 @@ var ROOT_GROUPS = [
5455
5973
  {
5456
5974
  roots: ["inline", "block"],
5457
5975
  resolvers: SIZING,
5458
- spec: { kinds: ["spacing", "fraction"] }
5976
+ spec: { kinds: ["spacing", "fraction"], keywords: SIZING_KEYWORDS }
5459
5977
  },
5460
5978
  // Borders
5461
5979
  {
@@ -5501,6 +6019,14 @@ var ROOT_GROUPS = [
5501
6019
  {
5502
6020
  roots: ["ring"],
5503
6021
  resolvers: EFFECTS,
6022
+ spec: OUTER_RING_SPEC
6023
+ },
6024
+ // `ring-offset-*` shares the ring's value space — an integer width or a
6025
+ // colour — but is its own root so the enumerator offers the whole family
6026
+ // rather than leaving it to be spelled through `ring`.
6027
+ {
6028
+ roots: ["ring-offset"],
6029
+ resolvers: EFFECTS,
5504
6030
  spec: RING_SPEC
5505
6031
  },
5506
6032
  {
@@ -5580,10 +6106,18 @@ var ROOT_GROUPS = [
5580
6106
  // Statics-only registration for the remaining filter roots — their dynamic
5581
6107
  // value spaces (percentages, angles, drop-shadow tokens) are not enumerated.
5582
6108
  {
5583
- roots: ["brightness", "contrast", "saturate", "hue", "drop"],
6109
+ roots: ["brightness", "contrast", "saturate", "hue"],
5584
6110
  resolvers: EFFECTS,
5585
6111
  spec: STATICS_ONLY
5586
6112
  },
6113
+ {
6114
+ // `drop-shadow-*` dispatches through the bare `drop` root, so its value
6115
+ // carries the `shadow-` segment. Only the two namespace-free spellings
6116
+ // are listed; the named scale comes from the preset's @utility blocks.
6117
+ roots: ["drop"],
6118
+ resolvers: EFFECTS,
6119
+ spec: { kinds: ["keywords"], keywords: ["shadow-none", "shadow-initial"] }
6120
+ },
5587
6121
  {
5588
6122
  roots: ["mask"],
5589
6123
  resolvers: EFFECTS,
@@ -5601,6 +6135,11 @@ var ROOT_GROUPS = [
5601
6135
  kinds: ["keywords"],
5602
6136
  keywords: [
5603
6137
  ...SPACING_SAMPLES.flatMap((s) => [`x-${s}`, `y-${s}`, `z-${s}`]),
6138
+ // Every fraction on both axes and the shorthand — the resolver
6139
+ // reads the whole shared FRACTIONAL table, so a spec listing only
6140
+ // `1/2` would under-report the family by 75 names.
6141
+ ...FRACTION_KEYS.flatMap((f) => [`x-${f}`, `y-${f}`, f]),
6142
+ "3d",
5604
6143
  "x-full",
5605
6144
  "y-full",
5606
6145
  "x-1/2",
@@ -6136,6 +6675,12 @@ var PROPERTY_GROUPS = Object.assign(/* @__PURE__ */ Object.create(null), {
6136
6675
  "--ri-inset-shadow-color": 141,
6137
6676
  "--ri-ring-color": 141,
6138
6677
  "--ri-inset-ring-color": 141,
6678
+ "--ri-ring-offset-color": 141,
6679
+ // Width and flag are read by the ring/offset shadow layers rather than by a
6680
+ // property of their own, so they belong to the same group for the same
6681
+ // reason the slot vars do.
6682
+ "--ri-ring-offset-width": 141,
6683
+ "--ri-ring-inset": 141,
6139
6684
  "text-shadow": 141,
6140
6685
  "--ri-text-shadow-color": 141,
6141
6686
  outline: 142,
@@ -6216,16 +6761,24 @@ var PROPERTY_GROUPS = Object.assign(/* @__PURE__ */ Object.create(null), {
6216
6761
  "--ri-rotate-z": 160,
6217
6762
  "--ri-skew-x": 160,
6218
6763
  "--ri-skew-y": 160,
6219
- translate: 161,
6220
6764
  "--ri-translate-x": 161,
6221
6765
  "--ri-translate-y": 161,
6222
6766
  "--ri-translate-z": 161,
6223
- rotate: 162,
6224
- scale: 163,
6225
- "--ri-scale-x": 163,
6226
- "--ri-scale-y": 163,
6227
- "--ri-scale-z": 163,
6228
- zoom: 164,
6767
+ // The whole-property `translate` sorts AFTER the axis variables, not with
6768
+ // them. `translate-none` and `translate-3d` are the only utilities whose
6769
+ // first declaration is the shorthand, and both are meant to win over an axis
6770
+ // utility beside them — a reset that loses to `translate-x-4` resets nothing,
6771
+ // and `translate-3d` exists precisely to re-emit the shorthand with the Z
6772
+ // axis in it. Sharing 161 left the codepoint tie-break to decide, and it put
6773
+ // `.translate-3d` and `.translate-none` first, so both were inert.
6774
+ // Tailwind orders them last for the same reason.
6775
+ translate: 162,
6776
+ rotate: 163,
6777
+ scale: 164,
6778
+ "--ri-scale-x": 164,
6779
+ "--ri-scale-y": 164,
6780
+ "--ri-scale-z": 164,
6781
+ zoom: 165,
6229
6782
  // Transition / Animation
6230
6783
  transition: 170,
6231
6784
  "transition-property": 171,
@@ -6868,6 +7421,11 @@ function compileUtility(parsed, theme, result, customVariantMap, warnSeen, break
6868
7421
  }
6869
7422
  const utilResult = resolveUtilityDeclarations(parsed, theme, result.warnings);
6870
7423
  if (!utilResult) {
7424
+ if (detail && parsed.raw === parsed.utility && isMarkerUtility(parsed.utility)) {
7425
+ detail.reason = "marker";
7426
+ detail.declarations = [];
7427
+ return null;
7428
+ }
6871
7429
  if (detail) detail.reason = "unknown-utility";
6872
7430
  return null;
6873
7431
  }
@@ -6913,43 +7471,51 @@ function compileUtility(parsed, theme, result, customVariantMap, warnSeen, break
6913
7471
  if (!wrappers) wrappers = [];
6914
7472
  wrappers.push(wrapper);
6915
7473
  }
6916
- let atRuleOpen = "";
6917
- let atRuleClose = "";
6918
- let startingStyleWrap = false;
6919
- if (wrappers) {
6920
- const applied = applyVariantWrappers(selector, wrappers);
6921
- selector = applied.selector;
6922
- for (const atRule of applied.atRules) {
7474
+ const renderBranch = (branch) => {
7475
+ let atRuleOpen = "";
7476
+ let atRuleClose = "";
7477
+ for (const atRule of branch.atRules) {
6923
7478
  atRuleOpen += `${atRule} {
6924
7479
  `;
6925
7480
  atRuleClose = `}
6926
7481
  ${atRuleClose}`;
6927
7482
  }
6928
- startingStyleWrap = applied.startingStyle;
6929
- }
6930
- let css;
6931
- if (startingStyleWrap) {
6932
- const indentedDecls = declCSS.replace(/\n/g, "\n ");
6933
- css = `${selector} {
7483
+ let out;
7484
+ if (branch.startingStyle) {
7485
+ const indentedDecls = declCSS.replace(/\n/g, "\n ");
7486
+ out = `${branch.selector} {
6934
7487
  @starting-style {
6935
7488
  ${indentedDecls}
6936
7489
  }
6937
7490
  }`;
6938
- } else {
6939
- css = `${selector} {
7491
+ } else {
7492
+ out = `${branch.selector} {
6940
7493
  ${declCSS}
6941
7494
  }`;
6942
- }
6943
- if (atRuleOpen) {
6944
- css = `${atRuleOpen}${css}
6945
- ${atRuleClose}`;
7495
+ }
7496
+ return atRuleOpen ? `${atRuleOpen}${out}
7497
+ ${atRuleClose}` : out;
7498
+ };
7499
+ let css;
7500
+ if (wrappers) {
7501
+ const branches = applyVariantWrappers(selector, wrappers);
7502
+ selector = branches[0].selector;
7503
+ css = branches.map(renderBranch).join("\n\n");
7504
+ } else {
7505
+ css = renderBranch({ selector, atRules: [], startingStyle: false });
6946
7506
  }
6947
7507
  const cssProperty = utilResult.declarations[0]?.property || "";
6948
7508
  const sortKey = computeSortKey(cssProperty, parsed.variants, breakpointWeights);
6949
7509
  return { selector, sortKey, css };
6950
7510
  }
7511
+ function indentBlock(body) {
7512
+ return body.split("\n").map((line) => line.trim() === "" ? "" : ` ${line}`).join("\n");
7513
+ }
6951
7514
  function scanStringForTokenUsage(str, result) {
6952
7515
  if (str.includes("var(--color-")) {
7516
+ for (const m of str.matchAll(COLOR_VAR_REF_RE)) {
7517
+ result.usedColorNames.add(m[1]);
7518
+ }
6953
7519
  for (const m of str.matchAll(COLOR_STOP_REF_RE)) {
6954
7520
  const hue = m[1];
6955
7521
  const suffix = m[2] ? Number(m[2]) : void 0;
@@ -7021,6 +7587,7 @@ function createEmptyCompilationResult() {
7021
7587
  keyframes: [],
7022
7588
  properties: [],
7023
7589
  usedColorStops: /* @__PURE__ */ new Map(),
7590
+ usedColorNames: /* @__PURE__ */ new Set(),
7024
7591
  usedTextSizes: /* @__PURE__ */ new Set(),
7025
7592
  usedFonts: /* @__PURE__ */ new Set(),
7026
7593
  usedShadows: /* @__PURE__ */ new Set(),
@@ -7063,6 +7630,7 @@ function compileClassEntry(raw, theme, customVariantMap, breakpointWeights, vari
7063
7630
  warnings: scratch.warnings,
7064
7631
  arbitraryWarning,
7065
7632
  usedColorStops: scratch.usedColorStops,
7633
+ usedColorNames: scratch.usedColorNames,
7066
7634
  usedTextSizes: scratch.usedTextSizes,
7067
7635
  usedFonts: scratch.usedFonts,
7068
7636
  usedShadows: scratch.usedShadows,
@@ -7148,6 +7716,7 @@ function compileInternal(classNames, theme, variantMapCache, authored) {
7148
7716
  }
7149
7717
  for (const stop of stops) set.add(stop);
7150
7718
  }
7719
+ for (const v of entry.usedColorNames) result.usedColorNames.add(v);
7151
7720
  for (const v of entry.usedTextSizes) result.usedTextSizes.add(v);
7152
7721
  for (const v of entry.usedFonts) result.usedFonts.add(v);
7153
7722
  for (const v of entry.usedShadows) result.usedShadows.add(v);
@@ -7181,7 +7750,7 @@ function compileInternal(classNames, theme, variantMapCache, authored) {
7181
7750
  for (const [name, def] of Object.entries(theme.animations)) {
7182
7751
  if (def.keyframes && animationBases.has(`animate-${name}`)) {
7183
7752
  result.keyframes.push(`@keyframes ${name} {
7184
- ${def.keyframes}
7753
+ ${indentBlock(def.keyframes)}
7185
7754
  }`);
7186
7755
  }
7187
7756
  }
@@ -7360,19 +7929,26 @@ function readURLImportTarget(src, start) {
7360
7929
  if (src[i] === ")") i++;
7361
7930
  return { target: target || null, nextIndex: i };
7362
7931
  }
7363
- function scanImportTarget(src, paramsStart) {
7932
+ function readImportTarget(src, paramsStart) {
7364
7933
  let i = paramsStart;
7365
7934
  while (i < src.length && WS_RE2.test(src[i])) i++;
7366
- if (i >= src.length) return null;
7935
+ if (i >= src.length) return { target: null, nextIndex: i };
7367
7936
  if (src[i] === '"' || src[i] === "'") {
7368
- return readQuotedImportTarget(src, i).target;
7937
+ return readQuotedImportTarget(src, i);
7369
7938
  }
7370
- return readURLImportTarget(src, i).target;
7939
+ return readURLImportTarget(src, i);
7371
7940
  }
7372
- var RI_IMPORT_SPECIFIERS = Object.freeze([
7941
+ function scanImportTarget(src, paramsStart) {
7942
+ return readImportTarget(src, paramsStart).target;
7943
+ }
7944
+ var RI_MARKER_IMPORT_SPECIFIERS = Object.freeze([
7373
7945
  "rainbowindex",
7374
7946
  "rainbowindex/index.css"
7375
7947
  ]);
7948
+ var RI_IMPORT_SPECIFIERS = Object.freeze([
7949
+ ...RI_MARKER_IMPORT_SPECIFIERS,
7950
+ "rainbowindex/tailwind.css"
7951
+ ]);
7376
7952
  var RI_IMPORT_SPECIFIER_ALTERNATION = RI_IMPORT_SPECIFIERS.map(
7377
7953
  (s) => s.replace(/[/\\^$.*+?()[\]{}|]/g, "\\$&")
7378
7954
  ).join("|");
@@ -7479,9 +8055,15 @@ function splitColorAlpha(side) {
7479
8055
  }
7480
8056
  return { base: side, alpha: null };
7481
8057
  }
7482
- function expandColorSide(side) {
8058
+ var COLOR_KEYWORDS = /* @__PURE__ */ new Set(["transparent", "currentColor", "inherit"]);
8059
+ function expandBareColorRef(v) {
8060
+ if (COLOR_KEYWORDS.has(v)) return v;
8061
+ return /^[\w-]+$/.test(v) ? `var(--color-${v})` : v;
8062
+ }
8063
+ function expandColorSide(side, bareNames = false) {
7483
8064
  const { base, alpha } = splitColorAlpha(side);
7484
- const expanded = expandColorStopRef(base);
8065
+ const stop = expandColorStopRef(base);
8066
+ const expanded = stop !== base || !(bareNames || alpha !== null) ? stop : expandBareColorRef(base);
7485
8067
  if (alpha === null) return expanded;
7486
8068
  const isPercent = alpha.endsWith("%");
7487
8069
  const num = Number.parseFloat(isPercent ? alpha.slice(0, -1) : alpha);
@@ -7496,15 +8078,26 @@ function parseColorBody(body, warnings) {
7496
8078
  for (const entry of scanEntries(cleanedBody, { newlineTerminates: true })) {
7497
8079
  if (entry.removal) {
7498
8080
  removals.push(entry.key);
8081
+ if (entry.legacyRemoval) {
8082
+ warnings?.push(legacySyntaxWarning(`!${entry.key};`, `${entry.key}: initial;`, "color"));
8083
+ }
7499
8084
  continue;
7500
8085
  }
8086
+ let { key, value } = entry;
8087
+ let darkBlockOverride = null;
7501
8088
  if (entry.fragment) {
7502
- warnings?.push(
7503
- `[RI-1035] Invalid @color key "${entry.value}" \u2014 color names may only contain letters, numbers, hyphens, and underscores. The entry was skipped.`
7504
- );
7505
- continue;
8089
+ if (entry.block === void 0) {
8090
+ warnings?.push(
8091
+ `[RI-1035] Invalid @color key "${entry.value}" \u2014 color names may only contain letters, numbers, hyphens, and underscores. The entry was skipped.`
8092
+ );
8093
+ continue;
8094
+ }
8095
+ const nested = readNestedColorEntry(entry.value, entry.block, warnings);
8096
+ if (nested === null) continue;
8097
+ key = entry.value;
8098
+ value = nested.value;
8099
+ darkBlockOverride = nested.options;
7506
8100
  }
7507
- const { key, value } = entry;
7508
8101
  if (!key || !value) continue;
7509
8102
  if (colorCount >= MAX_COLOR_ENTRIES) {
7510
8103
  warnings?.push(
@@ -7518,7 +8111,16 @@ function parseColorBody(body, warnings) {
7518
8111
  );
7519
8112
  continue;
7520
8113
  }
7521
- const darkBlock = entry.block ?? null;
8114
+ const darkBlock = darkBlockOverride ?? entry.block ?? null;
8115
+ if (darkBlockOverride === null && entry.block !== void 0) {
8116
+ warnings?.push(
8117
+ legacySyntaxWarning(
8118
+ `${key}: <value> { \u2026 }`,
8119
+ `${key} { ${isGenerativeValue(value) ? "ramp" : "value"}: <value>; \u2026 }`,
8120
+ "color"
8121
+ )
8122
+ );
8123
+ }
7522
8124
  let darkOverride;
7523
8125
  let hasInline = false;
7524
8126
  let hasParabolic;
@@ -7529,9 +8131,16 @@ function parseColorBody(body, warnings) {
7529
8131
  const colonIdx = stmt.indexOf(":");
7530
8132
  if (colonIdx === -1) {
7531
8133
  for (const flag of stmt.split(/\s+/)) {
7532
- if (flag === "inline") hasInline = true;
7533
- else if (flag === "parabolic") hasParabolic = true;
7534
- else if (flag === "no-parabolic") hasParabolic = false;
8134
+ if (flag === "inline") {
8135
+ hasInline = true;
8136
+ warnings?.push(legacySyntaxWarning("inline;", "inline: true;", "color"));
8137
+ } else if (flag === "parabolic") {
8138
+ hasParabolic = true;
8139
+ warnings?.push(legacySyntaxWarning("parabolic;", "parabolic: true;", "color"));
8140
+ } else if (flag === "no-parabolic") {
8141
+ hasParabolic = false;
8142
+ warnings?.push(legacySyntaxWarning("no-parabolic;", "parabolic: false;", "color"));
8143
+ }
7535
8144
  }
7536
8145
  continue;
7537
8146
  }
@@ -7539,9 +8148,9 @@ function parseColorBody(body, warnings) {
7539
8148
  const optVal = stmt.slice(colonIdx + 1).trim();
7540
8149
  if (optKey === "dark") {
7541
8150
  darkOverride = parseDarkOverrideValue(optVal);
7542
- } else if (optKey === "--ri-inline") {
8151
+ } else if (optKey === "inline" || optKey === "--ri-inline") {
7543
8152
  if (optVal === "true") hasInline = true;
7544
- } else if (optKey === "--ri-parabolic") {
8153
+ } else if (optKey === "parabolic" || optKey === "--ri-parabolic") {
7545
8154
  if (optVal === "true") hasParabolic = true;
7546
8155
  else if (optVal === "false") hasParabolic = false;
7547
8156
  }
@@ -7563,19 +8172,44 @@ function parseColorBody(body, warnings) {
7563
8172
  }
7564
8173
  return { colors, removals };
7565
8174
  }
8175
+ function isGenerativeValue(value) {
8176
+ return /^-?[\d.]+\s+-?[\d.]+$/.test(value.trim());
8177
+ }
8178
+ function readNestedColorEntry(name, block, warnings) {
8179
+ let value = null;
8180
+ const options = [];
8181
+ for (const statement of block.split(";")) {
8182
+ const stmt = statement.trim();
8183
+ if (!stmt) continue;
8184
+ const colon = stmt.indexOf(":");
8185
+ const key = colon === -1 ? "" : stmt.slice(0, colon).trim();
8186
+ if (key === "ramp" || key === "value") {
8187
+ value = stmt.slice(colon + 1).trim();
8188
+ continue;
8189
+ }
8190
+ options.push(stmt);
8191
+ }
8192
+ if (value === null || value === "") {
8193
+ warnings?.push(
8194
+ `[RI-1126] @color entry "${name}" has a block but no \`ramp:\` or \`value:\` declaration, so it defines no colour. Write \`${name} { ramp: <chroma> <hue>; }\` or \`${name} { value: oklch(\u2026); }\`.`
8195
+ );
8196
+ return null;
8197
+ }
8198
+ return { value, options: options.join("; ") };
8199
+ }
7566
8200
  function parseColorValue(key, value, warnings) {
7567
8201
  const slashIdx = findPairSeparator(value);
7568
8202
  if (slashIdx !== -1) {
7569
- const light = expandColorSide(value.slice(0, slashIdx).trim());
7570
- const dark = expandColorSide(value.slice(slashIdx + 1).trim());
8203
+ const light = expandColorSide(value.slice(0, slashIdx).trim(), true);
8204
+ const dark = expandColorSide(value.slice(slashIdx + 1).trim(), true);
7571
8205
  return { type: "pair", light, dark };
7572
8206
  }
7573
8207
  if (/^light-dark\s*\(/.test(value)) {
7574
8208
  const inner = value.slice(value.indexOf("(") + 1, value.lastIndexOf(")")).trim();
7575
8209
  const commaIdx = topLevelIndexOf(inner, ",");
7576
8210
  if (commaIdx !== -1) {
7577
- const light = expandColorSide(inner.slice(0, commaIdx).trim());
7578
- const dark = expandColorSide(inner.slice(commaIdx + 1).trim());
8211
+ const light = expandColorSide(inner.slice(0, commaIdx).trim(), true);
8212
+ const dark = expandColorSide(inner.slice(commaIdx + 1).trim(), true);
7579
8213
  return { type: "pair", light, dark };
7580
8214
  }
7581
8215
  }
@@ -7847,6 +8481,10 @@ function applyFaceOptions(face, entries, slot, warnings) {
7847
8481
  }
7848
8482
  }
7849
8483
  }
8484
+ function unwrapURL(value) {
8485
+ const match = /^url\(\s*(.*?)\s*\)$/i.exec(value.trim());
8486
+ return stripQuotes(match ? match[1] : value);
8487
+ }
7850
8488
  var LEGACY_METRICS_KEYS = /* @__PURE__ */ new Set([
7851
8489
  "metricsFallback",
7852
8490
  "sizeAdjust",
@@ -7864,6 +8502,22 @@ function parseSlotBody(body, slot, warnings) {
7864
8502
  continue;
7865
8503
  }
7866
8504
  if (entry.fragment) {
8505
+ if (entry.value === "face" && entry.block !== void 0) {
8506
+ let src = "";
8507
+ const own = [];
8508
+ for (const [k, v] of parseKeyValueBody(entry.block, warnings, "font").entries) {
8509
+ if (k === "src") src = unwrapURL(v);
8510
+ else own.push([k, v]);
8511
+ }
8512
+ if (src === "") {
8513
+ warnings?.push(
8514
+ `[RI-1217] @font slot "${slot}" has a \`face { \u2026 }\` block with no \`src:\` \u2014 write \`face { src: url("/fonts/\u2026"); }\`. The entry was ignored.`
8515
+ );
8516
+ } else {
8517
+ out.faces.push({ src, entries: own });
8518
+ }
8519
+ continue;
8520
+ }
7867
8521
  if (entry.value === "@face" && entry.block !== void 0) {
7868
8522
  warnDeprecated(warnings, slot, "`@face { src: \u2026; }`", "use `face: <src> { \u2026 }`");
7869
8523
  let src = "";
@@ -8092,11 +8746,45 @@ function buildSlot(slot, preambleText, blockBody, warnings) {
8092
8746
  }
8093
8747
  return config;
8094
8748
  }
8749
+ function readNestedSlot(slot, block, warnings) {
8750
+ let family = null;
8751
+ let from = null;
8752
+ const rest = [];
8753
+ for (const entry of scanEntries(stripCSSComments(block), { newlineTerminates: false })) {
8754
+ if (entry.block === void 0 && entry.key === "family") {
8755
+ family = entry.value;
8756
+ continue;
8757
+ }
8758
+ if (entry.block === void 0 && entry.key === "from") {
8759
+ from = entry.value;
8760
+ continue;
8761
+ }
8762
+ rest.push(block.slice(entry.start, entry.end).trim());
8763
+ }
8764
+ if (family === null || family === "") {
8765
+ warnings?.push(
8766
+ `[RI-1221] @font slot "${slot}" has a block but no \`family:\` declaration, so it names no font. Write \`${slot} { family: "<Family>", <fallback>; }\` \u2014 or \`family: system;\` for the system stack.`
8767
+ );
8768
+ return null;
8769
+ }
8770
+ return {
8771
+ preamble: from === null ? family : `${family} from ${from}`,
8772
+ body: rest.join("\n")
8773
+ };
8774
+ }
8095
8775
  function parseNestedFontBlock(body, warnings) {
8096
8776
  const cleanedBody = stripCSSComments(body);
8097
8777
  const configs = [];
8098
8778
  for (const entry of scanEntries(cleanedBody, { newlineTerminates: false })) {
8099
- if (entry.removal || entry.fragment) continue;
8779
+ if (entry.removal) continue;
8780
+ if (entry.fragment) {
8781
+ if (entry.block === void 0) continue;
8782
+ const nested = readNestedSlot(entry.value, entry.block, warnings);
8783
+ if (nested !== null) {
8784
+ configs.push(buildSlot(entry.value, nested.preamble, nested.body, warnings));
8785
+ }
8786
+ continue;
8787
+ }
8100
8788
  if (entry.unclosedBlock) break;
8101
8789
  if (!entry.key) continue;
8102
8790
  if (configs.length >= MAX_FONT_CONFIGS) {
@@ -8106,6 +8794,13 @@ function parseNestedFontBlock(body, warnings) {
8106
8794
  break;
8107
8795
  }
8108
8796
  if (entry.block !== void 0) {
8797
+ warnings?.push(
8798
+ legacySyntaxWarning(
8799
+ `${entry.key}: <family> { \u2026 }`,
8800
+ `${entry.key} { family: <family>; \u2026 }`,
8801
+ "font"
8802
+ )
8803
+ );
8109
8804
  configs.push(buildSlot(entry.key, entry.value, entry.block, warnings));
8110
8805
  } else if (entry.value) {
8111
8806
  configs.push(buildSlot(entry.key, entry.value, void 0, warnings));
@@ -8145,23 +8840,122 @@ function parseSpacingBody(body) {
8145
8840
  base
8146
8841
  };
8147
8842
  }
8148
- function parseAnimateBody(body) {
8843
+ function parseAnimateBody(body, warnings) {
8149
8844
  const animations = {};
8150
8845
  const removals = [];
8151
8846
  const cleaned = stripCSSComments(body);
8152
8847
  for (const entry of scanEntries(cleaned, { newlineTerminates: false })) {
8153
8848
  if (entry.removal) {
8154
8849
  removals.push(entry.key);
8850
+ if (entry.legacyRemoval) {
8851
+ warnings?.push(legacySyntaxWarning(`!${entry.key};`, `${entry.key}: initial;`, "animate"));
8852
+ }
8155
8853
  continue;
8156
8854
  }
8157
8855
  if (entry.block === void 0) continue;
8856
+ if (entry.fragment) {
8857
+ const parsed = parseNestedAnimation(entry.value, entry.block, warnings);
8858
+ if (parsed !== null) animations[entry.value] = parsed;
8859
+ continue;
8860
+ }
8158
8861
  if (!IDENT_KEY_RE.test(entry.key)) continue;
8159
- animations[entry.key] = { shorthand: entry.value, keyframes: entry.block };
8862
+ warnings?.push(
8863
+ legacySyntaxWarning(
8864
+ `${entry.key}: <animation> { \u2026 }`,
8865
+ `${entry.key} { animation: <animation>; @keyframes ${entry.key} { \u2026 } }`,
8866
+ "animate"
8867
+ )
8868
+ );
8869
+ animations[entry.key] = {
8870
+ shorthand: entry.value,
8871
+ keyframes: normalizeKeyframes(entry.block)
8872
+ };
8873
+ }
8874
+ for (const [name, definition] of Object.entries(animations)) {
8875
+ if (definition.shorthand.split(/\s+/).includes(name)) continue;
8876
+ warnings?.push(
8877
+ `[RI-1049] @animate entry "${name}" has the shorthand "${definition.shorthand}", which never names "${name}" \u2014 so \`animate-${name}\` sets no animation-name and the keyframes never run. Write \`${name} ${definition.shorthand}\`.`
8878
+ );
8160
8879
  }
8161
8880
  return { animations, removals };
8162
8881
  }
8163
- function parseFluidBody(body) {
8164
- const { entries } = parseKeyValueBody(body);
8882
+ function parseNestedAnimation(name, block, warnings) {
8883
+ if (!IDENT_KEY_RE.test(name)) return null;
8884
+ let shorthand = "";
8885
+ let keyframes = null;
8886
+ let keyframesName = null;
8887
+ let rest = "";
8888
+ let at = 0;
8889
+ const cleaned = stripCSSComments(block);
8890
+ for (const rule of scanTopLevelAtRules(cleaned)) {
8891
+ rest += cleaned.slice(at, rule.start);
8892
+ at = rule.end;
8893
+ if (rule.name !== "keyframes" || rule.body === null) continue;
8894
+ keyframes = rule.body.trim();
8895
+ keyframesName = rule.prelude.trim();
8896
+ }
8897
+ rest += cleaned.slice(at);
8898
+ for (const [key, value] of parseKeyValueBody(rest).entries) {
8899
+ if (key === "animation") shorthand = value;
8900
+ }
8901
+ if (keyframes === null) {
8902
+ warnings?.push(
8903
+ `[RI-1047] @animate entry "${name}" has no @keyframes block, so it defines no animation. Add \`@keyframes ${name} { \u2026 }\` inside it \u2014 or, if a custom utility was intended, move the block out of @animate.`
8904
+ );
8905
+ return null;
8906
+ }
8907
+ if (shorthand === "") {
8908
+ warnings?.push(
8909
+ `[RI-1047] @animate entry "${name}" has @keyframes but no \`animation:\` declaration, so nothing says how to run them. Add \`animation: ${keyframesName || name} <duration> <timing> <iteration>;\`.`
8910
+ );
8911
+ return null;
8912
+ }
8913
+ if (keyframesName !== null && keyframesName !== "" && keyframesName !== name) {
8914
+ warnings?.push(
8915
+ `[RI-1048] @animate entry "${name}" holds \`@keyframes ${keyframesName}\`, but the emitted keyframes are named after the entry \u2014 so "${keyframesName}" would never exist. Rename one of them to match. To reuse another entry's keyframes, name them here too.`
8916
+ );
8917
+ return null;
8918
+ }
8919
+ return { shorthand, keyframes: normalizeKeyframes(keyframes) };
8920
+ }
8921
+ function normalizeKeyframes(body) {
8922
+ const lines = body.split("\n");
8923
+ if (lines.length < 2) return body;
8924
+ let common = Number.POSITIVE_INFINITY;
8925
+ for (const line of lines.slice(1)) {
8926
+ if (line.trim() === "") continue;
8927
+ common = Math.min(common, line.length - line.trimStart().length);
8928
+ }
8929
+ if (!Number.isFinite(common) || common === 0) return body;
8930
+ return [lines[0], ...lines.slice(1).map((line) => line.slice(common))].join("\n");
8931
+ }
8932
+ function scanTopLevelAtRules(css) {
8933
+ const out = [];
8934
+ for (let i = 0; i < css.length; i++) {
8935
+ if (css[i] !== "@") continue;
8936
+ let nameEnd = i + 1;
8937
+ while (nameEnd < css.length && /[\w-]/.test(css[nameEnd])) nameEnd++;
8938
+ const name = css.slice(i + 1, nameEnd);
8939
+ if (!name) continue;
8940
+ let j = nameEnd;
8941
+ while (j < css.length && css[j] !== "{" && css[j] !== ";") j++;
8942
+ if (j >= css.length) break;
8943
+ const prelude = css.slice(nameEnd, j).trim();
8944
+ if (css[j] === ";") {
8945
+ out.push({ name, prelude, body: null, start: i, end: j + 1 });
8946
+ i = j;
8947
+ continue;
8948
+ }
8949
+ const close = findClosingBrace(css, j);
8950
+ if (close === -1) break;
8951
+ out.push({ name, prelude, body: css.slice(j + 1, close), start: i, end: close + 1 });
8952
+ i = close;
8953
+ }
8954
+ return out;
8955
+ }
8956
+ var DEAD_FLUID_KEYWORDS = /* @__PURE__ */ new Set(["parabolic", "no-parabolic", "shift", "no-shift"]);
8957
+ function parseFluidBody(body, warnings) {
8958
+ const { entries } = parseKeyValueBody(body, warnings, "fluid");
8165
8959
  const result = {};
8166
8960
  for (const [key, value] of entries) {
8167
8961
  if (key === "min") result.min = value;
@@ -8169,6 +8963,16 @@ function parseFluidBody(body) {
8169
8963
  if (key === "unit") result.unit = value;
8170
8964
  if (key === "multiplier") result.multiplier = value;
8171
8965
  }
8966
+ if (warnings) {
8967
+ for (const statement of stripCSSComments(body).split(/[;\n]/)) {
8968
+ const word = statement.trim();
8969
+ if (DEAD_FLUID_KEYWORDS.has(word)) {
8970
+ warnings.push(
8971
+ `[RI-1046] \`${word};\` in @fluid is a deprecated spelling that never had an effect \u2014 @fluid takes min, max, unit and multiplier, and nothing else. Delete it. The bare keyword is not valid CSS, so a formatter cannot read the file.`
8972
+ );
8973
+ }
8974
+ }
8975
+ }
8172
8976
  return result;
8173
8977
  }
8174
8978
  function parsePreflightDirective(body, modifier, base) {
@@ -8231,12 +9035,19 @@ function parseUtilityDirective(body, modifier, warnings) {
8231
9035
  if (!isValidUtilityName(name, warnings)) return null;
8232
9036
  return { name, functional, body: normalizedBody };
8233
9037
  }
9038
+ var ANIMATION_DECL_RE = /(^|[;{\s])animation(?:-name)?\s*:/;
8234
9039
  function extractUtilityBlocks(body, prefix, newlineTerminates, warnings) {
8235
9040
  const cleaned = stripCSSComments(body);
8236
9041
  const utilities = [];
8237
9042
  const cuts = [];
8238
9043
  for (const entry of scanEntries(cleaned, { newlineTerminates })) {
8239
9044
  if (!entry.fragment || entry.block === void 0) continue;
9045
+ if (hasTopLevelKeyframes(entry.block)) continue;
9046
+ if (prefix === "animate" && ANIMATION_DECL_RE.test(entry.block)) {
9047
+ warnings?.push(
9048
+ `[RI-1047] @animate entry "${entry.value}" sets \`animation\` but has no @keyframes block, so it was read as a custom utility named "animate-${entry.value}" and the animation it names is never defined. Add \`@keyframes \u2026 { \u2026 }\` inside the entry, or move the block out of @animate if a utility was intended.`
9049
+ );
9050
+ }
8240
9051
  const util = parseUtilityDirective(entry.block, `${prefix}-${entry.value}`, warnings);
8241
9052
  if (util) utilities.push(util);
8242
9053
  cuts.push([entry.start, entry.end]);
@@ -8470,7 +9281,11 @@ function mergeWithRemovals(defaults, overrides, removals, warnings, directiveNam
8470
9281
  for (const key of removals) {
8471
9282
  if (!Object.hasOwn(result, key) && warnings) {
8472
9283
  warnings.push(
8473
- `[RI-1103] Invalid removal "!${key}" in @${directiveName ?? "unknown"} \u2014 key "${key}" does not exist in defaults.`
9284
+ // Spelling-neutral: three forms produce a removal (`key: initial`,
9285
+ // the deprecated `!key`, and the plugin's `--ri-rm`), and naming
9286
+ // one of them in the message sends a reader looking for text that
9287
+ // is not in their file.
9288
+ `[RI-1103] Invalid removal of "${key}" in @${directiveName ?? "unknown"} \u2014 it does not exist, so there is nothing to remove.`
8474
9289
  );
8475
9290
  }
8476
9291
  delete result[key];
@@ -8544,6 +9359,19 @@ function applyFluidDirective(target, parsed, directiveName, warnings, options =
8544
9359
  }
8545
9360
  validateFluidRange(target, directiveName, warnings);
8546
9361
  }
9362
+ function parseDarkVariant(value, warnings) {
9363
+ if (value === "media") return { kind: "media" };
9364
+ if (value === "appearance") return { kind: "appearance" };
9365
+ const selector = /^selector\s*\((.*)\)$/s.exec(value);
9366
+ if (selector) {
9367
+ const inner = selector[1].trim();
9368
+ if (inner) return { kind: "selector", selector: inner };
9369
+ }
9370
+ warnings.push(
9371
+ `[RI-1111] Invalid @color dark variant "${value}" \u2014 expected "media", "appearance", or "selector(<sel>)", as in "selector(.dark)".`
9372
+ );
9373
+ return null;
9374
+ }
8547
9375
  function validateColorAliases(theme) {
8548
9376
  for (const [name, def] of Object.entries(theme.colors)) {
8549
9377
  if (def.type !== "alias") continue;
@@ -8567,11 +9395,26 @@ function validateColorAliases(theme) {
8567
9395
  }
8568
9396
  }
8569
9397
  }
8570
- var SHADOW_ALIAS_RE = /^shadow-([a-z0-9]+(?:-[a-z0-9]+)*)$/;
8571
- function resolveShadowAliases(theme) {
8572
- const aliases = /* @__PURE__ */ new Map();
8573
- for (const [name, value] of Object.entries(theme.shadows)) {
8574
- const match = SHADOW_ALIAS_RE.exec(value);
9398
+ function validateGenerativeColorRefs(theme) {
9399
+ for (const [name, def] of Object.entries(theme.colors)) {
9400
+ const values = def.type === "explicit" ? [def.value] : def.type === "pair" ? [def.light, def.dark] : [];
9401
+ for (const value of values) {
9402
+ for (const match of value.matchAll(BARE_COLOR_VAR_RE)) {
9403
+ const referenced = theme.colors[match[1]];
9404
+ if (referenced?.type !== "generative") continue;
9405
+ theme.warnings.push(
9406
+ `[RI-1109] @color "${name}" references "${match[1]}" with no stop. "${match[1]}" is a generative palette, which defines --color-${match[1]}-<stop> and no bare --color-${match[1]}. Name a stop, as in "${match[1]}-500".`
9407
+ );
9408
+ }
9409
+ }
9410
+ }
9411
+ }
9412
+ var BARE_COLOR_VAR_RE = /var\(--color-([a-z][\w-]*?)\s*[,)]/gi;
9413
+ var SHADOW_ALIAS_RE = /^shadow-([a-z0-9]+(?:-[a-z0-9]+)*)$/;
9414
+ function resolveShadowAliases(theme) {
9415
+ const aliases = /* @__PURE__ */ new Map();
9416
+ for (const [name, value] of Object.entries(theme.shadows)) {
9417
+ const match = SHADOW_ALIAS_RE.exec(value);
8575
9418
  if (match) aliases.set(name, match[1]);
8576
9419
  }
8577
9420
  for (const [name, target] of aliases) {
@@ -8768,9 +9611,12 @@ function resolveDirectives(directives, attribution) {
8768
9611
  } else if (k === "hue-shift") {
8769
9612
  const n = Number.parseFloat(v);
8770
9613
  if (!Number.isNaN(n)) theme.darkConfig.hueShift = n;
9614
+ } else if (k === "variant") {
9615
+ const strategy = parseDarkVariant(v, theme.warnings);
9616
+ if (strategy) theme.darkConfig.variant = strategy;
8771
9617
  } else {
8772
9618
  theme.warnings.push(
8773
- `[RI-1104] Unknown @color dark option "${k}" \u2014 supported: mode, chroma-boost, hue-shift.`
9619
+ `[RI-1104] Unknown @color dark option "${k}" \u2014 supported: mode, chroma-boost, hue-shift, variant.`
8774
9620
  );
8775
9621
  }
8776
9622
  }
@@ -8842,6 +9688,11 @@ function resolveDirectives(directives, attribution) {
8842
9688
  })) {
8843
9689
  if (entry.removal) {
8844
9690
  removals.push(entry.key);
9691
+ if (entry.legacyRemoval) {
9692
+ theme.warnings.push(
9693
+ legacySyntaxWarning(`!${entry.key};`, `${entry.key}: initial;`, "rounded")
9694
+ );
9695
+ }
8845
9696
  continue;
8846
9697
  }
8847
9698
  if (entry.fragment) continue;
@@ -8906,7 +9757,8 @@ function resolveDirectives(directives, attribution) {
8906
9757
  case "animate": {
8907
9758
  warnIgnoredModifier("animate", directive.modifier, theme.warnings);
8908
9759
  const { animations: parsedAnims, removals: animRemovals } = parseAnimateBody(
8909
- directive.body
9760
+ directive.body,
9761
+ theme.warnings
8910
9762
  );
8911
9763
  theme.animations = mergeWithRemovals(
8912
9764
  theme.animations,
@@ -8918,7 +9770,7 @@ function resolveDirectives(directives, attribution) {
8918
9770
  break;
8919
9771
  }
8920
9772
  case "fluid": {
8921
- const parsed = parseFluidBody(directive.body);
9773
+ const parsed = parseFluidBody(directive.body, theme.warnings);
8922
9774
  const targetName = directive.modifier?.trim();
8923
9775
  if (!targetName) {
8924
9776
  applyFluidDirective(theme.fluid, parsed, "@fluid", theme.warnings, {
@@ -9018,6 +9870,7 @@ function resolveDirectives(directives, attribution) {
9018
9870
  }
9019
9871
  }
9020
9872
  validateColorAliases(theme);
9873
+ validateGenerativeColorRefs(theme);
9021
9874
  resolveShadowAliases(theme);
9022
9875
  theme.fonts = dedupeLastWins(
9023
9876
  theme.fonts,
@@ -9320,6 +10173,66 @@ var OutputMap = class {
9320
10173
  }
9321
10174
  };
9322
10175
  var BODY_TOKEN_RE = /\S+/g;
10176
+ function normalizeApplyParenGroups(input) {
10177
+ if (!input.includes("(")) return input;
10178
+ let out = null;
10179
+ for (let i = 0; i < input.length; i++) {
10180
+ if (input[i] === "[") {
10181
+ while (i < input.length && input[i] !== "]") i++;
10182
+ continue;
10183
+ }
10184
+ if (input[i] !== "(") continue;
10185
+ if (i < 2 || input[i - 1] !== ":") continue;
10186
+ if (!isGroupSegmentCharCode(input.charCodeAt(i - 2))) continue;
10187
+ const close = matchingParen(input, i);
10188
+ if (close === -1) continue;
10189
+ out ??= [...input];
10190
+ out[i] = "{";
10191
+ out[close] = "}";
10192
+ }
10193
+ return out === null ? input : out.join("");
10194
+ }
10195
+ function matchingParen(input, open) {
10196
+ let depth = 0;
10197
+ for (let i = open; i < input.length; i++) {
10198
+ const ch = input[i];
10199
+ if (ch === "\\") {
10200
+ i++;
10201
+ continue;
10202
+ }
10203
+ if (ch === "'" || ch === '"') {
10204
+ const quote = ch;
10205
+ i++;
10206
+ while (i < input.length && input[i] !== quote) {
10207
+ if (input[i] === "\\") i++;
10208
+ i++;
10209
+ }
10210
+ continue;
10211
+ }
10212
+ if (ch === "[") {
10213
+ while (i < input.length && input[i] !== "]") {
10214
+ if (input[i] === "\\") i++;
10215
+ i++;
10216
+ }
10217
+ continue;
10218
+ }
10219
+ if (ch === "(") depth++;
10220
+ else if (ch === ")") {
10221
+ depth--;
10222
+ if (depth === 0) return i;
10223
+ }
10224
+ }
10225
+ return -1;
10226
+ }
10227
+ var LEGACY_APPLY_GROUP_RE = /[\w@-]+:\{/;
10228
+ function expandApplyBodyGroups(input, warnings, path) {
10229
+ if (LEGACY_APPLY_GROUP_RE.test(input)) {
10230
+ warnings?.push(
10231
+ `[RI-1046] \`@apply variant:{ \u2026 }\`${path ? ` in ${path}` : ""} is a deprecated spelling \u2014 write \`@apply variant:( \u2026 )\` instead. A brace opens a block to every CSS parser, so the old form makes the file unreadable to a formatter; it still works, and will be removed at 1.0.`
10232
+ );
10233
+ }
10234
+ return expandVariantGroups(normalizeApplyParenGroups(input), warnings, path);
10235
+ }
9323
10236
  function expandVariantGroups(input, warnings, path) {
9324
10237
  return expandVariantGroupsCore(input, warnings, null, path);
9325
10238
  }
@@ -9472,8 +10385,8 @@ function expandVariantGroupsCore(input, warnings, map, path) {
9472
10385
  return parts.join("");
9473
10386
  }
9474
10387
  var APPLY_AT_RULE_RE = /@(?:apply|a)\s+/g;
9475
- function expandApplyGroups(css, warnings, path) {
9476
- if (!css.includes("{") || !css.includes("@")) return css;
10388
+ function expandGroupsInStylesheet(css, warnings, path) {
10389
+ if (!css.includes("{") && !css.includes("(") || !css.includes("@")) return css;
9477
10390
  APPLY_AT_RULE_RE.lastIndex = 0;
9478
10391
  let match = APPLY_AT_RULE_RE.exec(css);
9479
10392
  if (match === null) return css;
@@ -9493,8 +10406,8 @@ function expandApplyGroups(css, warnings, path) {
9493
10406
  i++;
9494
10407
  }
9495
10408
  const body = css.slice(bodyStart, i);
9496
- if (body.includes("{")) {
9497
- const expanded = expandVariantGroups(body, warnings, path);
10409
+ if (body.includes("{") || body.includes("(")) {
10410
+ const expanded = expandApplyBodyGroups(body, warnings, path);
9498
10411
  if (expanded !== body) {
9499
10412
  out.push(css.slice(lastEnd, bodyStart));
9500
10413
  out.push(expanded);
@@ -9882,7 +10795,9 @@ var CandidateCollector = class {
9882
10795
  var BOUNDARY = /(?:^|[\s"'`{;:,=+(|])/.source;
9883
10796
  var NEGATIVE = /-?/.source;
9884
10797
  var IMPORTANT_SUFFIX = /!?/.source;
9885
- var VARIANT_SEGMENT = /(?:[\w.@-]+(?:\[[^\]]*\])?|\[[^\]]*\]):/.source;
10798
+ var VARIANT_BRACKET = /\[(?:[^[\]]|\[[^\]]*\])*\]/.source;
10799
+ var VARIANT_NAME_SUFFIX = /(?:\/[\w-]+)?/.source;
10800
+ var VARIANT_SEGMENT = `(?:[\\w.@-]+(?:${VARIANT_BRACKET})?|${VARIANT_BRACKET})${VARIANT_NAME_SUFFIX}:`;
9886
10801
  var VARIANT_PREFIX = `(?:${VARIANT_SEGMENT})*`;
9887
10802
  var UTILITY_VALUE = /[\w.@-]+(?:\[[^\]]*\]|(?<=-)\([^)]*\))?(?:\/(?:[\w.]+|\[[^\]]*\]|\([^)]*--[^)]*\)))?/.source;
9888
10803
  var VARIANT_GROUP = /[\w.@-]+:\{[^}]*\}/.source;
@@ -9901,7 +10816,7 @@ var CLASS_HELPERS = [
9901
10816
  "twJoin",
9902
10817
  "twMerge"
9903
10818
  ];
9904
- var VARIANT_HELPERS = ["cva", "tv"];
10819
+ var VARIANT_HELPERS = ["cva", "tv", "recipe"];
9905
10820
  var CLASS_HELPER_NAMES = Object.freeze([...CLASS_HELPERS]);
9906
10821
  var VARIANT_HELPER_NAMES = Object.freeze([...VARIANT_HELPERS]);
9907
10822
  var CLASS_HELPERS_CALL_RE = new RegExp(`\\b(?:${CLASS_HELPERS.join("|")})\\s*\\(`, "g");
@@ -9910,8 +10825,9 @@ var CLASS_MAP_CALL_RE = /\bclassMap\s*\(/g;
9910
10825
  var TRANSLATE_SCRATCH = new Int32Array(4);
9911
10826
  var BRACKET_SPAN_RE = /\[[^\]]*\]/g;
9912
10827
  var HAS_UPPERCASE_RE = /[A-Z]/;
9913
- var BRACKET_WHITESPACE_RE = /\[[^\]]*\s+[^\]]*\]/;
9914
- var INDEX_ACCESS_RE = /\[\d*\]$/;
10828
+ var BRACKET_WHITESPACE_RE = /\[(?:[^[\]]|\[[^\]]*\])*\s(?:[^[\]]|\[[^\]]*\])*\]/;
10829
+ var INDEX_ACCESS_RE = /[^-/]\[\d+\]$/;
10830
+ var EMPTY_BRACKET_RE = /\[\]$/;
9915
10831
  var PROPERTY_ACCESS_RE = /^[\w.@]+\[[^\]]+\]$/;
9916
10832
  function warnBracketWhitespace(warnings, cls) {
9917
10833
  const message = `[RI-1412] Class "${cls}" has whitespace inside its arbitrary value, so it can never match an element \u2014 class attributes, @a/@apply, and safelist() all split on whitespace. Use "_" for a space (\`bg-[url('a_b')]\` emits \`url('a b')\`) and "\\_" for a literal underscore. The class was skipped.`;
@@ -9972,16 +10888,17 @@ function scanClassTokens(sink, source, baseOffset, warnings) {
9972
10888
  if (!cls) continue;
9973
10889
  const unbanged = cls.endsWith("!") ? cls.slice(0, -1) : cls;
9974
10890
  const base = unbanged.replace(VARIANT_STRIP_RE, "");
10891
+ if (BRACKET_WHITESPACE_RE.test(unbanged)) {
10892
+ if (warnings && sink.inClassList) warnBracketWhitespace(warnings, cls);
10893
+ continue;
10894
+ }
9975
10895
  if (!base.includes("[")) {
9976
10896
  if (HAS_UPPERCASE_RE.test(base)) continue;
9977
10897
  } else {
9978
10898
  if (HAS_UPPERCASE_RE.test(base.replace(BRACKET_SPAN_RE, ""))) continue;
10899
+ if (EMPTY_BRACKET_RE.test(base)) continue;
9979
10900
  if (INDEX_ACCESS_RE.test(base)) continue;
9980
10901
  if (PROPERTY_ACCESS_RE.test(base)) continue;
9981
- if (BRACKET_WHITESPACE_RE.test(base)) {
9982
- if (warnings && sink.inClassList) warnBracketWhitespace(warnings, cls);
9983
- continue;
9984
- }
9985
10902
  }
9986
10903
  if (!wantsPositions) {
9987
10904
  sink.add(cls, 0, 0, -1, -1);
@@ -10354,6 +11271,37 @@ function extractSvelte(sink, context, warnings) {
10354
11271
  warnings
10355
11272
  );
10356
11273
  }
11274
+ function astroFrontmatterEnd(content) {
11275
+ if (!content.startsWith("---")) return 0;
11276
+ const afterOpen = content.indexOf("\n");
11277
+ if (afterOpen === -1) return 0;
11278
+ for (let i = afterOpen; i !== -1; i = content.indexOf("\n", i + 1)) {
11279
+ if (!content.startsWith("---", i + 1)) continue;
11280
+ const lineEnd = content.indexOf("\n", i + 1);
11281
+ const rest = lineEnd === -1 ? content.slice(i + 4) : content.slice(i + 4, lineEnd);
11282
+ if (rest.trim() === "") return i + 4;
11283
+ }
11284
+ return 0;
11285
+ }
11286
+ function extractAstro(sink, context, warnings) {
11287
+ const content = context.content;
11288
+ sink.setOrigin?.("plain");
11289
+ scanClassTokens(sink, content, 0, warnings);
11290
+ sink.setOrigin?.("attribute");
11291
+ collectAssignedValues(sink, content, /(?<![:\w-])class\s*=/g, NOOP_VISITOR, 0, warnings);
11292
+ collectAssignedValues(sink, content, /\bclass:list\s*=/g, collectClassishExpression, 0, warnings);
11293
+ const scriptEnd = astroFrontmatterEnd(content);
11294
+ if (scriptEnd > 0) {
11295
+ const script = content.slice(0, scriptEnd);
11296
+ sink.setOrigin?.("helper");
11297
+ collectCallArguments(sink, script, CLASS_HELPERS_CALL_RE, void 0, 0, warnings);
11298
+ const variantMetadata = collectVariantHelperArguments(sink, script, 0, warnings);
11299
+ collectClassMapArguments(sink, script, 0, warnings);
11300
+ sink.setHelper?.(null);
11301
+ pruneVariantMetadata(sink, variantMetadata, script);
11302
+ }
11303
+ pruneTokens(sink, NON_CLASS_IDENTIFIERS);
11304
+ }
10357
11305
  var EXTRACTORS = [
10358
11306
  {
10359
11307
  test: (context) => !!context.path && context.path.endsWith(".vue"),
@@ -10363,6 +11311,10 @@ var EXTRACTORS = [
10363
11311
  test: (context) => !!context.path && context.path.endsWith(".svelte"),
10364
11312
  extract: extractSvelte
10365
11313
  },
11314
+ {
11315
+ test: (context) => !!context.path && context.path.endsWith(".astro"),
11316
+ extract: extractAstro
11317
+ },
10366
11318
  {
10367
11319
  test: (context) => !!context.path && context.path.endsWith(".html"),
10368
11320
  extract: extractHTML
@@ -10439,6 +11391,352 @@ function extractClassCandidates(input, warnings) {
10439
11391
  return collector.finish();
10440
11392
  }
10441
11393
 
11394
+ // src/css/strip.ts
11395
+ var DIRECTIVE_AT_RULE_PATTERNS = [...EXTRACTABLE_DIRECTIVE_NAMES].map(directiveAtRulePattern);
11396
+ function buildProtectedRanges(css) {
11397
+ const ranges = [];
11398
+ let i = 0;
11399
+ while (i < css.length) {
11400
+ if (css[i] === "/" && css[i + 1] === "*") {
11401
+ const start = i;
11402
+ const end = css.indexOf("*/", i + 2);
11403
+ i = end === -1 ? css.length : end + 2;
11404
+ ranges.push([start, i]);
11405
+ continue;
11406
+ }
11407
+ if (css[i] === '"' || css[i] === "'") {
11408
+ const quote = css[i];
11409
+ const start = i;
11410
+ i++;
11411
+ while (i < css.length && css[i] !== quote) {
11412
+ if (css[i] === "\\" && i + 1 < css.length) i++;
11413
+ i++;
11414
+ }
11415
+ if (i < css.length) i++;
11416
+ ranges.push([start, i]);
11417
+ } else {
11418
+ i++;
11419
+ }
11420
+ }
11421
+ return ranges;
11422
+ }
11423
+ function protectedRangeEnd(pos, ranges) {
11424
+ let lo = 0;
11425
+ let hi = ranges.length - 1;
11426
+ while (lo <= hi) {
11427
+ const mid = lo + hi >>> 1;
11428
+ const [start, end] = ranges[mid];
11429
+ if (pos < start) hi = mid - 1;
11430
+ else if (pos >= end) lo = mid + 1;
11431
+ else return end;
11432
+ }
11433
+ return -1;
11434
+ }
11435
+ function isInsideProtectedRange(pos, ranges) {
11436
+ return protectedRangeEnd(pos, ranges) !== -1;
11437
+ }
11438
+ function replaceOutsideProtectedRanges(css, re, ranges) {
11439
+ re.lastIndex = 0;
11440
+ const parts = [];
11441
+ let lastIdx = 0;
11442
+ for (; ; ) {
11443
+ const match = re.exec(css);
11444
+ if (match === null) break;
11445
+ const guardEnd = protectedRangeEnd(match.index, ranges);
11446
+ if (guardEnd !== -1) {
11447
+ re.lastIndex = guardEnd;
11448
+ continue;
11449
+ }
11450
+ if (!isAtRuleBoundary(css, match.index)) {
11451
+ re.lastIndex = match.index + 1;
11452
+ continue;
11453
+ }
11454
+ let end = match.index + match[0].length;
11455
+ if (isInsideProtectedRange(end - 1, ranges)) {
11456
+ let k = end;
11457
+ let aborted = false;
11458
+ while (k < css.length) {
11459
+ if (!isInsideProtectedRange(k, ranges)) {
11460
+ const ch = css[k];
11461
+ if (ch === ";") break;
11462
+ if (ch === "{" || ch === "}") {
11463
+ aborted = true;
11464
+ break;
11465
+ }
11466
+ }
11467
+ k++;
11468
+ }
11469
+ if (aborted) continue;
11470
+ end = k < css.length ? k + 1 : css.length;
11471
+ re.lastIndex = end;
11472
+ }
11473
+ parts.push(css.slice(lastIdx, match.index));
11474
+ lastIdx = end;
11475
+ }
11476
+ parts.push(css.slice(lastIdx));
11477
+ return parts.join("");
11478
+ }
11479
+ function stripBalancedBlocks(css, re, protectedRanges) {
11480
+ re.lastIndex = 0;
11481
+ const ranges = [];
11482
+ for (; ; ) {
11483
+ const match = re.exec(css);
11484
+ if (match === null) break;
11485
+ const start = match.index;
11486
+ const guardEnd = protectedRangeEnd(start, protectedRanges);
11487
+ if (guardEnd !== -1) {
11488
+ re.lastIndex = guardEnd;
11489
+ continue;
11490
+ }
11491
+ if (!isAtRuleBoundary(css, start)) {
11492
+ re.lastIndex = start + 1;
11493
+ continue;
11494
+ }
11495
+ let braceIdx = css.indexOf("{", start);
11496
+ while (braceIdx !== -1 && isInsideProtectedRange(braceIdx, protectedRanges)) {
11497
+ braceIdx = css.indexOf("{", braceIdx + 1);
11498
+ }
11499
+ const preludeEnd = braceIdx === -1 ? css.length : braceIdx;
11500
+ let semiIdx = -1;
11501
+ for (let k = start; k < preludeEnd; k++) {
11502
+ if (css[k] === ";" && !isInsideProtectedRange(k, protectedRanges)) {
11503
+ semiIdx = k;
11504
+ break;
11505
+ }
11506
+ }
11507
+ if (semiIdx !== -1) {
11508
+ ranges.push([start, semiIdx + 1]);
11509
+ re.lastIndex = semiIdx + 1;
11510
+ continue;
11511
+ }
11512
+ if (braceIdx === -1) {
11513
+ ranges.push([start, css.length]);
11514
+ break;
11515
+ }
11516
+ let depth = 1;
11517
+ let j = braceIdx + 1;
11518
+ while (j < css.length && depth > 0) {
11519
+ const ch = css[j];
11520
+ if (ch === '"' || ch === "'") {
11521
+ const quote = ch;
11522
+ j++;
11523
+ while (j < css.length && css[j] !== quote) {
11524
+ if (css[j] === "\\" && j + 1 < css.length) j++;
11525
+ j++;
11526
+ }
11527
+ j++;
11528
+ continue;
11529
+ }
11530
+ if (ch === "/" && css[j + 1] === "*") {
11531
+ const end = css.indexOf("*/", j + 2);
11532
+ j = end === -1 ? css.length : end + 2;
11533
+ continue;
11534
+ }
11535
+ if (ch === "{") depth++;
11536
+ else if (ch === "}") depth--;
11537
+ j++;
11538
+ }
11539
+ ranges.push([start, j]);
11540
+ re.lastIndex = j;
11541
+ }
11542
+ if (ranges.length === 0) return css;
11543
+ const parts = [];
11544
+ let lastEnd = 0;
11545
+ for (const [start, end] of ranges) {
11546
+ parts.push(css.slice(lastEnd, start));
11547
+ lastEnd = end;
11548
+ }
11549
+ parts.push(css.slice(lastEnd));
11550
+ return parts.join("");
11551
+ }
11552
+ var ALL_SEMI_DIRECTIVES_RE = new RegExp(
11553
+ DIRECTIVE_AT_RULE_PATTERNS.map((p) => `${p}[^{;]*;`).join("|"),
11554
+ "g"
11555
+ );
11556
+ var ALL_BRACE_DIRECTIVES_RE = new RegExp(
11557
+ DIRECTIVE_AT_RULE_PATTERNS.map((p) => `${p}[^{]*{`).join("|"),
11558
+ "g"
11559
+ );
11560
+ var RI_IMPORT_RE = new RegExp(
11561
+ `@import\\s+(?:url\\(\\s*)?["'](?:${RI_IMPORT_SPECIFIER_ALTERNATION})["']\\s*\\)?[^;]*;`,
11562
+ "g"
11563
+ );
11564
+ var NO_JOIN_NEEDED = /* @__PURE__ */ new Set([";", ",", "(", ")", "{", "}", ":"]);
11565
+ function stripCSSComments2(css) {
11566
+ if (!css.includes("/*")) return css;
11567
+ const parts = [];
11568
+ let cursor = 0;
11569
+ for (const [start, end] of buildProtectedRanges(css)) {
11570
+ if (css[start] !== "/") continue;
11571
+ if (css[start + 2] === "!") continue;
11572
+ const before = css[start - 1];
11573
+ const after = css[end];
11574
+ const welds = before !== void 0 && after !== void 0 && !/\s/.test(before) && !/\s/.test(after) && !NO_JOIN_NEEDED.has(before) && !NO_JOIN_NEEDED.has(after);
11575
+ parts.push(css.slice(cursor, start), welds ? " " : "");
11576
+ cursor = end;
11577
+ }
11578
+ if (cursor === 0) return css;
11579
+ parts.push(css.slice(cursor));
11580
+ return parts.join("").replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n");
11581
+ }
11582
+ function stripRIDirectives(css) {
11583
+ let currentRanges = buildProtectedRanges(css);
11584
+ let result = replaceOutsideProtectedRanges(css, RI_IMPORT_RE, currentRanges);
11585
+ if (result !== css) {
11586
+ currentRanges = buildProtectedRanges(result);
11587
+ }
11588
+ const beforePhase1 = result;
11589
+ result = replaceOutsideProtectedRanges(result, ALL_SEMI_DIRECTIVES_RE, currentRanges);
11590
+ const dirty = result !== beforePhase1;
11591
+ if (dirty) {
11592
+ currentRanges = buildProtectedRanges(result);
11593
+ }
11594
+ result = stripBalancedBlocks(result, ALL_BRACE_DIRECTIVES_RE, currentRanges);
11595
+ return result.trim();
11596
+ }
11597
+
11598
+ // src/project/imports.ts
11599
+ var DEFAULT_MAX_DEPTH = 8;
11600
+ var RI_MARKER_TARGETS = new Set(RI_MARKER_IMPORT_SPECIFIERS);
11601
+ function isRemoteTarget(target) {
11602
+ return target.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(target) || // A leading "/" is a URL path relative to the site root, not a file path.
11603
+ target.startsWith("/");
11604
+ }
11605
+ function findStatementEnd(src, from) {
11606
+ let i = from;
11607
+ let depth = 0;
11608
+ while (i < src.length) {
11609
+ const ch = src[i];
11610
+ if (ch === "/" && src[i + 1] === "*") {
11611
+ const end = src.indexOf("*/", i + 2);
11612
+ i = end === -1 ? src.length : end + 2;
11613
+ continue;
11614
+ }
11615
+ if (ch === '"' || ch === "'") {
11616
+ const quote = ch;
11617
+ i++;
11618
+ while (i < src.length && src[i] !== quote) {
11619
+ if (src[i] === "\\") i++;
11620
+ i++;
11621
+ }
11622
+ i++;
11623
+ continue;
11624
+ }
11625
+ if (ch === "(") {
11626
+ depth++;
11627
+ i++;
11628
+ continue;
11629
+ }
11630
+ if (ch === ")") {
11631
+ if (depth > 0) depth--;
11632
+ i++;
11633
+ continue;
11634
+ }
11635
+ if (depth === 0) {
11636
+ if (ch === ";") return i + 1;
11637
+ if (ch === "{") return -1;
11638
+ }
11639
+ i++;
11640
+ }
11641
+ return src.length;
11642
+ }
11643
+ function isPackageSpecifier(target) {
11644
+ return !target.startsWith(".");
11645
+ }
11646
+ function inlineInto(css, from, depth, ctx, inPackage) {
11647
+ const edits = [];
11648
+ scanAtRules(css, (name, atPos, nameEnd) => {
11649
+ if (name !== "import") return void 0;
11650
+ const stmtEnd = findStatementEnd(css, nameEnd);
11651
+ if (stmtEnd === -1) return void 0;
11652
+ const { target, nextIndex } = readImportTarget(css, nameEnd);
11653
+ if (target === null) return stmtEnd;
11654
+ const preludeEnd = css[stmtEnd - 1] === ";" ? stmtEnd - 1 : stmtEnd;
11655
+ const tail = css.slice(Math.min(nextIndex, preludeEnd), preludeEnd).trim();
11656
+ if (RI_MARKER_TARGETS.has(target)) {
11657
+ if (depth > 0) edits.push({ start: atPos, end: stmtEnd, text: "" });
11658
+ return stmtEnd;
11659
+ }
11660
+ if (isRemoteTarget(target)) return stmtEnd;
11661
+ if (tail !== "") {
11662
+ ctx.warnings.push(
11663
+ `[RI-1045] Conditional @import "${target}" (${tail}) was left in place; directives inside it are not read. Move the directives to an unconditional @import.`
11664
+ );
11665
+ return stmtEnd;
11666
+ }
11667
+ if (ctx.exhausted) return stmtEnd;
11668
+ if (depth >= ctx.maxDepth) {
11669
+ ctx.warnings.push(
11670
+ `[RI-1043] @import "${target}" nests deeper than ${ctx.maxDepth} levels and was left in place. Flatten the import chain.`
11671
+ );
11672
+ return stmtEnd;
11673
+ }
11674
+ const resolved = ctx.resolve(target, from);
11675
+ if (resolved === null) {
11676
+ ctx.warnings.push(
11677
+ `[RI-1041] Could not resolve @import "${target}"${from ? ` from "${from}"` : ""}. The at-rule was left in place and its directives were not read. Check the path, or install the package.`
11678
+ );
11679
+ return stmtEnd;
11680
+ }
11681
+ if (ctx.stack.has(resolved.path)) {
11682
+ ctx.warnings.push(
11683
+ `[RI-1042] Circular @import "${target}" \u2014 "${resolved.path}" already imports the file importing it. The repeat was dropped.`
11684
+ );
11685
+ edits.push({ start: atPos, end: stmtEnd, text: "" });
11686
+ return stmtEnd;
11687
+ }
11688
+ if (ctx.visited.has(resolved.path)) {
11689
+ edits.push({ start: atPos, end: stmtEnd, text: "" });
11690
+ return stmtEnd;
11691
+ }
11692
+ if (ctx.bytes + resolved.content.length > ctx.maxBytes) {
11693
+ ctx.exhausted = true;
11694
+ ctx.warnings.push(
11695
+ `[RI-1044] Inlined @import files exceed the ${ctx.maxBytes / 1048576} MB limit at "${target}". It and any later import were left in place. Split the stylesheet.`
11696
+ );
11697
+ return stmtEnd;
11698
+ }
11699
+ ctx.visited.add(resolved.path);
11700
+ ctx.files.push(resolved.path);
11701
+ ctx.bytes += resolved.content.length;
11702
+ ctx.stack.add(resolved.path);
11703
+ const entersPackage = !inPackage && isPackageSpecifier(target);
11704
+ const text = inlineInto(
11705
+ resolved.content,
11706
+ resolved.path,
11707
+ depth + 1,
11708
+ ctx,
11709
+ inPackage || entersPackage
11710
+ );
11711
+ ctx.stack.delete(resolved.path);
11712
+ edits.push({ start: atPos, end: stmtEnd, text: entersPackage ? stripCSSComments2(text) : text });
11713
+ return stmtEnd;
11714
+ });
11715
+ if (edits.length === 0) return css;
11716
+ let out = "";
11717
+ let cursor = 0;
11718
+ for (const edit of edits) {
11719
+ out += css.slice(cursor, edit.start) + edit.text;
11720
+ cursor = edit.end;
11721
+ }
11722
+ return out + css.slice(cursor);
11723
+ }
11724
+ function inlineDirectiveImports(css, options) {
11725
+ const ctx = {
11726
+ resolve: options.resolve,
11727
+ maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH,
11728
+ maxBytes: options.maxBytes ?? MAX_DIRECTIVE_INPUT_SIZE,
11729
+ warnings: [],
11730
+ files: [],
11731
+ stack: new Set(options.from === void 0 ? [] : [options.from]),
11732
+ visited: new Set(options.from === void 0 ? [] : [options.from]),
11733
+ bytes: 0,
11734
+ exhausted: false
11735
+ };
11736
+ const out = inlineInto(css, options.from, 0, ctx, false);
11737
+ return { css: out, warnings: ctx.warnings, files: ctx.files };
11738
+ }
11739
+
10442
11740
  // src/project/analyze.ts
10443
11741
  function nextLineTargets(css, spans, warnings) {
10444
11742
  const targets = /* @__PURE__ */ new Map();
@@ -10533,11 +11831,880 @@ function isDisabled(diagnostic, directiveIndex, suppressed, targets, capture) {
10533
11831
  return false;
10534
11832
  }
10535
11833
 
11834
+ // src/integrations/font-providers/metrics-data.ts
11835
+ var FONT_METRICS_TABLE = {
11836
+ abel: [2006, -604, 0, 2048, 783, "sans-serif"],
11837
+ alegreya: [1016, -345, 0, 1e3, 410, "serif"],
11838
+ "anonymous pro": [1675, -373, 0, 2048, 1118, "monospace"],
11839
+ archivo: [878, -210, 0, 1e3, 440, "sans-serif"],
11840
+ arial: [1854, -434, 67, 2048, 913, "sans-serif"],
11841
+ asap: [934, -212, 0, 1e3, 442, "sans-serif"],
11842
+ barlow: [1e3, -200, 0, 1e3, 431, "sans-serif"],
11843
+ bitter: [935, -265, 0, 1e3, 465, "serif"],
11844
+ "bricolage grotesque": [930, -270, 0, 1e3, 470, "sans-serif"],
11845
+ cabin: [1930, -500, 0, 2e3, 844, "sans-serif"],
11846
+ catamaran: [1100, -540, 0, 1e3, 411, "sans-serif"],
11847
+ chivo: [940, -250, 0, 1e3, 478, "sans-serif"],
11848
+ "cormorant garamond": [924, -287, 0, 1e3, 394, "serif"],
11849
+ "courier new": [1705, -615, 0, 2048, 1229, "monospace"],
11850
+ "crimson pro": [918, -220, 0, 1024, 410, "serif"],
11851
+ "crimson text": [972, -359, 0, 1024, 405, "serif"],
11852
+ "dm sans": [992, -310, 0, 1e3, 466, "sans-serif"],
11853
+ "dm serif display": [1036, -335, 0, 1e3, 446, "serif"],
11854
+ dosis: [1027, -237, 0, 1e3, 377, "sans-serif"],
11855
+ "eb garamond": [1007, -298, 0, 1e3, 385, "serif"],
11856
+ epilogue: [1580, -470, 0, 2e3, 990, "sans-serif"],
11857
+ "exo 2": [999, -201, 0, 1e3, 455, "sans-serif"],
11858
+ figtree: [950, -250, 0, 1e3, 449, "sans-serif"],
11859
+ "fira code": [1980, -644, 0, 2e3, 1200, "monospace"],
11860
+ "fira sans": [935, -265, 0, 1e3, 458, "sans-serif"],
11861
+ fraunces: [1956, -510, 0, 2e3, 938, "serif"],
11862
+ gabarito: [940, -260, 0, 1e3, 442, "display"],
11863
+ "geist mono": [1005, -295, 0, 1e3, 600, "monospace"],
11864
+ geist: [1005, -295, 0, 1e3, 467, "sans-serif"],
11865
+ georgia: [1878, -449, 0, 2048, 913, "serif"],
11866
+ heebo: [2146, -862, 0, 2048, 912, "sans-serif"],
11867
+ "helvetica neue": [952, -213, 28, 1e3, 450, "sans-serif"],
11868
+ helvetica: [1577, -471, 0, 2048, 913, "sans-serif"],
11869
+ hind: [1055, -546, 0, 1e3, 429, "sans-serif"],
11870
+ "ibm plex mono": [1025, -275, 0, 1e3, 600, "monospace"],
11871
+ "ibm plex sans": [1025, -275, 0, 1e3, 451, "sans-serif"],
11872
+ "ibm plex serif": [1025, -275, 0, 1e3, 473, "serif"],
11873
+ inconsolata: [859, -190, 0, 1e3, 500, "monospace"],
11874
+ "instrument sans": [970, -250, 0, 1e3, 458, "sans-serif"],
11875
+ "instrument serif": [990, -310, 0, 1e3, 341, "serif"],
11876
+ inter: [1984, -494, 0, 2048, 978, "sans-serif"],
11877
+ "jetbrains mono": [1020, -300, 0, 1e3, 600, "monospace"],
11878
+ "josefin sans": [750, -250, 0, 1e3, 456, "sans-serif"],
11879
+ kanit: [1100, -395, 0, 1e3, 452, "sans-serif"],
11880
+ karla: [1834, -504, 0, 2e3, 913, "sans-serif"],
11881
+ lato: [1974, -426, 0, 2e3, 871, "sans-serif"],
11882
+ lexend: [1e3, -250, 0, 1e3, 490, "sans-serif"],
11883
+ "libre baskerville": [970, -270, 0, 1e3, 517, "serif"],
11884
+ "libre franklin": [966, -246, 0, 1e3, 465, "sans-serif"],
11885
+ lora: [1006, -274, 0, 1e3, 468, "serif"],
11886
+ manrope: [2132, -600, 0, 2e3, 920, "sans-serif"],
11887
+ "maven pro": [965, -210, 0, 1e3, 462, "sans-serif"],
11888
+ merriweather: [1968, -546, 0, 2e3, 970, "serif"],
11889
+ montserrat: [968, -251, 0, 1e3, 503, "sans-serif"],
11890
+ mulish: [1005, -250, 0, 1e3, 464, "sans-serif"],
11891
+ newsreader: [1470, -530, 0, 2e3, 857, "serif"],
11892
+ "noto sans": [1069, -293, 0, 1e3, 474, "sans-serif"],
11893
+ "noto serif": [1069, -293, 0, 1e3, 481, "serif"],
11894
+ "nunito sans": [1011, -353, 0, 1e3, 452, "sans-serif"],
11895
+ nunito: [1011, -353, 0, 1e3, 452, "sans-serif"],
11896
+ onest: [970, -305, 0, 1e3, 469, "sans-serif"],
11897
+ "open sans": [2189, -600, 0, 2048, 960, "sans-serif"],
11898
+ oswald: [1193, -289, 0, 1e3, 363, "sans-serif"],
11899
+ outfit: [1e3, -260, 0, 1e3, 445, "sans-serif"],
11900
+ overpass: [1766, -766, 0, 2e3, 898, "sans-serif"],
11901
+ oxygen: [2103, -483, 0, 2048, 923, "sans-serif"],
11902
+ "playfair display": [1082, -251, 0, 1e3, 452, "serif"],
11903
+ "plus jakarta sans": [1038, -222, 0, 1e3, 468, "sans-serif"],
11904
+ poppins: [1050, -350, 100, 1e3, 500, "sans-serif"],
11905
+ "pt sans": [1018, -276, 0, 1e3, 431, "sans-serif"],
11906
+ "pt serif": [1039, -286, 0, 1e3, 448, "serif"],
11907
+ "public sans": [1900, -450, 0, 2e3, 935, "sans-serif"],
11908
+ quicksand: [1e3, -250, 0, 1e3, 465, "sans-serif"],
11909
+ raleway: [940, -234, 0, 1e3, 463, "sans-serif"],
11910
+ "red hat display": [1018, -305, 0, 1e3, 442, "sans-serif"],
11911
+ "red hat text": [1018, -305, 0, 1e3, 447, "sans-serif"],
11912
+ "roboto condensed": [1900, -500, 0, 2048, 811, "sans-serif"],
11913
+ "roboto flex": [1900, -500, 0, 2048, 908, "sans-serif"],
11914
+ "roboto mono": [2146, -555, 0, 2048, 1229, "monospace"],
11915
+ "roboto slab": [2146, -555, 0, 2048, 972, "serif"],
11916
+ roboto: [1900, -500, 0, 2048, 911, "sans-serif"],
11917
+ rubik: [935, -250, 0, 1e3, 468, "sans-serif"],
11918
+ "schibsted grotesk": [2e3, -528, 0, 2048, 954, "sans-serif"],
11919
+ "segoe ui": [2210, -514, 0, 2048, 908, "sans-serif"],
11920
+ sora: [970, -290, 0, 1e3, 507, "sans-serif"],
11921
+ "source code pro": [984, -273, 0, 1e3, 600, "monospace"],
11922
+ "source sans 3": [1024, -400, 0, 1e3, 418, "sans-serif"],
11923
+ "source serif 4": [1036, -335, 0, 1e3, 479, "serif"],
11924
+ "space grotesk": [984, -292, 0, 1e3, 489, "sans-serif"],
11925
+ "space mono": [1120, -361, 0, 1e3, 612, "monospace"],
11926
+ spectral: [1059, -463, 0, 1e3, 446, "serif"],
11927
+ tahoma: [2049, -423, 0, 2048, 917, "sans-serif"],
11928
+ "times new roman": [1825, -443, 87, 2048, 832, "serif"],
11929
+ "titillium web": [1133, -388, 0, 1e3, 421, "sans-serif"],
11930
+ "trebuchet ms": [1923, -455, 0, 2048, 934, "sans-serif"],
11931
+ "ubuntu mono": [830, -170, 0, 1e3, 500, "monospace"],
11932
+ ubuntu: [932, -189, 28, 1e3, 455, "sans-serif"],
11933
+ urbanist: [1900, -500, 0, 2e3, 883, "sans-serif"],
11934
+ "varela round": [918, -286, 0, 1e3, 478, "sans-serif"],
11935
+ verdana: [2059, -430, 0, 2048, 1049, "sans-serif"],
11936
+ "victor mono": [1100, -250, 0, 1e3, 600, "monospace"],
11937
+ vollkorn: [952, -441, 0, 1e3, 438, "serif"],
11938
+ "work sans": [930, -243, 0, 1e3, 499, "sans-serif"],
11939
+ "zilla slab": [944, -256, 0, 1e3, 434, "serif"]
11940
+ };
11941
+
11942
+ // src/integrations/font-providers/metrics.ts
11943
+ var CATEGORY_FALLBACK = {
11944
+ "sans-serif": "Arial",
11945
+ serif: "Times New Roman",
11946
+ monospace: "Courier New"
11947
+ };
11948
+ function lookupFontMetrics(family) {
11949
+ return FONT_METRICS_TABLE[family.trim().toLowerCase()];
11950
+ }
11951
+ var round4 = (n) => Math.round(n * 1e4) / 1e4;
11952
+ function computeFallbackMetrics(fallbackName, font, fallbackFont) {
11953
+ const [ascent, descent, lineGap, unitsPerEm, xWidthAvg] = font;
11954
+ const [, , , fbUnitsPerEm, fbXWidthAvg] = fallbackFont;
11955
+ const sizeAdjust = xWidthAvg / unitsPerEm / (fbXWidthAvg / fbUnitsPerEm);
11956
+ return {
11957
+ fallback: fallbackName,
11958
+ sizeAdjust: round4(sizeAdjust * 100),
11959
+ ascent: round4(ascent / unitsPerEm / sizeAdjust * 100),
11960
+ descent: round4(Math.abs(descent) / unitsPerEm / sizeAdjust * 100),
11961
+ lineGap: round4(lineGap / unitsPerEm / sizeAdjust * 100)
11962
+ };
11963
+ }
11964
+ function resolveAutoMetrics(family, fallbackStack, explicitFallback) {
11965
+ const font = lookupFontMetrics(family);
11966
+ if (!font) return null;
11967
+ const fallbackName = explicitFallback ?? fallbackStack.find((f) => lookupFontMetrics(f)) ?? CATEGORY_FALLBACK[font[5]] ?? "Arial";
11968
+ const fallbackFont = lookupFontMetrics(fallbackName);
11969
+ if (!fallbackFont) return null;
11970
+ return computeFallbackMetrics(fallbackName, font, fallbackFont);
11971
+ }
11972
+
11973
+ // src/integrations/font-providers/emit.ts
11974
+ var SYSTEM_STACKS = Object.freeze({
11975
+ sans: 'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
11976
+ serif: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
11977
+ mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'
11978
+ });
11979
+ function getFallbackStack(slot) {
11980
+ if (!SYSTEM_STACKS[slot] && isRIDebug()) {
11981
+ console.warn(`[RI-DEBUG] Unknown font slot "${slot}" \u2014 falling back to sans stack.`);
11982
+ }
11983
+ return SYSTEM_STACKS[slot] || SYSTEM_STACKS.sans;
11984
+ }
11985
+ function googleFontsUrl(family, face) {
11986
+ const encodedFamily = encodeURIComponent(family).replace(/%20/g, "+");
11987
+ let axisParam;
11988
+ if (face.weight.includes(",")) {
11989
+ const weights = face.weight.split(",").map((w) => w.trim());
11990
+ if (face.style.includes("italic")) {
11991
+ const tuples = weights.flatMap((w) => [`0,${w}`, `1,${w}`]);
11992
+ axisParam = `ital,wght@${tuples.join(";")}`;
11993
+ } else {
11994
+ axisParam = `wght@${weights.join(";")}`;
11995
+ }
11996
+ } else if (face.weight.includes(" ")) {
11997
+ const range = face.weight.replace(" ", "..");
11998
+ axisParam = face.style.includes("italic") ? `ital,wght@0,${range};1,${range}` : `wght@${range}`;
11999
+ } else {
12000
+ const w = face.weight || "400";
12001
+ axisParam = face.style.includes("italic") ? `ital,wght@0,${w};1,${w}` : `wght@${w}`;
12002
+ }
12003
+ const display = face.display || "swap";
12004
+ return `https://fonts.googleapis.com/css2?family=${encodedFamily}:${axisParam}&display=${display}`;
12005
+ }
12006
+ function escapeFontFamily(name) {
12007
+ return name.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\0/g, "\\0").replace(/\n/g, "\\a ").replace(/\r/g, "\\d ");
12008
+ }
12009
+ function generateFallbackFontFace(family, metrics) {
12010
+ const safeFamily = escapeFontFamily(family);
12011
+ return `@font-face {
12012
+ font-family: "${safeFamily} Fallback";
12013
+ src: local("${escapeFontFamily(metrics.fallback)}");
12014
+ size-adjust: ${metrics.sizeAdjust}%;
12015
+ ascent-override: ${metrics.ascent}%;
12016
+ descent-override: ${metrics.descent}%;
12017
+ line-gap-override: ${metrics.lineGap}%;
12018
+ }`;
12019
+ }
12020
+ var FONT_FORMAT_MIME = {
12021
+ woff2: "font/woff2",
12022
+ woff: "font/woff",
12023
+ truetype: "font/ttf",
12024
+ opentype: "font/otf"
12025
+ };
12026
+ function inferFontFormat(path) {
12027
+ if (path.endsWith(".woff2")) return "woff2";
12028
+ if (path.endsWith(".woff")) return "woff";
12029
+ if (path.endsWith(".ttf")) return "truetype";
12030
+ if (path.endsWith(".otf")) return "opentype";
12031
+ return "woff2";
12032
+ }
12033
+ function generateWebFontFace(family, face) {
12034
+ if (face.provider === "system" || !face.provider) return null;
12035
+ if (face.provider === "google") {
12036
+ return { type: "import", css: `@import url("${googleFontsUrl(family, face)}");` };
12037
+ }
12038
+ const safeProvider = escapeFontFamily(face.provider);
12039
+ const format = inferFontFormat(face.provider);
12040
+ const src = `url("${safeProvider}") format("${format}")`;
12041
+ const declarations = [` font-family: "${escapeFontFamily(family)}";`, ` src: ${src};`];
12042
+ if (face.weight) declarations.push(` font-weight: ${face.weight};`);
12043
+ if (face.style && face.style !== "normal") declarations.push(` font-style: ${face.style};`);
12044
+ if (face.display) declarations.push(` font-display: ${face.display};`);
12045
+ if (face.unicodeRange) declarations.push(` unicode-range: ${face.unicodeRange};`);
12046
+ return { type: "font-face", css: `@font-face {
12047
+ ${declarations.join("\n")}
12048
+ }` };
12049
+ }
12050
+ function normalizeLocalStyle(style, family, warnings) {
12051
+ if (style.includes(" ") && !style.startsWith("oblique")) {
12052
+ const first = style.split(/\s+/)[0];
12053
+ warnings.push(
12054
+ `[RI-1203] Local font "${family}" has a compound font-style "${style}" \u2014 a single @font-face takes one style. Split upright and italic into separate @face blocks (or use the italic: shorthand). Using "${first}".`
12055
+ );
12056
+ return first;
12057
+ }
12058
+ return style;
12059
+ }
12060
+ function resolveSlotMetrics(slot, warnings) {
12061
+ const cfg = slot.metrics;
12062
+ if (cfg === null) return null;
12063
+ if (cfg?.sizeAdjust !== void 0) {
12064
+ return {
12065
+ fallback: cfg.fallback || slot.fallback[0] || "Arial",
12066
+ sizeAdjust: cfg.sizeAdjust,
12067
+ ascent: cfg.ascent,
12068
+ descent: cfg.descent,
12069
+ lineGap: cfg.lineGap
12070
+ };
12071
+ }
12072
+ const resolved = resolveAutoMetrics(slot.family, slot.fallback, cfg?.fallback);
12073
+ if (!resolved && cfg?.fallback) {
12074
+ const missing = lookupFontMetrics(slot.family) ? cfg.fallback : slot.family;
12075
+ warnings.push(
12076
+ `[RI-1220] @font slot "${slot.slot}" requests metrics matching against "${cfg.fallback}", but "${missing}" is not in the built-in metrics table \u2014 no fallback @font-face was generated. Provide the four percentages explicitly (metrics: "${cfg.fallback}" <size-adjust> <ascent> <descent> <line-gap>) or use \`metrics: none\`.`
12077
+ );
12078
+ }
12079
+ return resolved;
12080
+ }
12081
+ function generateFontCSS(slot) {
12082
+ const imports = [];
12083
+ const fontFaces = [];
12084
+ const variables = [];
12085
+ const warnings = [];
12086
+ const pushFeatureVars = () => {
12087
+ if (slot.features) variables.push(`--font-${slot.slot}--features: ${slot.features};`);
12088
+ if (slot.variation) variables.push(`--font-${slot.slot}--variations: ${slot.variation};`);
12089
+ };
12090
+ if (slot.kind === "system") {
12091
+ variables.push(`--font-${slot.slot}: ${getFallbackStack(slot.slot)};`);
12092
+ return { imports, fontFaces, variables, warnings };
12093
+ }
12094
+ if (slot.kind === "manual") {
12095
+ const fallback = slot.fallback.length > 0 ? slot.fallback.join(", ") : getFallbackStack(slot.slot);
12096
+ const stack = [`"${escapeFontFamily(slot.family)}"`, fallback].join(", ");
12097
+ variables.push(`--font-${slot.slot}: ${stack};`);
12098
+ pushFeatureVars();
12099
+ return { imports, fontFaces, variables, warnings };
12100
+ }
12101
+ const metrics = resolveSlotMetrics(slot, warnings);
12102
+ if (metrics) fontFaces.push(generateFallbackFontFace(slot.family, metrics));
12103
+ for (const face of slot.faces) {
12104
+ if (face.provider !== "google" && !face.provider.startsWith("/") && !face.provider.startsWith("http") && !face.provider.startsWith(".")) {
12105
+ warnings.push(
12106
+ `[RI-1201] Unknown font provider "${face.provider}" for "${slot.family}" \u2014 supported: google, or a file path/URL.`
12107
+ );
12108
+ continue;
12109
+ }
12110
+ const emitFace = face.provider === "google" ? face : { ...face, style: normalizeLocalStyle(face.style, slot.family, warnings) };
12111
+ const webFont = generateWebFontFace(slot.family, emitFace);
12112
+ if (!webFont) continue;
12113
+ if (webFont.type === "import") imports.push(webFont.css);
12114
+ else fontFaces.push(webFont.css);
12115
+ }
12116
+ const fallbackStack = slot.fallback.length > 0 ? slot.fallback.join(", ") : getFallbackStack(slot.slot);
12117
+ const safeFamily = escapeFontFamily(slot.family);
12118
+ const stackParts = [`"${safeFamily}"`];
12119
+ if (metrics) stackParts.push(`"${safeFamily} Fallback"`);
12120
+ stackParts.push(fallbackStack);
12121
+ variables.push(`--font-${slot.slot}: ${stackParts.join(", ")};`);
12122
+ pushFeatureVars();
12123
+ return { imports, fontFaces, variables, warnings };
12124
+ }
12125
+ function getFontPreloadLinks(slots) {
12126
+ const links = [];
12127
+ const seen = /* @__PURE__ */ new Set();
12128
+ for (const slot of slots) {
12129
+ for (const face of slot.faces) {
12130
+ if (!face.preload) continue;
12131
+ if (face.provider === "system" || face.provider === "google" || !face.provider) continue;
12132
+ if (seen.has(face.provider)) continue;
12133
+ seen.add(face.provider);
12134
+ links.push({
12135
+ href: face.provider,
12136
+ as: "font",
12137
+ type: FONT_FORMAT_MIME[inferFontFormat(face.provider)],
12138
+ crossorigin: true
12139
+ });
12140
+ }
12141
+ }
12142
+ return links;
12143
+ }
12144
+
12145
+ // src/css/preflight.ts
12146
+ var DEFAULT_PREFLIGHT = {
12147
+ core: true,
12148
+ typography: true,
12149
+ content: true,
12150
+ forms: true,
12151
+ interactive: true,
12152
+ modern: true
12153
+ };
12154
+ var modules = [
12155
+ // ── Core ─────────────────────────────────────────────────
12156
+ {
12157
+ name: "box-sizing",
12158
+ category: "core",
12159
+ css: `*, *::before, *::after {
12160
+ box-sizing: border-box;
12161
+ }`
12162
+ },
12163
+ {
12164
+ name: "margins",
12165
+ category: "core",
12166
+ css: `body, h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, pre, ol, ul, menu {
12167
+ margin: 0;
12168
+ }`
12169
+ },
12170
+ {
12171
+ name: "borders",
12172
+ category: "core",
12173
+ css: `*, *::before, *::after {
12174
+ border-width: 0;
12175
+ border-style: solid;
12176
+ border-color: inherit;
12177
+ }`
12178
+ },
12179
+ {
12180
+ name: "root-defaults",
12181
+ category: "core",
12182
+ css: `:root {
12183
+ line-height: 1.5;
12184
+ -webkit-text-size-adjust: 100%;
12185
+ tab-size: 4;
12186
+ }
12187
+ body {
12188
+ line-height: inherit;
12189
+ font-family: var(--font-sans);
12190
+ }
12191
+ code, kbd, samp, pre {
12192
+ font-family: var(--font-mono);
12193
+ font-size: 1em;
12194
+ }`
12195
+ },
12196
+ // ── Typography ───────────────────────────────────────────
12197
+ {
12198
+ name: "font-smoothing",
12199
+ category: "typography",
12200
+ css: `body {
12201
+ -webkit-font-smoothing: antialiased;
12202
+ -moz-osx-font-smoothing: grayscale;
12203
+ }`
12204
+ },
12205
+ {
12206
+ name: "font-inherit",
12207
+ category: "typography",
12208
+ css: `button, input, optgroup, select, textarea {
12209
+ font-family: inherit;
12210
+ font-feature-settings: inherit;
12211
+ font-variation-settings: inherit;
12212
+ font-size: 100%;
12213
+ font-weight: inherit;
12214
+ line-height: inherit;
12215
+ letter-spacing: inherit;
12216
+ color: inherit;
12217
+ }`
12218
+ },
12219
+ {
12220
+ name: "heading-sizes",
12221
+ category: "typography",
12222
+ css: `h1, h2, h3, h4, h5, h6 {
12223
+ font-size: inherit;
12224
+ font-weight: inherit;
12225
+ }`
12226
+ },
12227
+ {
12228
+ name: "link-reset",
12229
+ category: "typography",
12230
+ css: `a {
12231
+ color: inherit;
12232
+ text-decoration: inherit;
12233
+ }`
12234
+ },
12235
+ // ── Content ──────────────────────────────────────────────
12236
+ {
12237
+ name: "media-block",
12238
+ category: "content",
12239
+ css: `img, svg, video, canvas, audio, iframe, embed, object {
12240
+ display: block;
12241
+ max-inline-size: 100%;
12242
+ }
12243
+ img, video {
12244
+ block-size: auto;
12245
+ }`
12246
+ },
12247
+ {
12248
+ name: "list-reset",
12249
+ category: "content",
12250
+ css: `ol, ul, menu {
12251
+ list-style: none;
12252
+ padding: 0;
12253
+ }`
12254
+ },
12255
+ {
12256
+ name: "table-reset",
12257
+ category: "content",
12258
+ css: `table {
12259
+ text-indent: 0;
12260
+ border-color: inherit;
12261
+ border-collapse: collapse;
12262
+ }`
12263
+ },
12264
+ {
12265
+ name: "hr-reset",
12266
+ category: "content",
12267
+ css: `hr {
12268
+ block-size: 0;
12269
+ border-top-width: 1px;
12270
+ color: inherit;
12271
+ }`
12272
+ },
12273
+ // ── Forms ────────────────────────────────────────────────
12274
+ {
12275
+ name: "button-reset",
12276
+ category: "forms",
12277
+ css: `button, [role="button"] {
12278
+ cursor: pointer;
12279
+ padding: 0;
12280
+ }
12281
+ button {
12282
+ background-color: transparent;
12283
+ background-image: none;
12284
+ }`
12285
+ },
12286
+ {
12287
+ name: "input-reset",
12288
+ category: "forms",
12289
+ css: `input::placeholder, textarea::placeholder {
12290
+ opacity: 1;
12291
+ color: color-mix(in oklab, currentColor 48%, transparent);
12292
+ }
12293
+ input:where([type="button"], [type="reset"], [type="submit"]) {
12294
+ -webkit-appearance: button;
12295
+ appearance: button;
12296
+ }`
12297
+ },
12298
+ {
12299
+ name: "textarea-reset",
12300
+ category: "forms",
12301
+ css: `textarea {
12302
+ resize: vertical;
12303
+ }`
12304
+ },
12305
+ {
12306
+ name: "fieldset-reset",
12307
+ category: "forms",
12308
+ css: `fieldset {
12309
+ margin: 0;
12310
+ padding: 0;
12311
+ }
12312
+ legend {
12313
+ padding: 0;
12314
+ }`
12315
+ },
12316
+ // ── Interactive ──────────────────────────────────────────
12317
+ {
12318
+ name: "focus-visible",
12319
+ category: "interactive",
12320
+ css: `:focus-visible {
12321
+ outline-width: 2px;
12322
+ outline-style: solid;
12323
+ outline-offset: 2px;
12324
+ outline-color: currentColor;
12325
+ }`
12326
+ },
12327
+ {
12328
+ name: "dialog-reset",
12329
+ category: "interactive",
12330
+ css: `dialog {
12331
+ padding: 0;
12332
+ }
12333
+ dialog::backdrop {
12334
+ background-color: oklch(0 0 0 / 0.5);
12335
+ }`
12336
+ },
12337
+ {
12338
+ name: "summary-reset",
12339
+ category: "interactive",
12340
+ css: `summary {
12341
+ display: list-item;
12342
+ cursor: pointer;
12343
+ }`
12344
+ },
12345
+ // ── Modern ───────────────────────────────────────────────
12346
+ {
12347
+ name: "interpolate-size",
12348
+ category: "modern",
12349
+ css: `@supports (interpolate-size: allow-keywords) {
12350
+ :root {
12351
+ interpolate-size: allow-keywords;
12352
+ }
12353
+ }`
12354
+ },
12355
+ {
12356
+ name: "overflow-wrap",
12357
+ category: "modern",
12358
+ css: `body {
12359
+ overflow-wrap: break-word;
12360
+ }`
12361
+ },
12362
+ {
12363
+ name: "color-scheme",
12364
+ category: "modern",
12365
+ css: `html {
12366
+ color-scheme: light dark;
12367
+ }
12368
+ html[data-appearance="dark"] {
12369
+ color-scheme: dark;
12370
+ }
12371
+ html[data-appearance="light"] {
12372
+ color-scheme: light;
12373
+ }`
12374
+ }
12375
+ ];
12376
+ function generatePreflight(config = DEFAULT_PREFLIGHT) {
12377
+ const enabledModules = modules.filter((m) => config[m.category]);
12378
+ if (enabledModules.length === 0) return "";
12379
+ return enabledModules.map((m) => `/* preflight: ${m.name} */
12380
+ ${m.css}`).join("\n\n");
12381
+ }
12382
+
12383
+ // src/assembly.ts
12384
+ function resolveColorDemand(theme, usage) {
12385
+ const shadowsToEmit = resolveTransitiveShadowDeps(theme.shadows, usage.usedShadows);
12386
+ const effectiveStops = new Map(
12387
+ [...usage.usedColorStops].map(([k, v]) => [k, new Set(v)])
12388
+ );
12389
+ const liveNames = new Set(usage.usedColorNames);
12390
+ let demandGrew = true;
12391
+ const addStops = (name, stops) => {
12392
+ let set = effectiveStops.get(name);
12393
+ if (!set) {
12394
+ set = /* @__PURE__ */ new Set();
12395
+ effectiveStops.set(name, set);
12396
+ demandGrew = true;
12397
+ }
12398
+ for (const stop of stops) {
12399
+ if (set.has(stop)) continue;
12400
+ set.add(stop);
12401
+ demandGrew = true;
12402
+ }
12403
+ };
12404
+ const addName = (name) => {
12405
+ if (liveNames.has(name)) return;
12406
+ liveNames.add(name);
12407
+ demandGrew = true;
12408
+ };
12409
+ const scanForColors = (text) => {
12410
+ if (!text.includes("var(--color-")) return;
12411
+ for (const m of text.matchAll(COLOR_VAR_REF_RE)) addName(m[1]);
12412
+ for (const m of text.matchAll(COLOR_STOP_REF_RE)) {
12413
+ if (m[2]) addStops(m[1], [Number(m[2])]);
12414
+ }
12415
+ };
12416
+ for (const name of shadowsToEmit) scanForColors(theme.shadows[name] ?? "");
12417
+ for (const body of usage.keyframes) scanForColors(body);
12418
+ for (const registration of theme.registeredProperties) {
12419
+ if (registration.initialValue) scanForColors(registration.initialValue);
12420
+ }
12421
+ while (demandGrew) {
12422
+ demandGrew = false;
12423
+ for (const [name, def] of Object.entries(theme.colors)) {
12424
+ const stops = effectiveStops.get(name);
12425
+ const liveByStop = stops !== void 0 && stops.size > 0;
12426
+ const liveByName = liveNames.has(name);
12427
+ if (!liveByStop && !liveByName) continue;
12428
+ switch (def.type) {
12429
+ case "alias":
12430
+ if (liveByStop) addStops(def.source, [...stops]);
12431
+ if (liveByName) addName(def.source);
12432
+ break;
12433
+ case "explicit":
12434
+ scanForColors(def.value);
12435
+ break;
12436
+ case "pair":
12437
+ scanForColors(def.light);
12438
+ scanForColors(def.dark);
12439
+ break;
12440
+ default:
12441
+ break;
12442
+ }
12443
+ }
12444
+ }
12445
+ const themeSuffixes = effectiveStops.get("theme");
12446
+ if (theme.colors.theme && themeSuffixes) {
12447
+ for (const [name, def] of Object.entries(theme.colors)) {
12448
+ if (name === "theme" || def.type !== "generative" || !def.inline) continue;
12449
+ addStops(name, themeSuffixes);
12450
+ }
12451
+ }
12452
+ return { effectiveStops, liveNames, shadowsToEmit };
12453
+ }
12454
+ function generateTokenLayer(theme, usage, fontOutputCache, demand = resolveColorDemand(theme, usage)) {
12455
+ const vars = [];
12456
+ vars.push(`--spacing: ${theme.spacing.base};`);
12457
+ const pushBounds = (prefix, config) => {
12458
+ if (config?.min !== void 0) vars.push(`${prefix}-min: ${config.min};`);
12459
+ if (config?.max !== void 0) vars.push(`${prefix}-max: ${config.max};`);
12460
+ };
12461
+ pushBounds("--fluid", theme.fluid);
12462
+ pushBounds("--fluid-text", theme.textFluid);
12463
+ pushBounds("--fluid-spacing", theme.spacingFluid);
12464
+ for (const [name, range] of Object.entries(theme.fluidRanges)) {
12465
+ pushBounds(`--fluid-${name}`, range);
12466
+ }
12467
+ const { effectiveStops, liveNames, shadowsToEmit } = demand;
12468
+ const colorVars = generateAllColorVariables(
12469
+ theme.colors,
12470
+ theme.darkConfig,
12471
+ effectiveStops,
12472
+ liveNames
12473
+ );
12474
+ vars.push(...colorVars);
12475
+ for (const [name, size] of Object.entries(theme.text).sort(
12476
+ ([a], [b]) => codepointCompare(a, b)
12477
+ )) {
12478
+ if (!usage.usedTextSizes.has(name)) continue;
12479
+ vars.push(`--text-${name}: ${size.fontSize};`);
12480
+ vars.push(`--text-${name}-leading: ${size.lineHeight};`);
12481
+ }
12482
+ const configuredSlots = /* @__PURE__ */ new Set();
12483
+ for (const fontConfig of theme.fonts) {
12484
+ if (!usage.usedFonts.has(fontConfig.slot)) continue;
12485
+ const output = getCachedFontOutput(fontConfig, fontOutputCache);
12486
+ vars.push(...output.variables);
12487
+ configuredSlots.add(fontConfig.slot);
12488
+ }
12489
+ for (const [slot, stack] of Object.entries(SYSTEM_STACKS).sort(
12490
+ ([a], [b]) => codepointCompare(a, b)
12491
+ )) {
12492
+ if (!configuredSlots.has(slot) && usage.usedFonts.has(slot)) {
12493
+ vars.push(`--font-${slot}: ${stack};`);
12494
+ }
12495
+ }
12496
+ for (const [name, val] of Object.entries(theme.shadows).sort(
12497
+ ([a], [b]) => codepointCompare(a, b)
12498
+ )) {
12499
+ if (!shadowsToEmit.has(name)) continue;
12500
+ vars.push(`--shadow-${name}: ${val};`);
12501
+ }
12502
+ for (const [name, val] of Object.entries(theme.radii).sort(
12503
+ ([a], [b]) => codepointCompare(a, b)
12504
+ )) {
12505
+ vars.push(`--rounded-${name}: ${val};`);
12506
+ }
12507
+ for (const [name, def] of Object.entries(theme.animations).sort(
12508
+ ([a], [b]) => codepointCompare(a, b)
12509
+ )) {
12510
+ if (!usage.usedAnimations.has(name)) continue;
12511
+ vars.push(`--animate-${name}: ${def.shorthand};`);
12512
+ }
12513
+ return `:root {
12514
+ ${vars.join("\n ")}
12515
+ }`;
12516
+ }
12517
+ function resolveTransitiveShadowDeps(shadows, seeds) {
12518
+ const out = /* @__PURE__ */ new Set();
12519
+ const worklist = [];
12520
+ for (const name of seeds) {
12521
+ if (Object.hasOwn(shadows, name)) {
12522
+ out.add(name);
12523
+ worklist.push(name);
12524
+ }
12525
+ }
12526
+ while (worklist.length > 0) {
12527
+ const name = worklist.pop();
12528
+ const value = shadows[name];
12529
+ if (!value?.includes("var(--shadow-")) continue;
12530
+ for (const m of value.matchAll(SHADOW_VAR_REF_RE)) {
12531
+ const dep = m[1];
12532
+ if (!out.has(dep) && Object.hasOwn(shadows, dep)) {
12533
+ out.add(dep);
12534
+ worklist.push(dep);
12535
+ }
12536
+ }
12537
+ }
12538
+ return out;
12539
+ }
12540
+ function cornerShapeValue(shape) {
12541
+ return typeof shape === "string" ? shape : `superellipse(${shape.superellipse})`;
12542
+ }
12543
+ function generateCornerShapeBlock(theme) {
12544
+ if (theme.roundedShape === null) return null;
12545
+ const value = cornerShapeValue(theme.roundedShape);
12546
+ const shapeRule = `:root {
12547
+ corner-shape: ${value};
12548
+ }
12549
+ *, ::before, ::after {
12550
+ corner-shape: inherit;
12551
+ }`;
12552
+ if (theme.roundedShapeScale === 1) return shapeRule;
12553
+ const scaleBlock = `@supports (corner-shape: ${value}) {
12554
+ :root {
12555
+ --ri-rounded-scale: ${theme.roundedShapeScale};
12556
+ }
12557
+ }`;
12558
+ return `${shapeRule}
12559
+
12560
+ ${scaleBlock}`;
12561
+ }
12562
+ function fontCacheKey(config) {
12563
+ return JSON.stringify(config);
12564
+ }
12565
+ function getCachedFontOutput(config, cache) {
12566
+ const key = fontCacheKey(config);
12567
+ let cached = cache.get(key);
12568
+ if (!cached) {
12569
+ cached = generateFontCSS(config);
12570
+ cache.set(key, cached);
12571
+ }
12572
+ return cached;
12573
+ }
12574
+ function renderRegisteredProperty(reg) {
12575
+ const lines = [
12576
+ `@property ${reg.name} {`,
12577
+ ` syntax: ${reg.syntax};`,
12578
+ ` inherits: ${reg.inherits};`
12579
+ ];
12580
+ if (reg.initialValue !== void 0) lines.push(` initial-value: ${reg.initialValue};`);
12581
+ lines.push("}");
12582
+ return lines.join("\n");
12583
+ }
12584
+ var PROPERTY_NAME_RE = /@property\s+(--[A-Za-z0-9_-]+)/;
12585
+ function collectPropertyBlocks(registered, engineProperties) {
12586
+ const blocks = [];
12587
+ const seen = /* @__PURE__ */ new Set();
12588
+ const push = (block) => {
12589
+ const name = PROPERTY_NAME_RE.exec(block)?.[1];
12590
+ if (name) {
12591
+ if (seen.has(name)) return;
12592
+ seen.add(name);
12593
+ }
12594
+ blocks.push(block);
12595
+ };
12596
+ for (const reg of registered) push(renderRegisteredProperty(reg));
12597
+ for (const block of engineProperties) push(block);
12598
+ return blocks;
12599
+ }
12600
+ function assembleSections(compilation, theme, fontOutputCache) {
12601
+ const baseSections = [];
12602
+ const fontImports = [];
12603
+ const fontFaceBlocks = [];
12604
+ const assemblyWarnings = [];
12605
+ const fontWarningsEmitted = /* @__PURE__ */ new Set();
12606
+ if (theme.preflight.core !== false) {
12607
+ compilation.usedFonts.add("sans");
12608
+ compilation.usedFonts.add("mono");
12609
+ }
12610
+ for (const fontConfig of theme.fonts) {
12611
+ if (!compilation.usedFonts.has(fontConfig.slot)) continue;
12612
+ const output = getCachedFontOutput(fontConfig, fontOutputCache);
12613
+ fontImports.push(...output.imports);
12614
+ fontFaceBlocks.push(...output.fontFaces);
12615
+ for (const w of output.warnings) {
12616
+ if (!fontWarningsEmitted.has(w)) {
12617
+ assemblyWarnings.push(w);
12618
+ fontWarningsEmitted.add(w);
12619
+ }
12620
+ }
12621
+ }
12622
+ const importsSection = fontImports.length > 0 ? fontImports.join("\n") : null;
12623
+ const propertyBlocks = collectPropertyBlocks(theme.registeredProperties, compilation.properties);
12624
+ if (propertyBlocks.length > 0) {
12625
+ baseSections.push(propertyBlocks.join("\n\n"));
12626
+ }
12627
+ if (fontFaceBlocks.length > 0) {
12628
+ baseSections.push(fontFaceBlocks.join("\n\n"));
12629
+ }
12630
+ const demand = resolveColorDemand(theme, compilation);
12631
+ const tokenLayer = generateTokenLayer(theme, compilation, fontOutputCache, demand);
12632
+ if (tokenLayer) baseSections.push(tokenLayer);
12633
+ for (const w of checkPaletteContrast(theme.colors, compilation.usedColorStops)) {
12634
+ assemblyWarnings.push(w);
12635
+ }
12636
+ const themeOverrides = generateThemeOverrides(theme.colors, demand.effectiveStops.get("theme"));
12637
+ if (themeOverrides.length > 0) {
12638
+ baseSections.push(themeOverrides.join("\n\n"));
12639
+ }
12640
+ const cornerShape = generateCornerShapeBlock(theme);
12641
+ if (cornerShape) baseSections.push(cornerShape);
12642
+ if (compilation.keyframes.length > 0) {
12643
+ baseSections.push(compilation.keyframes.join("\n\n"));
12644
+ }
12645
+ const preflight = generatePreflight(theme.preflight);
12646
+ if (preflight) baseSections.push(preflight);
12647
+ const utilityCSS = renderCSS({
12648
+ ...compilation,
12649
+ properties: [],
12650
+ keyframes: []
12651
+ });
12652
+ const utilitiesSection = utilityCSS !== "" ? utilityCSS : null;
12653
+ const parts = {
12654
+ imports: importsSection,
12655
+ base: baseSections,
12656
+ utilities: utilitiesSection
12657
+ };
12658
+ const sections = theme.layer ? applyLayerWrapping(parts, theme.layer) : [
12659
+ ...importsSection !== null ? [importsSection] : [],
12660
+ ...baseSections,
12661
+ ...utilitiesSection !== null ? [utilitiesSection] : []
12662
+ ];
12663
+ return { sections, warnings: assemblyWarnings };
12664
+ }
12665
+ function wrapInLayer(content, layerName) {
12666
+ const indented = content.replace(/^(?=.)/gm, " ");
12667
+ return `@layer ${layerName} {
12668
+ ${indented}
12669
+ }`;
12670
+ }
12671
+ function applyLayerWrapping(parts, layer) {
12672
+ const sections = [];
12673
+ if (parts.imports !== null) sections.push(parts.imports);
12674
+ if (layer.wrapAll) {
12675
+ sections.push(`@layer ${layer.wrapAll};`);
12676
+ const joined = [...parts.base, ...parts.utilities !== null ? [parts.utilities] : []].join(
12677
+ "\n\n"
12678
+ );
12679
+ if (joined) sections.push(wrapInLayer(joined, layer.wrapAll));
12680
+ return sections;
12681
+ }
12682
+ if (layer.order && layer.order.length > 0) {
12683
+ sections.push(`@layer ${layer.order.join(", ")};`);
12684
+ }
12685
+ if (parts.base.length > 0) {
12686
+ const baseJoined = parts.base.join("\n\n");
12687
+ if (layer.base) {
12688
+ sections.push(wrapInLayer(baseJoined, layer.base));
12689
+ } else {
12690
+ sections.push(baseJoined);
12691
+ }
12692
+ }
12693
+ if (parts.utilities !== null) {
12694
+ if (layer.utilities) {
12695
+ sections.push(wrapInLayer(parts.utilities, layer.utilities));
12696
+ } else {
12697
+ sections.push(parts.utilities);
12698
+ }
12699
+ }
12700
+ return sections;
12701
+ }
12702
+
10536
12703
  export {
10537
- COLOR_STOP_REF_RE,
10538
- SHADOW_VAR_REF_RE,
12704
+ escapeSelector,
10539
12705
  isRIDebug,
10540
12706
  withTimeout,
12707
+ stripCSSComments,
10541
12708
  isAtRuleBoundary,
10542
12709
  codepointCompare,
10543
12710
  APPLY_ALIASES,
@@ -10548,6 +12715,7 @@ export {
10548
12715
  matchCustomUtility,
10549
12716
  forEachApplyClassList,
10550
12717
  DIRECTIVE_TYPE_NAMES,
12718
+ scanEntries,
10551
12719
  findClosingBrace,
10552
12720
  SAFE_FONT_FAMILY_RE,
10553
12721
  weightIsLoaded,
@@ -10583,10 +12751,8 @@ export {
10583
12751
  scanCSSForTokenUsage,
10584
12752
  createEmptyCompilationResult,
10585
12753
  createThemeSnapshot,
10586
- renderCSS,
10587
12754
  createCompiler,
10588
12755
  DIRECTIVE_NAMES_SET,
10589
- directiveAtRulePattern,
10590
12756
  isAtRuleNameChar,
10591
12757
  RI_IMPORT_SPECIFIERS,
10592
12758
  RI_IMPORT_SPECIFIER_ALTERNATION,
@@ -10594,16 +12760,20 @@ export {
10594
12760
  isSuppressible,
10595
12761
  parseFileDisables,
10596
12762
  resolveDirectives,
10597
- EXTRACTABLE_DIRECTIVE_NAMES,
10598
12763
  MAX_DIRECTIVE_INPUT_SIZE,
10599
12764
  extractDirectives,
12765
+ expandApplyBodyGroups,
10600
12766
  expandVariantGroups,
10601
- expandApplyGroups,
12767
+ expandGroupsInStylesheet,
10602
12768
  CLASS_HELPER_NAMES,
10603
12769
  VARIANT_HELPER_NAMES,
10604
12770
  extractClasses,
10605
12771
  extractClassesFromSource,
10606
12772
  extractClassCandidates,
12773
+ getFontPreloadLinks,
12774
+ stripRIDirectives,
12775
+ inlineDirectiveImports,
10607
12776
  analyzeProjectCSSMemo,
10608
- analyzeProjectCSS
12777
+ analyzeProjectCSS,
12778
+ assembleSections
10609
12779
  };