rnwind 0.0.8 → 0.0.9

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 (131) hide show
  1. package/lib/cjs/core/parser/color.cjs +33 -1
  2. package/lib/cjs/core/parser/color.cjs.map +1 -1
  3. package/lib/cjs/core/parser/color.d.ts +10 -0
  4. package/lib/cjs/core/parser/declaration.cjs +121 -9
  5. package/lib/cjs/core/parser/declaration.cjs.map +1 -1
  6. package/lib/cjs/core/parser/gradient.cjs +46 -12
  7. package/lib/cjs/core/parser/gradient.cjs.map +1 -1
  8. package/lib/cjs/core/parser/gradient.d.ts +2 -1
  9. package/lib/cjs/core/parser/keyframes.cjs +27 -12
  10. package/lib/cjs/core/parser/keyframes.cjs.map +1 -1
  11. package/lib/cjs/core/parser/keyframes.d.ts +11 -0
  12. package/lib/cjs/core/parser/layout-dispatcher.cjs +33 -10
  13. package/lib/cjs/core/parser/layout-dispatcher.cjs.map +1 -1
  14. package/lib/cjs/core/parser/length.cjs +17 -1
  15. package/lib/cjs/core/parser/length.cjs.map +1 -1
  16. package/lib/cjs/core/parser/safe-area.cjs +24 -3
  17. package/lib/cjs/core/parser/safe-area.cjs.map +1 -1
  18. package/lib/cjs/core/parser/theme-vars.cjs +58 -8
  19. package/lib/cjs/core/parser/theme-vars.cjs.map +1 -1
  20. package/lib/cjs/core/parser/tokens.cjs +77 -9
  21. package/lib/cjs/core/parser/tokens.cjs.map +1 -1
  22. package/lib/cjs/core/parser/tokens.d.ts +9 -0
  23. package/lib/cjs/core/parser/transform.cjs +18 -9
  24. package/lib/cjs/core/parser/transform.cjs.map +1 -1
  25. package/lib/cjs/core/parser/tw-parser.cjs +93 -33
  26. package/lib/cjs/core/parser/tw-parser.cjs.map +1 -1
  27. package/lib/cjs/core/parser/typography-dispatcher.cjs +19 -1
  28. package/lib/cjs/core/parser/typography-dispatcher.cjs.map +1 -1
  29. package/lib/cjs/core/parser/typography.cjs +15 -18
  30. package/lib/cjs/core/parser/typography.cjs.map +1 -1
  31. package/lib/cjs/core/parser/typography.d.ts +5 -5
  32. package/lib/cjs/core/style-builder/union-builder.cjs +0 -10
  33. package/lib/cjs/core/style-builder/union-builder.cjs.map +1 -1
  34. package/lib/cjs/core/style-builder/union-builder.d.ts +0 -8
  35. package/lib/cjs/metro/dts.cjs +6 -1
  36. package/lib/cjs/metro/dts.cjs.map +1 -1
  37. package/lib/cjs/metro/transformer.cjs +42 -77
  38. package/lib/cjs/metro/transformer.cjs.map +1 -1
  39. package/lib/cjs/metro/with-config.cjs +9 -29
  40. package/lib/cjs/metro/with-config.cjs.map +1 -1
  41. package/lib/cjs/runtime/hooks/use-scheme.cjs +9 -6
  42. package/lib/cjs/runtime/hooks/use-scheme.cjs.map +1 -1
  43. package/lib/cjs/runtime/hooks/use-scheme.d.ts +7 -4
  44. package/lib/cjs/runtime/index.cjs +1 -1
  45. package/lib/cjs/runtime/index.cjs.map +1 -1
  46. package/lib/cjs/runtime/index.d.ts +1 -1
  47. package/lib/cjs/runtime/lookup-css.cjs +14 -0
  48. package/lib/cjs/runtime/lookup-css.cjs.map +1 -1
  49. package/lib/cjs/runtime/lookup-css.d.ts +11 -0
  50. package/lib/cjs/runtime/resolve.cjs +8 -6
  51. package/lib/cjs/runtime/resolve.cjs.map +1 -1
  52. package/lib/cjs/runtime/wrap.cjs +50 -57
  53. package/lib/cjs/runtime/wrap.cjs.map +1 -1
  54. package/lib/cjs/runtime/wrap.d.ts +10 -4
  55. package/lib/esm/core/parser/color.d.ts +10 -0
  56. package/lib/esm/core/parser/color.mjs +33 -2
  57. package/lib/esm/core/parser/color.mjs.map +1 -1
  58. package/lib/esm/core/parser/declaration.mjs +122 -10
  59. package/lib/esm/core/parser/declaration.mjs.map +1 -1
  60. package/lib/esm/core/parser/gradient.d.ts +2 -1
  61. package/lib/esm/core/parser/gradient.mjs +45 -11
  62. package/lib/esm/core/parser/gradient.mjs.map +1 -1
  63. package/lib/esm/core/parser/keyframes.d.ts +11 -0
  64. package/lib/esm/core/parser/keyframes.mjs +27 -12
  65. package/lib/esm/core/parser/keyframes.mjs.map +1 -1
  66. package/lib/esm/core/parser/layout-dispatcher.mjs +33 -10
  67. package/lib/esm/core/parser/layout-dispatcher.mjs.map +1 -1
  68. package/lib/esm/core/parser/length.mjs +17 -1
  69. package/lib/esm/core/parser/length.mjs.map +1 -1
  70. package/lib/esm/core/parser/safe-area.mjs +24 -3
  71. package/lib/esm/core/parser/safe-area.mjs.map +1 -1
  72. package/lib/esm/core/parser/theme-vars.mjs +58 -8
  73. package/lib/esm/core/parser/theme-vars.mjs.map +1 -1
  74. package/lib/esm/core/parser/tokens.d.ts +9 -0
  75. package/lib/esm/core/parser/tokens.mjs +77 -10
  76. package/lib/esm/core/parser/tokens.mjs.map +1 -1
  77. package/lib/esm/core/parser/transform.mjs +18 -9
  78. package/lib/esm/core/parser/transform.mjs.map +1 -1
  79. package/lib/esm/core/parser/tw-parser.mjs +95 -35
  80. package/lib/esm/core/parser/tw-parser.mjs.map +1 -1
  81. package/lib/esm/core/parser/typography-dispatcher.mjs +19 -1
  82. package/lib/esm/core/parser/typography-dispatcher.mjs.map +1 -1
  83. package/lib/esm/core/parser/typography.d.ts +5 -5
  84. package/lib/esm/core/parser/typography.mjs +15 -18
  85. package/lib/esm/core/parser/typography.mjs.map +1 -1
  86. package/lib/esm/core/style-builder/union-builder.d.ts +0 -8
  87. package/lib/esm/core/style-builder/union-builder.mjs +0 -10
  88. package/lib/esm/core/style-builder/union-builder.mjs.map +1 -1
  89. package/lib/esm/metro/dts.mjs +6 -1
  90. package/lib/esm/metro/dts.mjs.map +1 -1
  91. package/lib/esm/metro/transformer.mjs +42 -77
  92. package/lib/esm/metro/transformer.mjs.map +1 -1
  93. package/lib/esm/metro/with-config.mjs +10 -30
  94. package/lib/esm/metro/with-config.mjs.map +1 -1
  95. package/lib/esm/runtime/hooks/use-scheme.d.ts +7 -4
  96. package/lib/esm/runtime/hooks/use-scheme.mjs +9 -6
  97. package/lib/esm/runtime/hooks/use-scheme.mjs.map +1 -1
  98. package/lib/esm/runtime/index.d.ts +1 -1
  99. package/lib/esm/runtime/index.mjs +1 -1
  100. package/lib/esm/runtime/index.mjs.map +1 -1
  101. package/lib/esm/runtime/lookup-css.d.ts +11 -0
  102. package/lib/esm/runtime/lookup-css.mjs +14 -1
  103. package/lib/esm/runtime/lookup-css.mjs.map +1 -1
  104. package/lib/esm/runtime/resolve.mjs +9 -7
  105. package/lib/esm/runtime/resolve.mjs.map +1 -1
  106. package/lib/esm/runtime/wrap.d.ts +10 -4
  107. package/lib/esm/runtime/wrap.mjs +50 -57
  108. package/lib/esm/runtime/wrap.mjs.map +1 -1
  109. package/package.json +1 -1
  110. package/src/core/parser/color.ts +32 -1
  111. package/src/core/parser/declaration.ts +119 -10
  112. package/src/core/parser/gradient.ts +48 -11
  113. package/src/core/parser/keyframes.ts +31 -3
  114. package/src/core/parser/layout-dispatcher.ts +32 -9
  115. package/src/core/parser/length.ts +18 -1
  116. package/src/core/parser/safe-area.ts +23 -2
  117. package/src/core/parser/theme-vars.ts +75 -8
  118. package/src/core/parser/tokens.ts +76 -9
  119. package/src/core/parser/transform.ts +19 -8
  120. package/src/core/parser/tw-parser.ts +95 -30
  121. package/src/core/parser/typography-dispatcher.ts +20 -1
  122. package/src/core/parser/typography.ts +15 -15
  123. package/src/core/style-builder/union-builder.ts +0 -11
  124. package/src/metro/dts.ts +6 -1
  125. package/src/metro/transformer.ts +42 -78
  126. package/src/metro/with-config.ts +10 -29
  127. package/src/runtime/hooks/use-scheme.ts +9 -6
  128. package/src/runtime/index.ts +1 -1
  129. package/src/runtime/lookup-css.ts +14 -0
  130. package/src/runtime/resolve.ts +9 -7
  131. package/src/runtime/wrap.tsx +57 -61
@@ -53,7 +53,8 @@ export type GradientDirection = 'to-r' | 'to-l' | 'to-t' | 'to-b' | 'to-tr' | 't
53
53
  * return the atom's role + data. Returns `null` for rules that don't
54
54
  * belong to a gradient utility.
55
55
  * @param declarations Declarations from one lightningcss style rule.
56
+ * @param themeVars
56
57
  * @returns Gradient info, or null.
57
58
  */
58
- declare function detectGradientAtom(declarations: readonly LcDeclaration[]): GradientAtomInfo | null;
59
+ declare function detectGradientAtom(declarations: readonly LcDeclaration[], themeVars?: ReadonlyMap<string, string>): GradientAtomInfo | null;
59
60
  export { detectGradientAtom };
@@ -37,17 +37,13 @@ function keyframesName(raw) {
37
37
  return raw.value;
38
38
  }
39
39
  /**
40
- * Render a keyframe step's selector list back to CSS-text (`'from'`,
41
- * `'to'`, or `'50%'`). Timeline-range keyframe selectors (CSS Scroll /
42
- * View Timelines) can't run in React Native — those steps are skipped.
43
- * @param selectors Step selectors.
44
- * @returns Step offset, or `null` when unrepresentable in RN.
40
+ * Render ONE keyframe selector to its CSS offset text, or `null` when it's a
41
+ * timeline-range selector RN can't run.
42
+ * @param selector A single keyframe selector.
43
+ * @returns Offset text (`'from'` / `'to'` / `'50%'`), or `null`.
45
44
  */
46
- function keyframeSelectorOffset(selectors) {
47
- const [head] = selectors;
48
- if (!head)
49
- return null;
50
- switch (head.type) {
45
+ function offsetForSelector(selector) {
46
+ switch (selector.type) {
51
47
  case 'from': {
52
48
  return 'from';
53
49
  }
@@ -55,13 +51,32 @@ function keyframeSelectorOffset(selectors) {
55
51
  return 'to';
56
52
  }
57
53
  case 'percentage': {
58
- return `${head.value * 100}%`;
54
+ return `${selector.value * 100}%`;
59
55
  }
60
56
  default: {
61
57
  return null;
62
58
  }
63
59
  }
64
60
  }
61
+ /**
62
+ * Render EVERY representable offset in a frame's selector list. Tailwind
63
+ * collapses shared steps into one frame with multiple selectors — `animate-ping`
64
+ * emits `75%, 100% { … }`, `animate-bounce` emits `0%, 100% { … }`. Reading only
65
+ * the first selector (the old singular helper) dropped the terminal `100%`, so
66
+ * the looping animation never defined its end/return-to-start state. Returns all
67
+ * offsets the frame's style applies to; timeline-range selectors are skipped.
68
+ * @param selectors Step selectors for one frame.
69
+ * @returns Every representable offset (may be empty).
70
+ */
71
+ function keyframeSelectorOffsets(selectors) {
72
+ const offsets = [];
73
+ for (const selector of selectors) {
74
+ const offset = offsetForSelector(selector);
75
+ if (offset !== null)
76
+ offsets.push(offset);
77
+ }
78
+ return offsets;
79
+ }
65
80
  /**
66
81
  * Extract the referenced `@keyframes` name from a declaration whose
67
82
  * property is `animation-name` or a shorthand `animation` that names one.
@@ -89,7 +104,7 @@ function pickAnimationName(decl) {
89
104
  return null;
90
105
  }
91
106
 
92
- exports.keyframeSelectorOffset = keyframeSelectorOffset;
107
+ exports.keyframeSelectorOffsets = keyframeSelectorOffsets;
93
108
  exports.keyframesName = keyframesName;
94
109
  exports.pickAnimationName = pickAnimationName;
95
110
  //# sourceMappingURL=keyframes.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"keyframes.cjs","sources":["../../../../../src/core/parser/keyframes.ts"],"sourcesContent":["import type { Animation, Declaration as LcDeclaration, KeyframeSelector, KeyframesName } from 'lightningcss'\n\n/**\n * Pull the first keyframe name out of a typed `animation` shorthand.\n * @param animations Parsed animation shorthand entries.\n * @returns First ident name, or `null`.\n */\nfunction firstAnimationName(animations: readonly Animation[]): string | null {\n for (const animation of animations) {\n if (animation.name.type === 'ident' || animation.name.type === 'string') return animation.name.value\n }\n return null\n}\n\n/**\n * Pull the first ident name from a typed `animation-name` longhand.\n * @param names Animation-name list.\n * @returns First name, or `null`.\n */\nfunction firstNameFromAnimationNames(names: readonly { type: string; value?: string }[]): string | null {\n for (const name of names) {\n if ((name.type === 'ident' || name.type === 'string') && typeof name.value === 'string') return name.value\n }\n return null\n}\n\n/**\n * Extract the animation name of an `@keyframes` rule. lightningcss models\n * the name as a discriminated union (`ident` / `custom`); both variants\n * carry the same downstream string.\n * @param raw lightningcss `KeyframesName`.\n * @returns Animation name, or `null` when empty.\n */\nexport function keyframesName(raw: KeyframesName): string | null {\n if (typeof raw.value !== 'string' || raw.value.length === 0) return null\n return raw.value\n}\n\n/**\n * Render a keyframe step's selector list back to CSS-text (`'from'`,\n * `'to'`, or `'50%'`). Timeline-range keyframe selectors (CSS Scroll /\n * View Timelines) can't run in React Native — those steps are skipped.\n * @param selectors Step selectors.\n * @returns Step offset, or `null` when unrepresentable in RN.\n */\nexport function keyframeSelectorOffset(selectors: readonly KeyframeSelector[]): string | null {\n const [head] = selectors\n if (!head) return null\n switch (head.type) {\n case 'from': {\n return 'from'\n }\n case 'to': {\n return 'to'\n }\n case 'percentage': {\n return `${head.value * 100}%`\n }\n default: {\n return null\n }\n }\n}\n\n/**\n * Extract the referenced `@keyframes` name from a declaration whose\n * property is `animation-name` or a shorthand `animation` that names one.\n * Returns the first ident found inside the value — Tailwind's animate-*\n * utilities emit exactly one animation-name per rule.\n * @param decl One declaration from a style rule.\n * @returns Keyframe name, or `null` when the declaration doesn't reference one.\n */\nexport function pickAnimationName(decl: LcDeclaration): string | null {\n if (decl.property === 'animation') return firstAnimationName(decl.value)\n if (decl.property === 'animation-name') return firstNameFromAnimationNames(decl.value)\n if (decl.property !== 'unparsed') return null\n const targetProperty = decl.value.propertyId.property\n if (targetProperty !== 'animation-name' && targetProperty !== 'animation') return null\n for (const token of decl.value.value) {\n if (token.type === 'token' && token.value.type === 'ident') return token.value.value\n if (token.type === 'dashed-ident') return token.value\n }\n return null\n}\n"],"names":[],"mappings":";;AAEA;;;;AAIG;AACH,SAAS,kBAAkB,CAAC,UAAgC,EAAA;AAC1D,IAAA,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;AAClC,QAAA,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ;AAAE,YAAA,OAAO,SAAS,CAAC,IAAI,CAAC,KAAK;IACtG;AACA,IAAA,OAAO,IAAI;AACb;AAEA;;;;AAIG;AACH,SAAS,2BAA2B,CAAC,KAAkD,EAAA;AACrF,IAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACxB,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,KAAK,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,KAAK;IAC5G;AACA,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;AAMG;AACG,SAAU,aAAa,CAAC,GAAkB,EAAA;AAC9C,IAAA,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;IACxE,OAAO,GAAG,CAAC,KAAK;AAClB;AAEA;;;;;;AAMG;AACG,SAAU,sBAAsB,CAAC,SAAsC,EAAA;AAC3E,IAAA,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS;AACxB,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,IAAI;AACtB,IAAA,QAAQ,IAAI,CAAC,IAAI;QACf,KAAK,MAAM,EAAE;AACX,YAAA,OAAO,MAAM;QACf;QACA,KAAK,IAAI,EAAE;AACT,YAAA,OAAO,IAAI;QACb;QACA,KAAK,YAAY,EAAE;AACjB,YAAA,OAAO,GAAG,IAAI,CAAC,KAAK,GAAG,GAAG,GAAG;QAC/B;QACA,SAAS;AACP,YAAA,OAAO,IAAI;QACb;;AAEJ;AAEA;;;;;;;AAOG;AACG,SAAU,iBAAiB,CAAC,IAAmB,EAAA;AACnD,IAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,WAAW;AAAE,QAAA,OAAO,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC;AACxE,IAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,gBAAgB;AAAE,QAAA,OAAO,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC;AACtF,IAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU;AAAE,QAAA,OAAO,IAAI;IAC7C,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ;AACrD,IAAA,IAAI,cAAc,KAAK,gBAAgB,IAAI,cAAc,KAAK,WAAW;AAAE,QAAA,OAAO,IAAI;IACtF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE;AACpC,QAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO;AAAE,YAAA,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK;AACpF,QAAA,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;YAAE,OAAO,KAAK,CAAC,KAAK;IACvD;AACA,IAAA,OAAO,IAAI;AACb;;;;;;"}
1
+ {"version":3,"file":"keyframes.cjs","sources":["../../../../../src/core/parser/keyframes.ts"],"sourcesContent":["import type { Animation, Declaration as LcDeclaration, KeyframeSelector, KeyframesName } from 'lightningcss'\n\n/**\n * Pull the first keyframe name out of a typed `animation` shorthand.\n * @param animations Parsed animation shorthand entries.\n * @returns First ident name, or `null`.\n */\nfunction firstAnimationName(animations: readonly Animation[]): string | null {\n for (const animation of animations) {\n if (animation.name.type === 'ident' || animation.name.type === 'string') return animation.name.value\n }\n return null\n}\n\n/**\n * Pull the first ident name from a typed `animation-name` longhand.\n * @param names Animation-name list.\n * @returns First name, or `null`.\n */\nfunction firstNameFromAnimationNames(names: readonly { type: string; value?: string }[]): string | null {\n for (const name of names) {\n if ((name.type === 'ident' || name.type === 'string') && typeof name.value === 'string') return name.value\n }\n return null\n}\n\n/**\n * Extract the animation name of an `@keyframes` rule. lightningcss models\n * the name as a discriminated union (`ident` / `custom`); both variants\n * carry the same downstream string.\n * @param raw lightningcss `KeyframesName`.\n * @returns Animation name, or `null` when empty.\n */\nexport function keyframesName(raw: KeyframesName): string | null {\n if (typeof raw.value !== 'string' || raw.value.length === 0) return null\n return raw.value\n}\n\n/**\n * Render a keyframe step's selector list back to CSS-text (`'from'`,\n * `'to'`, or `'50%'`). Timeline-range keyframe selectors (CSS Scroll /\n * View Timelines) can't run in React Native — those steps are skipped.\n * @param selectors Step selectors.\n * @returns Step offset, or `null` when unrepresentable in RN.\n */\nexport function keyframeSelectorOffset(selectors: readonly KeyframeSelector[]): string | null {\n const [head] = selectors\n return head ? offsetForSelector(head) : null\n}\n\n/**\n * Render ONE keyframe selector to its CSS offset text, or `null` when it's a\n * timeline-range selector RN can't run.\n * @param selector A single keyframe selector.\n * @returns Offset text (`'from'` / `'to'` / `'50%'`), or `null`.\n */\nfunction offsetForSelector(selector: KeyframeSelector): string | null {\n switch (selector.type) {\n case 'from': {\n return 'from'\n }\n case 'to': {\n return 'to'\n }\n case 'percentage': {\n return `${selector.value * 100}%`\n }\n default: {\n return null\n }\n }\n}\n\n/**\n * Render EVERY representable offset in a frame's selector list. Tailwind\n * collapses shared steps into one frame with multiple selectors — `animate-ping`\n * emits `75%, 100% { … }`, `animate-bounce` emits `0%, 100% { … }`. Reading only\n * the first selector (the old singular helper) dropped the terminal `100%`, so\n * the looping animation never defined its end/return-to-start state. Returns all\n * offsets the frame's style applies to; timeline-range selectors are skipped.\n * @param selectors Step selectors for one frame.\n * @returns Every representable offset (may be empty).\n */\nexport function keyframeSelectorOffsets(selectors: readonly KeyframeSelector[]): readonly string[] {\n const offsets: string[] = []\n for (const selector of selectors) {\n const offset = offsetForSelector(selector)\n if (offset !== null) offsets.push(offset)\n }\n return offsets\n}\n\n/**\n * Extract the referenced `@keyframes` name from a declaration whose\n * property is `animation-name` or a shorthand `animation` that names one.\n * Returns the first ident found inside the value — Tailwind's animate-*\n * utilities emit exactly one animation-name per rule.\n * @param decl One declaration from a style rule.\n * @returns Keyframe name, or `null` when the declaration doesn't reference one.\n */\nexport function pickAnimationName(decl: LcDeclaration): string | null {\n if (decl.property === 'animation') return firstAnimationName(decl.value)\n if (decl.property === 'animation-name') return firstNameFromAnimationNames(decl.value)\n if (decl.property !== 'unparsed') return null\n const targetProperty = decl.value.propertyId.property\n if (targetProperty !== 'animation-name' && targetProperty !== 'animation') return null\n for (const token of decl.value.value) {\n if (token.type === 'token' && token.value.type === 'ident') return token.value.value\n if (token.type === 'dashed-ident') return token.value\n }\n return null\n}\n"],"names":[],"mappings":";;AAEA;;;;AAIG;AACH,SAAS,kBAAkB,CAAC,UAAgC,EAAA;AAC1D,IAAA,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;AAClC,QAAA,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ;AAAE,YAAA,OAAO,SAAS,CAAC,IAAI,CAAC,KAAK;IACtG;AACA,IAAA,OAAO,IAAI;AACb;AAEA;;;;AAIG;AACH,SAAS,2BAA2B,CAAC,KAAkD,EAAA;AACrF,IAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACxB,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,KAAK,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,KAAK;IAC5G;AACA,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;AAMG;AACG,SAAU,aAAa,CAAC,GAAkB,EAAA;AAC9C,IAAA,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;IACxE,OAAO,GAAG,CAAC,KAAK;AAClB;AAcA;;;;;AAKG;AACH,SAAS,iBAAiB,CAAC,QAA0B,EAAA;AACnD,IAAA,QAAQ,QAAQ,CAAC,IAAI;QACnB,KAAK,MAAM,EAAE;AACX,YAAA,OAAO,MAAM;QACf;QACA,KAAK,IAAI,EAAE;AACT,YAAA,OAAO,IAAI;QACb;QACA,KAAK,YAAY,EAAE;AACjB,YAAA,OAAO,GAAG,QAAQ,CAAC,KAAK,GAAG,GAAG,GAAG;QACnC;QACA,SAAS;AACP,YAAA,OAAO,IAAI;QACb;;AAEJ;AAEA;;;;;;;;;AASG;AACG,SAAU,uBAAuB,CAAC,SAAsC,EAAA;IAC5E,MAAM,OAAO,GAAa,EAAE;AAC5B,IAAA,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE;AAChC,QAAA,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC;QAC1C,IAAI,MAAM,KAAK,IAAI;AAAE,YAAA,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;IAC3C;AACA,IAAA,OAAO,OAAO;AAChB;AAEA;;;;;;;AAOG;AACG,SAAU,iBAAiB,CAAC,IAAmB,EAAA;AACnD,IAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,WAAW;AAAE,QAAA,OAAO,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC;AACxE,IAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,gBAAgB;AAAE,QAAA,OAAO,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC;AACtF,IAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU;AAAE,QAAA,OAAO,IAAI;IAC7C,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ;AACrD,IAAA,IAAI,cAAc,KAAK,gBAAgB,IAAI,cAAc,KAAK,WAAW;AAAE,QAAA,OAAO,IAAI;IACtF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE;AACpC,QAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO;AAAE,YAAA,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK;AACpF,QAAA,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;YAAE,OAAO,KAAK,CAAC,KAAK;IACvD;AACA,IAAA,OAAO,IAAI;AACb;;;;;;"}
@@ -15,6 +15,17 @@ export declare function keyframesName(raw: KeyframesName): string | null;
15
15
  * @returns Step offset, or `null` when unrepresentable in RN.
16
16
  */
17
17
  export declare function keyframeSelectorOffset(selectors: readonly KeyframeSelector[]): string | null;
18
+ /**
19
+ * Render EVERY representable offset in a frame's selector list. Tailwind
20
+ * collapses shared steps into one frame with multiple selectors — `animate-ping`
21
+ * emits `75%, 100% { … }`, `animate-bounce` emits `0%, 100% { … }`. Reading only
22
+ * the first selector (the old singular helper) dropped the terminal `100%`, so
23
+ * the looping animation never defined its end/return-to-start state. Returns all
24
+ * offsets the frame's style applies to; timeline-range selectors are skipped.
25
+ * @param selectors Step selectors for one frame.
26
+ * @returns Every representable offset (may be empty).
27
+ */
28
+ export declare function keyframeSelectorOffsets(selectors: readonly KeyframeSelector[]): readonly string[];
18
29
  /**
19
30
  * Extract the referenced `@keyframes` name from a declaration whose
20
31
  * property is `animation-name` or a shorthand `animation` that names one.
@@ -1,5 +1,33 @@
1
1
  'use strict';
2
2
 
3
+ /**
4
+ * Lower a CSS `overflow` keyword to one RN's `overflow` prop accepts
5
+ * (`'visible' | 'hidden' | 'scroll'`). `auto` → `scroll` (auto means
6
+ * scroll-when-needed), `clip` → `hidden` (closest no-scroll clip). Anything
7
+ * else (an unexpected keyword) drops so RN never sees an invalid value.
8
+ * @param css CSS overflow keyword.
9
+ * @returns RN overflow keyword, or `null` to drop.
10
+ */
11
+ function mapOverflow(css) {
12
+ if (css === 'visible' || css === 'hidden' || css === 'scroll')
13
+ return css;
14
+ if (css === 'auto')
15
+ return 'scroll';
16
+ if (css === 'clip')
17
+ return 'hidden';
18
+ return null;
19
+ }
20
+ /**
21
+ * Build the RN `overflow` entry for a raw axis value, mapping it to an
22
+ * RN-valid keyword and dropping unrepresentable shapes. Shared by the
23
+ * `overflow` shorthand and the `overflow-x` / `overflow-y` longhands.
24
+ * @param value Raw per-axis overflow value (string keyword or other).
25
+ * @returns Single `[overflow, …]` entry, or empty when unmappable.
26
+ */
27
+ function overflowEntry(value) {
28
+ const mapped = typeof value === 'string' ? mapOverflow(value) : null;
29
+ return mapped === null ? [] : [['overflow', mapped]];
30
+ }
3
31
  /**
4
32
  * Lower CSS alignment keywords to the strings RN accepts. CSS uses
5
33
  * `start`/`end` while RN sticks with the legacy `flex-start`/`flex-end`.
@@ -92,15 +120,10 @@ function dispatchLayoutDeclaration(decl) {
92
120
  }
93
121
  case 'overflow': {
94
122
  // Lightningcss splits CSS `overflow` into `{x, y}` axes; RN only
95
- // supports a single `overflow` keyword (and only `'hidden' |
96
- // 'visible' | 'scroll'` on iOS, `'hidden' | 'visible'` on
97
- // Android RN ignores unsupported keywords at runtime). Take
98
- // the `x` axis when the user wrote shorthand; per-axis Tailwind
99
- // utilities both emit shorthand here so axis splitting is rare.
100
- const value = decl.value;
101
- if (typeof value.x !== 'string')
102
- return [];
103
- return [['overflow', value.x]];
123
+ // supports a single `overflow` keyword. Take the `x` axis when the
124
+ // user wrote shorthand; per-axis Tailwind utilities both emit
125
+ // shorthand here so axis splitting is rare.
126
+ return overflowEntry(decl.value.x);
104
127
  }
105
128
  case 'overflow-x':
106
129
  case 'overflow-y': {
@@ -108,7 +131,7 @@ function dispatchLayoutDeclaration(decl) {
108
131
  // not the `overflow` shorthand. RN has only a single `overflow`,
109
132
  // so collapse both axes onto it (last one declared wins via the
110
133
  // normal entry-merge order).
111
- return typeof decl.value === 'string' ? [['overflow', decl.value]] : [];
134
+ return overflowEntry(decl.value);
112
135
  }
113
136
  default: {
114
137
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"layout-dispatcher.cjs","sources":["../../../../../src/core/parser/layout-dispatcher.ts"],"sourcesContent":["import type { Declaration as LcDeclaration } from 'lightningcss'\nimport type { RNEntry } from './types'\n\n/**\n * Lower CSS alignment keywords to the strings RN accepts. CSS uses\n * `start`/`end` while RN sticks with the legacy `flex-start`/`flex-end`.\n * Shared between `align-items`, `align-self`, `align-content`, and\n * `justify-content` — the lowering rule is identical for all four.\n * @param css CSS keyword (`center` / `start` / `end` / `baseline` / `stretch`).\n * @returns RN-compatible keyword.\n */\nfunction cssToRnAlignment(css: string): string {\n if (css === 'start') return 'flex-start'\n if (css === 'end') return 'flex-end'\n return css\n}\n\n/** Alias kept for clarity at the call site. Identical lowering rule. */\nconst cssToRnJustify = cssToRnAlignment\n\n/**\n * Map lightningcss's `align-items` / `align-self` / `align-content`\n * value (typed as `{type: 'self-position' | 'baseline-position', value: …}`)\n * to the RN keyword RN expects.\n * @param value Typed alignment value.\n * @returns RN alignment string, or `null` when unmappable.\n */\nfunction mapAlignKeyword(value: unknown): string | null {\n if (typeof value === 'string') return cssToRnAlignment(value)\n if (typeof value !== 'object' || value === null) return null\n const tagged = value as { type?: string; value?: string }\n if (tagged.type === 'baseline-position') return 'baseline'\n if (typeof tagged.value === 'string') return cssToRnAlignment(tagged.value)\n // Bare-keyword variants like `{type: 'stretch'}` carry the keyword\n // in the `type` field with no separate `value`.\n if (tagged.type === 'stretch' || tagged.type === 'normal') return cssToRnAlignment(tagged.type)\n return null\n}\n\n/**\n * Map lightningcss's `justify-content` value to the RN keyword.\n * @param value Typed justify value.\n * @returns RN justify string, or `null` when unmappable.\n */\nfunction mapJustifyKeyword(value: unknown): string | null {\n if (typeof value === 'string') return cssToRnJustify(value)\n if (typeof value !== 'object' || value === null) return null\n const tagged = value as { type?: string; value?: string }\n if (typeof tagged.value === 'string') return cssToRnJustify(tagged.value)\n return null\n}\n\n/**\n * Dispatch flexbox-layout declarations (flex-direction, flex-wrap,\n * align-items, align-self, align-content, justify-content). Returns\n * `null` for any property the dispatcher doesn't handle so the caller\n * can fall through to its main switch.\n *\n * RN expects keyword-mapped strings: `flex-start` / `flex-end` instead\n * of CSS's `start` / `end`. We do the lowering here.\n * @param decl One lightningcss declaration.\n * @returns RN entries when the property matched, else `null`.\n */\nexport function dispatchLayoutDeclaration(decl: LcDeclaration): readonly RNEntry[] | null {\n switch (decl.property) {\n case 'flex-direction': {\n return [['flexDirection', String(decl.value)]]\n }\n case 'flex-wrap': {\n return [['flexWrap', String(decl.value)]]\n }\n case 'align-items': {\n const v = mapAlignKeyword(decl.value)\n return v === null ? [] : [['alignItems', v]]\n }\n case 'align-self': {\n const v = mapAlignKeyword(decl.value)\n return v === null ? [] : [['alignSelf', v]]\n }\n case 'align-content': {\n const v = mapAlignKeyword(decl.value)\n return v === null ? [] : [['alignContent', v]]\n }\n case 'justify-content': {\n const v = mapJustifyKeyword(decl.value)\n return v === null ? [] : [['justifyContent', v]]\n }\n case 'overflow': {\n // Lightningcss splits CSS `overflow` into `{x, y}` axes; RN only\n // supports a single `overflow` keyword (and only `'hidden' |\n // 'visible' | 'scroll'` on iOS, `'hidden' | 'visible'` on\n // Android — RN ignores unsupported keywords at runtime). Take\n // the `x` axis when the user wrote shorthand; per-axis Tailwind\n // utilities both emit shorthand here so axis splitting is rare.\n const value = decl.value as { x?: unknown; y?: unknown }\n if (typeof value.x !== 'string') return []\n return [['overflow', value.x]]\n }\n case 'overflow-x':\n case 'overflow-y': {\n // Tailwind's `overflow-x-*` / `overflow-y-*` emit these longhands,\n // not the `overflow` shorthand. RN has only a single `overflow`,\n // so collapse both axes onto it (last one declared wins via the\n // normal entry-merge order).\n return typeof decl.value === 'string' ? [['overflow', decl.value]] : []\n }\n default: {\n return null\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"layout-dispatcher.cjs","sources":["../../../../../src/core/parser/layout-dispatcher.ts"],"sourcesContent":["import type { Declaration as LcDeclaration } from 'lightningcss'\nimport type { RNEntry } from './types'\n\n/**\n * Lower a CSS `overflow` keyword to one RN's `overflow` prop accepts\n * (`'visible' | 'hidden' | 'scroll'`). `auto` → `scroll` (auto means\n * scroll-when-needed), `clip` → `hidden` (closest no-scroll clip). Anything\n * else (an unexpected keyword) drops so RN never sees an invalid value.\n * @param css CSS overflow keyword.\n * @returns RN overflow keyword, or `null` to drop.\n */\nfunction mapOverflow(css: string): string | null {\n if (css === 'visible' || css === 'hidden' || css === 'scroll') return css\n if (css === 'auto') return 'scroll'\n if (css === 'clip') return 'hidden'\n return null\n}\n\n/**\n * Build the RN `overflow` entry for a raw axis value, mapping it to an\n * RN-valid keyword and dropping unrepresentable shapes. Shared by the\n * `overflow` shorthand and the `overflow-x` / `overflow-y` longhands.\n * @param value Raw per-axis overflow value (string keyword or other).\n * @returns Single `[overflow, …]` entry, or empty when unmappable.\n */\nfunction overflowEntry(value: unknown): readonly RNEntry[] {\n const mapped = typeof value === 'string' ? mapOverflow(value) : null\n return mapped === null ? [] : [['overflow', mapped]]\n}\n\n/**\n * Lower CSS alignment keywords to the strings RN accepts. CSS uses\n * `start`/`end` while RN sticks with the legacy `flex-start`/`flex-end`.\n * Shared between `align-items`, `align-self`, `align-content`, and\n * `justify-content` — the lowering rule is identical for all four.\n * @param css CSS keyword (`center` / `start` / `end` / `baseline` / `stretch`).\n * @returns RN-compatible keyword.\n */\nfunction cssToRnAlignment(css: string): string {\n if (css === 'start') return 'flex-start'\n if (css === 'end') return 'flex-end'\n return css\n}\n\n/** Alias kept for clarity at the call site. Identical lowering rule. */\nconst cssToRnJustify = cssToRnAlignment\n\n/**\n * Map lightningcss's `align-items` / `align-self` / `align-content`\n * value (typed as `{type: 'self-position' | 'baseline-position', value: …}`)\n * to the RN keyword RN expects.\n * @param value Typed alignment value.\n * @returns RN alignment string, or `null` when unmappable.\n */\nfunction mapAlignKeyword(value: unknown): string | null {\n if (typeof value === 'string') return cssToRnAlignment(value)\n if (typeof value !== 'object' || value === null) return null\n const tagged = value as { type?: string; value?: string }\n if (tagged.type === 'baseline-position') return 'baseline'\n if (typeof tagged.value === 'string') return cssToRnAlignment(tagged.value)\n // Bare-keyword variants like `{type: 'stretch'}` carry the keyword\n // in the `type` field with no separate `value`.\n if (tagged.type === 'stretch' || tagged.type === 'normal') return cssToRnAlignment(tagged.type)\n return null\n}\n\n/**\n * Map lightningcss's `justify-content` value to the RN keyword.\n * @param value Typed justify value.\n * @returns RN justify string, or `null` when unmappable.\n */\nfunction mapJustifyKeyword(value: unknown): string | null {\n if (typeof value === 'string') return cssToRnJustify(value)\n if (typeof value !== 'object' || value === null) return null\n const tagged = value as { type?: string; value?: string }\n if (typeof tagged.value === 'string') return cssToRnJustify(tagged.value)\n return null\n}\n\n/**\n * Dispatch flexbox-layout declarations (flex-direction, flex-wrap,\n * align-items, align-self, align-content, justify-content). Returns\n * `null` for any property the dispatcher doesn't handle so the caller\n * can fall through to its main switch.\n *\n * RN expects keyword-mapped strings: `flex-start` / `flex-end` instead\n * of CSS's `start` / `end`. We do the lowering here.\n * @param decl One lightningcss declaration.\n * @returns RN entries when the property matched, else `null`.\n */\nexport function dispatchLayoutDeclaration(decl: LcDeclaration): readonly RNEntry[] | null {\n switch (decl.property) {\n case 'flex-direction': {\n return [['flexDirection', String(decl.value)]]\n }\n case 'flex-wrap': {\n return [['flexWrap', String(decl.value)]]\n }\n case 'align-items': {\n const v = mapAlignKeyword(decl.value)\n return v === null ? [] : [['alignItems', v]]\n }\n case 'align-self': {\n const v = mapAlignKeyword(decl.value)\n return v === null ? [] : [['alignSelf', v]]\n }\n case 'align-content': {\n const v = mapAlignKeyword(decl.value)\n return v === null ? [] : [['alignContent', v]]\n }\n case 'justify-content': {\n const v = mapJustifyKeyword(decl.value)\n return v === null ? [] : [['justifyContent', v]]\n }\n case 'overflow': {\n // Lightningcss splits CSS `overflow` into `{x, y}` axes; RN only\n // supports a single `overflow` keyword. Take the `x` axis when the\n // user wrote shorthand; per-axis Tailwind utilities both emit\n // shorthand here so axis splitting is rare.\n return overflowEntry((decl.value as { x?: unknown }).x)\n }\n case 'overflow-x':\n case 'overflow-y': {\n // Tailwind's `overflow-x-*` / `overflow-y-*` emit these longhands,\n // not the `overflow` shorthand. RN has only a single `overflow`,\n // so collapse both axes onto it (last one declared wins via the\n // normal entry-merge order).\n return overflowEntry(decl.value)\n }\n default: {\n return null\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -56,11 +56,27 @@ function lengthToPx(length) {
56
56
  */
57
57
  function dimensionPercentageToNumber(value) {
58
58
  if (value.type === 'dimension')
59
- return lengthToPx(value.value);
59
+ return viewportToPercent(value.value) ?? lengthToPx(value.value);
60
60
  if (value.type === 'percentage')
61
61
  return `${roundFloat(value.value * 100)}%`;
62
62
  return null;
63
63
  }
64
+ /** Viewport-relative units — can't resolve to px statically; approximate as `%`. */
65
+ const VIEWPORT_UNITS = new Set(['vw', 'vh', 'vmin', 'vmax', 'dvw', 'dvh', 'svw', 'svh', 'lvw', 'lvh']);
66
+ /**
67
+ * Map a viewport-unit length to a percentage string — `100vw`/`100vh`
68
+ * (e.g. `w-screen`/`h-screen`) become `'100%'`. Viewport units can't be
69
+ * statically resolved to px (they need live `Dimensions`), and `'N%'` is
70
+ * the closest RN-renderable approximation, far better than treating `100vw`
71
+ * as a literal `100px` box. Returns null for non-viewport units.
72
+ * @param length Typed length value.
73
+ * @returns `'N%'` string, or null when not a viewport unit.
74
+ */
75
+ function viewportToPercent(length) {
76
+ if (!VIEWPORT_UNITS.has(length.unit))
77
+ return null;
78
+ return `${roundFloat(length.value)}%`;
79
+ }
64
80
  /**
65
81
  * Convert `LengthPercentageOrAuto` (per-side value type for padding /
66
82
  * margin / inset) to an RN scalar. `auto` maps to the string `'auto'`,
@@ -1 +1 @@
1
- {"version":3,"file":"length.cjs","sources":["../../../../../src/core/parser/length.ts"],"sourcesContent":["import type {\n DimensionPercentageFor_LengthValue as LcDimensionPercentage,\n GapValue,\n LengthPercentageOrAuto,\n LengthValue,\n MaxSize,\n Size,\n} from 'lightningcss'\nimport { REM_TO_PX } from './constants'\n\n/** Alias for lightningcss's snake_case dimension-or-percentage type. */\ntype DimensionPercentage = LcDimensionPercentage\n\n/** RN-compatible length/percent result, or `null` when unrepresentable. */\ntype LengthResult = number | string | null\n\n/**\n * Round a percentage / length float to 4 decimal places so lightningcss's\n * IEEE-754 noise (`0.237 → 0.23700000345…`) doesn't leak into RN style\n * strings. 4 decimals is well below CSS subpixel precision.\n * @param n Raw float.\n * @returns Rounded float with trailing zeros trimmed.\n */\nfunction roundFloat(n: number): number {\n return Math.round(n * 10_000) / 10_000\n}\n\n/**\n * \"Fully rounded\" sentinel — emitted for Tailwind's `rounded-full` (and\n * any other utility expanding to `calc(infinity * 1px)`). RN can't\n * render `Infinity` as a style value (the StyleSheet validator silently\n * drops it), but it accepts a finite large pixel count and renders the\n * same pill / circle shape. 9999 covers every realistic phone screen.\n */\nconst FULLY_ROUNDED_PX = 9999\n\n/**\n * Convert a lightningcss `LengthValue` to a pixel number. Handles the\n * units Tailwind emits: px, rem, em. Tailwind v4's \"fully rounded\"\n * expansion (`calc(infinity * 1px)`) lands here as `value === Infinity`\n * — we clamp to a finite sentinel so RN can render it. Other non-finite\n * values (NaN from a malformed expression) are clamped to 0 rather\n * than leaking through as `null` in the serialized RN style.\n * @param length Typed length value.\n * @returns Finite pixel number.\n */\nexport function lengthToPx(length: LengthValue): number {\n const raw = length.value\n if (!Number.isFinite(raw)) return raw === Number.POSITIVE_INFINITY ? FULLY_ROUNDED_PX : 0\n switch (length.unit) {\n case 'px': {\n return raw\n }\n case 'rem':\n case 'em': {\n return raw * REM_TO_PX\n }\n default: {\n return raw\n }\n }\n}\n\n/**\n * Collapse a `DimensionPercentageFor_LengthValue` to a number (pixel) or\n * `'N%'` string. `calc()` variants are not evaluated here — they fall\n * through to `null` so the caller can skip or serialize via tokens.\n * @param value Typed dimension-or-percentage.\n * @returns Number, percent string, or `null` when unrepresentable.\n */\nexport function dimensionPercentageToNumber(value: DimensionPercentage): LengthResult {\n if (value.type === 'dimension') return lengthToPx(value.value)\n if (value.type === 'percentage') return `${roundFloat(value.value * 100)}%`\n return null\n}\n\n/**\n * Convert `LengthPercentageOrAuto` (per-side value type for padding /\n * margin / inset) to an RN scalar. `auto` maps to the string `'auto'`,\n * which RN's margin accepts for centering; non-margin callers can filter\n * it out if they need a number.\n * @param value Typed length-percentage-or-auto.\n * @returns RN scalar or `null` for unrepresentable shapes.\n */\nexport function lengthPercentageOrAutoToValue(value: LengthPercentageOrAuto): LengthResult {\n if (value.type === 'auto') return 'auto'\n return dimensionPercentageToNumber(value.value)\n}\n\n/**\n * Convert a lightningcss `Size` (used by width/height) or `MaxSize` (used\n * by max-width/max-height) to an RN scalar. Sizing keywords with no RN\n * analog (`min-content`, `fit-content`, `stretch`, …) fall through to\n * `null` so the caller drops them.\n * @param value Typed size value.\n * @returns RN scalar or `null` when unrepresentable.\n */\nexport function sizeLikeToValue(value: Size | MaxSize): LengthResult {\n if (value.type === 'auto' || value.type === 'none') return value.type === 'auto' ? 'auto' : null\n if (value.type === 'length-percentage') return dimensionPercentageToNumber(value.value)\n return null\n}\n\n/**\n * Convert a lightningcss `GapValue` (per-axis gap, used by `row-gap` /\n * `column-gap`) to an RN scalar. The `normal` keyword has no RN analog\n * and falls through to `null`.\n * @param value Typed gap value.\n * @returns Number, percent string, or `null`.\n */\nexport function gapValueToValue(value: GapValue): LengthResult {\n if (value.type === 'normal') return null\n return dimensionPercentageToNumber(value.value)\n}\n"],"names":["REM_TO_PX"],"mappings":";;;;AAgBA;;;;;;AAMG;AACH,SAAS,UAAU,CAAC,CAAS,EAAA;IAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM;AACxC;AAEA;;;;;;AAMG;AACH,MAAM,gBAAgB,GAAG,IAAI;AAE7B;;;;;;;;;AASG;AACG,SAAU,UAAU,CAAC,MAAmB,EAAA;AAC5C,IAAA,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK;AACxB,IAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;AAAE,QAAA,OAAO,GAAG,KAAK,MAAM,CAAC,iBAAiB,GAAG,gBAAgB,GAAG,CAAC;AACzF,IAAA,QAAQ,MAAM,CAAC,IAAI;QACjB,KAAK,IAAI,EAAE;AACT,YAAA,OAAO,GAAG;QACZ;AACA,QAAA,KAAK,KAAK;QACV,KAAK,IAAI,EAAE;YACT,OAAO,GAAG,GAAGA,mBAAS;QACxB;QACA,SAAS;AACP,YAAA,OAAO,GAAG;QACZ;;AAEJ;AAEA;;;;;;AAMG;AACG,SAAU,2BAA2B,CAAC,KAA0B,EAAA;AACpE,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;AAAE,QAAA,OAAO,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC;AAC9D,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,CAAA,EAAG,UAAU,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAA,CAAA,CAAG;AAC3E,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;;AAOG;AACG,SAAU,6BAA6B,CAAC,KAA6B,EAAA;AACzE,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;AAAE,QAAA,OAAO,MAAM;AACxC,IAAA,OAAO,2BAA2B,CAAC,KAAK,CAAC,KAAK,CAAC;AACjD;AAEA;;;;;;;AAOG;AACG,SAAU,eAAe,CAAC,KAAqB,EAAA;IACnD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;AAAE,QAAA,OAAO,KAAK,CAAC,IAAI,KAAK,MAAM,GAAG,MAAM,GAAG,IAAI;AAChG,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB;AAAE,QAAA,OAAO,2BAA2B,CAAC,KAAK,CAAC,KAAK,CAAC;AACvF,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;AAMG;AACG,SAAU,eAAe,CAAC,KAAe,EAAA;AAC7C,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;AAAE,QAAA,OAAO,IAAI;AACxC,IAAA,OAAO,2BAA2B,CAAC,KAAK,CAAC,KAAK,CAAC;AACjD;;;;;;;;"}
1
+ {"version":3,"file":"length.cjs","sources":["../../../../../src/core/parser/length.ts"],"sourcesContent":["import type {\n DimensionPercentageFor_LengthValue as LcDimensionPercentage,\n GapValue,\n LengthPercentageOrAuto,\n LengthValue,\n MaxSize,\n Size,\n} from 'lightningcss'\nimport { REM_TO_PX } from './constants'\n\n/** Alias for lightningcss's snake_case dimension-or-percentage type. */\ntype DimensionPercentage = LcDimensionPercentage\n\n/** RN-compatible length/percent result, or `null` when unrepresentable. */\ntype LengthResult = number | string | null\n\n/**\n * Round a percentage / length float to 4 decimal places so lightningcss's\n * IEEE-754 noise (`0.237 → 0.23700000345…`) doesn't leak into RN style\n * strings. 4 decimals is well below CSS subpixel precision.\n * @param n Raw float.\n * @returns Rounded float with trailing zeros trimmed.\n */\nfunction roundFloat(n: number): number {\n return Math.round(n * 10_000) / 10_000\n}\n\n/**\n * \"Fully rounded\" sentinel — emitted for Tailwind's `rounded-full` (and\n * any other utility expanding to `calc(infinity * 1px)`). RN can't\n * render `Infinity` as a style value (the StyleSheet validator silently\n * drops it), but it accepts a finite large pixel count and renders the\n * same pill / circle shape. 9999 covers every realistic phone screen.\n */\nconst FULLY_ROUNDED_PX = 9999\n\n/**\n * Convert a lightningcss `LengthValue` to a pixel number. Handles the\n * units Tailwind emits: px, rem, em. Tailwind v4's \"fully rounded\"\n * expansion (`calc(infinity * 1px)`) lands here as `value === Infinity`\n * — we clamp to a finite sentinel so RN can render it. Other non-finite\n * values (NaN from a malformed expression) are clamped to 0 rather\n * than leaking through as `null` in the serialized RN style.\n * @param length Typed length value.\n * @returns Finite pixel number.\n */\nexport function lengthToPx(length: LengthValue): number {\n const raw = length.value\n if (!Number.isFinite(raw)) return raw === Number.POSITIVE_INFINITY ? FULLY_ROUNDED_PX : 0\n switch (length.unit) {\n case 'px': {\n return raw\n }\n case 'rem':\n case 'em': {\n return raw * REM_TO_PX\n }\n default: {\n return raw\n }\n }\n}\n\n/**\n * Collapse a `DimensionPercentageFor_LengthValue` to a number (pixel) or\n * `'N%'` string. `calc()` variants are not evaluated here — they fall\n * through to `null` so the caller can skip or serialize via tokens.\n * @param value Typed dimension-or-percentage.\n * @returns Number, percent string, or `null` when unrepresentable.\n */\nexport function dimensionPercentageToNumber(value: DimensionPercentage): LengthResult {\n if (value.type === 'dimension') return viewportToPercent(value.value) ?? lengthToPx(value.value)\n if (value.type === 'percentage') return `${roundFloat(value.value * 100)}%`\n return null\n}\n\n/** Viewport-relative units — can't resolve to px statically; approximate as `%`. */\nconst VIEWPORT_UNITS: ReadonlySet<string> = new Set(['vw', 'vh', 'vmin', 'vmax', 'dvw', 'dvh', 'svw', 'svh', 'lvw', 'lvh'])\n\n/**\n * Map a viewport-unit length to a percentage string — `100vw`/`100vh`\n * (e.g. `w-screen`/`h-screen`) become `'100%'`. Viewport units can't be\n * statically resolved to px (they need live `Dimensions`), and `'N%'` is\n * the closest RN-renderable approximation, far better than treating `100vw`\n * as a literal `100px` box. Returns null for non-viewport units.\n * @param length Typed length value.\n * @returns `'N%'` string, or null when not a viewport unit.\n */\nfunction viewportToPercent(length: LengthValue): string | null {\n if (!VIEWPORT_UNITS.has(length.unit)) return null\n return `${roundFloat(length.value)}%`\n}\n\n/**\n * Convert `LengthPercentageOrAuto` (per-side value type for padding /\n * margin / inset) to an RN scalar. `auto` maps to the string `'auto'`,\n * which RN's margin accepts for centering; non-margin callers can filter\n * it out if they need a number.\n * @param value Typed length-percentage-or-auto.\n * @returns RN scalar or `null` for unrepresentable shapes.\n */\nexport function lengthPercentageOrAutoToValue(value: LengthPercentageOrAuto): LengthResult {\n if (value.type === 'auto') return 'auto'\n return dimensionPercentageToNumber(value.value)\n}\n\n/**\n * Convert a lightningcss `Size` (used by width/height) or `MaxSize` (used\n * by max-width/max-height) to an RN scalar. Sizing keywords with no RN\n * analog (`min-content`, `fit-content`, `stretch`, …) fall through to\n * `null` so the caller drops them.\n * @param value Typed size value.\n * @returns RN scalar or `null` when unrepresentable.\n */\nexport function sizeLikeToValue(value: Size | MaxSize): LengthResult {\n if (value.type === 'auto' || value.type === 'none') return value.type === 'auto' ? 'auto' : null\n if (value.type === 'length-percentage') return dimensionPercentageToNumber(value.value)\n return null\n}\n\n/**\n * Convert a lightningcss `GapValue` (per-axis gap, used by `row-gap` /\n * `column-gap`) to an RN scalar. The `normal` keyword has no RN analog\n * and falls through to `null`.\n * @param value Typed gap value.\n * @returns Number, percent string, or `null`.\n */\nexport function gapValueToValue(value: GapValue): LengthResult {\n if (value.type === 'normal') return null\n return dimensionPercentageToNumber(value.value)\n}\n"],"names":["REM_TO_PX"],"mappings":";;;;AAgBA;;;;;;AAMG;AACH,SAAS,UAAU,CAAC,CAAS,EAAA;IAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM;AACxC;AAEA;;;;;;AAMG;AACH,MAAM,gBAAgB,GAAG,IAAI;AAE7B;;;;;;;;;AASG;AACG,SAAU,UAAU,CAAC,MAAmB,EAAA;AAC5C,IAAA,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK;AACxB,IAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;AAAE,QAAA,OAAO,GAAG,KAAK,MAAM,CAAC,iBAAiB,GAAG,gBAAgB,GAAG,CAAC;AACzF,IAAA,QAAQ,MAAM,CAAC,IAAI;QACjB,KAAK,IAAI,EAAE;AACT,YAAA,OAAO,GAAG;QACZ;AACA,QAAA,KAAK,KAAK;QACV,KAAK,IAAI,EAAE;YACT,OAAO,GAAG,GAAGA,mBAAS;QACxB;QACA,SAAS;AACP,YAAA,OAAO,GAAG;QACZ;;AAEJ;AAEA;;;;;;AAMG;AACG,SAAU,2BAA2B,CAAC,KAA0B,EAAA;AACpE,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;AAAE,QAAA,OAAO,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC;AAChG,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,CAAA,EAAG,UAAU,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAA,CAAA,CAAG;AAC3E,IAAA,OAAO,IAAI;AACb;AAEA;AACA,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAE3H;;;;;;;;AAQG;AACH,SAAS,iBAAiB,CAAC,MAAmB,EAAA;IAC5C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;AAAE,QAAA,OAAO,IAAI;IACjD,OAAO,CAAA,EAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG;AACvC;AAEA;;;;;;;AAOG;AACG,SAAU,6BAA6B,CAAC,KAA6B,EAAA;AACzE,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;AAAE,QAAA,OAAO,MAAM;AACxC,IAAA,OAAO,2BAA2B,CAAC,KAAK,CAAC,KAAK,CAAC;AACjD;AAEA;;;;;;;AAOG;AACG,SAAU,eAAe,CAAC,KAAqB,EAAA;IACnD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;AAAE,QAAA,OAAO,KAAK,CAAC,IAAI,KAAK,MAAM,GAAG,MAAM,GAAG,IAAI;AAChG,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB;AAAE,QAAA,OAAO,2BAA2B,CAAC,KAAK,CAAC,KAAK,CAAC;AACvF,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;AAMG;AACG,SAAU,eAAe,CAAC,KAAe,EAAA;AAC7C,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;AAAE,QAAA,OAAO,IAAI;AACxC,IAAA,OAAO,2BAA2B,CAAC,KAAK,CAAC,KAAK,CAAC;AACjD;;;;;;;;"}
@@ -42,11 +42,16 @@ function detectSafeAreaMarker(tokens, themeVars) {
42
42
  const nonWs = stripWhitespace(tokens);
43
43
  if (nonWs.length === 0)
44
44
  return null;
45
- // Shape 1: pure env(safe-area-inset-*)
45
+ // Shape 1: pure env(safe-area-inset-*), optionally with a CSS fallback —
46
+ // `env(safe-area-inset-top, 12px)`. The fallback is the author's intended
47
+ // floor when the inset is unavailable, so capture it as `or` (max(inset, N))
48
+ // instead of silently dropping it and collapsing to 0.
46
49
  if (nonWs.length === 1) {
47
50
  const side = envSide(nonWs[0]);
48
- if (side !== null)
49
- return { __safe: side };
51
+ if (side !== null) {
52
+ const or = envFallbackPx(nonWs[0], themeVars);
53
+ return or === null ? { __safe: side } : { __safe: side, or };
54
+ }
50
55
  }
51
56
  // Shape 2 / 3: max(env(...), n) or calc(env(...) + n)
52
57
  if (nonWs.length === 1 && isFunction(nonWs[0])) {
@@ -185,6 +190,22 @@ function envSide(token) {
185
190
  return null;
186
191
  return SIDE_TAG[name.value] ?? null;
187
192
  }
193
+ /**
194
+ * Read an `env(side, <fallback>)` token's CSS fallback as a pixel floor.
195
+ * Returns null when there's no fallback or it isn't a plain length — the
196
+ * caller then emits the bare marker.
197
+ * @param token One TokenOrValue (expected to be an `env` token).
198
+ * @param themeVars Optional theme-vars table for resolving `var()` in the fallback.
199
+ * @returns Fallback length in px, or null.
200
+ */
201
+ function envFallbackPx(token, themeVars) {
202
+ if (token.type !== 'env')
203
+ return null;
204
+ const { fallback } = token.value;
205
+ if (!fallback || fallback.length === 0)
206
+ return null;
207
+ return coerceLengthPx(stripWhitespace(fallback), themeVars);
208
+ }
188
209
  /**
189
210
  * Drop whitespace / comment tokens from a list so downstream branches
190
211
  * can pattern-match by index without counting spaces.
@@ -1 +1 @@
1
- {"version":3,"file":"safe-area.cjs","sources":["../../../../../src/core/parser/safe-area.ts"],"sourcesContent":["/**\n * Detect `env(safe-area-inset-*)` usage in a lightningcss TokenOrValue\n * tree and convert the containing declaration into a runtime-resolved\n * {@link SafeAreaMarker}.\n *\n * Three shapes the NativeWind-compatible `*-safe` utilities produce\n * through Tailwind v4's compiler:\n * - Pure `env(safe-area-inset-top)` → `{ __safe: 't' }`\n * - `max(env(safe-area-inset-top), 16px)` → `{ __safe: 't', or: 16 }`\n * - `calc(env(safe-area-inset-top) + 16px)` → `{ __safe: 't', offset: 16 }`\n *\n * The `h-screen-safe` / `min-h-screen-safe` / `max-h-screen-safe` shape\n * — `calc(100vh - (env(top) + env(bottom)))` — reports as\n * `{ __safe: 'screen-minus-y' }`; the runtime resolves against\n * `Dimensions.get('window').height` minus the current top + bottom\n * insets.\n *\n * Unrecognised compound shapes (e.g. `env(safe-area-inset-top, 12px)`\n * with a fallback) return `null` so the caller falls back to the\n * regular token-serialisation path.\n */\n\nimport type { TokenOrValue } from 'lightningcss'\nimport type { SafeAreaMarker } from './types'\n\n/** Map UA env names to the compact side tag used in the marker. */\nconst SIDE_TAG: Record<string, SafeAreaMarker['__safe']> = {\n 'safe-area-inset-top': 't',\n 'safe-area-inset-right': 'r',\n 'safe-area-inset-bottom': 'b',\n 'safe-area-inset-left': 'l',\n}\n\n/** Rem → px factor used across the parser (matches `tokens.ts`). */\nconst REM_TO_PX = 16\n\n/**\n * Theme-var lookup table we optionally receive. Values are stored as\n * raw CSS value strings (`'0.25rem'`, `'#fff'`, …); the safe-area\n * detector only needs the ones that resolve to lengths.\n */\ntype ThemeVars = ReadonlyMap<string, string>\n\n/**\n * Try to compile a token list into a {@link SafeAreaMarker}. Returns\n * `null` when the tokens don't match any recognised safe-area pattern\n * — the caller should fall back to the regular unparsed-value path.\n * @param tokens Declaration value token list from lightningcss.\n * @param themeVars\n * @returns Marker object, or `null`.\n */\nfunction detectSafeAreaMarker(tokens: readonly TokenOrValue[], themeVars?: ThemeVars): SafeAreaMarker | null {\n const nonWs = stripWhitespace(tokens)\n if (nonWs.length === 0) return null\n\n // Shape 1: pure env(safe-area-inset-*)\n if (nonWs.length === 1) {\n const side = envSide(nonWs[0]!)\n if (side !== null) return { __safe: side }\n }\n\n // Shape 2 / 3: max(env(...), n) or calc(env(...) + n)\n if (nonWs.length === 1 && isFunction(nonWs[0]!)) {\n const inner = functionInner(nonWs[0]!)\n if (inner) {\n const marker = tryFromFunction(inner.name, inner.args, themeVars)\n if (marker) return marker\n }\n }\n\n return null\n}\n\n/**\n * Short-circuit whether this token is a `function` token.\n * @param token\n */\nfunction isFunction(token: TokenOrValue): boolean {\n return token.type === 'function'\n}\n\n/**\n * Pull `(name, args-without-whitespace)` out of a function token.\n * @param token A function-type TokenOrValue.\n * @returns Inner record, or null when the shape is unexpected.\n */\nfunction functionInner(token: TokenOrValue): { name: string; args: readonly TokenOrValue[] } | null {\n if (token.type !== 'function') return null\n return { name: token.value.name, args: token.value.arguments }\n}\n\n/**\n * Pattern-match `max(env(...), n)` / `calc(env(...) + n)` / nested\n * screen-minus-y calc. Operates on the function's already-whitespace-\n * trimmed arg list so every branch can index by position.\n * @param name Function name (`max` / `calc`).\n * @param args Raw argument tokens (whitespace still present).\n * @param themeVars\n * @returns Marker, or null when not recognised.\n */\nfunction tryFromFunction(name: string, args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {\n if (name === 'max') return tryMax(args, themeVars)\n if (name === 'calc') return tryCalc(args, themeVars)\n return null\n}\n\n/**\n * `max(env(safe-area-inset-*), <length>)` — Tailwind v4's `*-safe-or-n`\n * shape. Optionally `max(env(...), calc(var(--spacing) * n))` — the\n * calc inside has already been resolved to a bare length by Tailwind's\n * compiler step.\n * @param args Whitespace-preserving argument tokens.\n * @param themeVars\n * @returns Marker, or null.\n */\nfunction tryMax(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {\n const parts = splitTopLevelComma(args)\n if (parts.length !== 2) return null\n const firstToken = onlyNonWhitespace(parts[0]!)\n if (!firstToken) return null\n const firstSide = envSide(firstToken)\n if (firstSide === null) return null\n const rhs = coerceLengthPx(parts[1]!, themeVars)\n if (rhs === null) return null\n return { __safe: firstSide, or: rhs }\n}\n\n/**\n * Recognise:\n * - `calc(env(safe-area-inset-*) + n)` — `*-safe-offset-n`\n * - `calc(100vh - (env(...-top) + env(...-bottom)))` — `h-screen-safe`\n * @param args Whitespace-preserving calc argument tokens.\n * @param themeVars\n * @returns Marker, or null.\n */\nfunction tryCalc(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {\n const [signIndex, sign] = findTopLevelPlusOrMinus(args)\n if (signIndex === -1) return reduceNestedCalc(args, themeVars)\n const lhs = stripWhitespace(args.slice(0, signIndex))\n const rhs = stripWhitespace(args.slice(signIndex + 1))\n const offset = matchOffset(lhs, rhs, sign, themeVars)\n if (offset) return offset\n if (sign === '-' && isViewportHeightHundred(lhs) && isParenthesisedTopBottomSum(rhs)) return { __safe: 'screen-minus-y' }\n return null\n}\n\n/**\n * Fall-through arm of {@link tryCalc}: when there's no +/− at the calc\n * body's top level, try to interpret the whole body as a nested\n * function (e.g. a bare `calc(max(...))`).\n * @param args Calc arguments.\n * @param themeVars Optional theme-vars table for length resolution.\n * @returns Marker or null.\n */\nfunction reduceNestedCalc(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {\n const inner = onlyNonWhitespace(args)\n if (!inner) return null\n return detectSafeAreaMarker([inner], themeVars)\n}\n\n/**\n * Try to match `calc(env(side) ± amount)` against the two-side split of\n * the calc body. Returns a marker with positive / negative `offset`\n * based on the sign, or null when the shape doesn't fit.\n * @param lhs Left-hand tokens (whitespace already stripped).\n * @param rhs Right-hand tokens (whitespace already stripped).\n * @param sign The `+` or `-` delim captured between them.\n * @param themeVars Optional theme-vars table.\n * @returns Offset marker, or null.\n */\nfunction matchOffset(\n lhs: readonly TokenOrValue[],\n rhs: readonly TokenOrValue[],\n sign: '+' | '-' | null,\n themeVars: ThemeVars | undefined,\n): SafeAreaMarker | null {\n if (!sign || lhs.length !== 1 || rhs.length !== 1) return null\n const side = envSide(lhs[0]!)\n if (side === null) return null\n const amount = coerceLengthPx([rhs[0]!], themeVars)\n if (amount === null) return null\n return { __safe: side, offset: sign === '+' ? amount : -amount }\n}\n\n/**\n * Check whether a token is the env() that names one of the four safe-area sides.\n * @param token One TokenOrValue.\n * @returns The compact side tag, or `null` when the token isn't a safe-area env reference.\n */\nfunction envSide(token: TokenOrValue): SafeAreaMarker['__safe'] | null {\n if (token.type !== 'env') return null\n const { name } = token.value\n if (name.type !== 'ua') return null\n return SIDE_TAG[name.value] ?? null\n}\n\n/**\n * Drop whitespace / comment tokens from a list so downstream branches\n * can pattern-match by index without counting spaces.\n * @param tokens Raw token list.\n * @returns Copy with whitespace tokens removed.\n */\nfunction stripWhitespace(tokens: readonly TokenOrValue[]): readonly TokenOrValue[] {\n const out: TokenOrValue[] = []\n for (const token of tokens) {\n if (isWhitespaceToken(token)) continue\n out.push(token)\n }\n return out\n}\n\n/**\n * Like {@link stripWhitespace} but returns the single non-whitespace\n * token (or null when there's zero or more than one).\n * @param tokens Raw token list.\n * @returns The single meaningful token, or null.\n */\nfunction onlyNonWhitespace(tokens: readonly TokenOrValue[]): TokenOrValue | null {\n const stripped = stripWhitespace(tokens)\n return stripped.length === 1 ? stripped[0]! : null\n}\n\n/**\n * Whether the token is pure whitespace / comment — ignorable when\n * matching by position.\n * @param token One TokenOrValue.\n * @returns True when the token carries no semantic value.\n */\nfunction isWhitespaceToken(token: TokenOrValue): boolean {\n if (token.type !== 'token') return false\n const { value } = token\n return value.type === 'white-space' || value.type === 'comment'\n}\n\n/**\n * Split `a, b` top-level argument lists into their slices. Respects\n * nested function/paren groups so commas inside an inner `calc(a, b)`\n * don't cause an outer split.\n * @param args Raw argument token list.\n * @returns Array of slices (one per top-level comma-separated segment).\n */\nfunction splitTopLevelComma(args: readonly TokenOrValue[]): readonly TokenOrValue[][] {\n // Functions already arrive as atomic `{type:'function', value}` nodes,\n // so commas inside them are never in `args` — a flat scan for the\n // raw `comma` token is enough.\n const parts: TokenOrValue[][] = [[]]\n for (const token of args) {\n if (token.type === 'token' && token.value.type === 'comma') {\n parts.push([])\n continue\n }\n parts.at(-1)!.push(token)\n }\n return parts\n}\n\n/**\n * Find the first top-level `+` / `-` delim in a calc body.\n * @param args Raw argument token list.\n * @returns Tuple of `[index, sign]` or `[-1, null]` when not found.\n */\nfunction findTopLevelPlusOrMinus(args: readonly TokenOrValue[]): [number, '+' | '-' | null] {\n for (const [index, argument] of args.entries()) {\n const token = argument!\n if (token.type !== 'token') continue\n if (token.value.type !== 'delim') continue\n const delim = token.value.value as string\n if (delim === '+' || delim === '-') return [index, delim]\n }\n return [-1, null]\n}\n\n/**\n * Coerce a token list that should represent a CSS length (px / rem /\n * integer-number with no unit) into a px value.\n * @param tokens Tokens for the length fragment.\n * @param themeVars\n * @returns Px value, or null when the shape is unrecognised.\n */\nfunction coerceLengthPx(tokens: readonly TokenOrValue[], themeVars: ThemeVars | undefined): number | null {\n const stripped = stripWhitespace(tokens)\n if (stripped.length !== 1) return null\n return coerceTokenToPx(stripped[0]!, themeVars)\n}\n\n/**\n * Convert one token to a px number, handling the lengths Tailwind v4\n * commonly emits after compile: `length` (px/rem), bare numbers,\n * `calc(0.25rem * 4)` nested functions.\n * @param token One TokenOrValue.\n * @param themeVars\n * @returns Px value, or null.\n */\nfunction coerceTokenToPx(token: TokenOrValue, themeVars: ThemeVars | undefined): number | null {\n if (token.type === 'length') {\n const { unit, value } = token.value\n if (unit === 'px') return value\n if (unit === 'rem' || unit === 'em') return value * REM_TO_PX\n return null\n }\n if (token.type === 'token' && token.value.type === 'number') {\n return token.value.value\n }\n if (token.type === 'function' && token.value.name === 'calc') {\n return evaluateSimpleCalc(token.value.arguments, themeVars)\n }\n if (token.type === 'var') return resolveVariableToPx(token.value.name.ident, themeVars)\n return null\n}\n\n/**\n * Evaluate a trivial `calc(A * B)` where A / B are lengths or numbers\n * Tailwind compiles down to — the exact shape `var(--spacing)` resolves\n * to after `theme(inline)` substitution.\n * @param args Raw calc argument token list.\n * @param themeVars\n * @returns Px value, or null.\n */\nfunction evaluateSimpleCalc(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): number | null {\n const stripped = stripWhitespace(args)\n if (stripped.length === 3) {\n const mul = stripped[1]!\n if (mul.type === 'token' && mul.value.type === 'delim' && (mul.value.value as string) === '*') {\n const left = coerceTokenToPx(stripped[0]!, themeVars)\n const right = coerceTokenToPx(stripped[2]!, themeVars)\n if (left !== null && right !== null) return left * right\n }\n }\n if (stripped.length === 1) return coerceTokenToPx(stripped[0]!, themeVars)\n return null\n}\n\n/**\n * Resolve a `var(--name)` reference to a px value using the supplied\n * theme-var table. Returns null when the name isn't registered or the\n * value isn't a recognisable length.\n * @param name Custom-property name (with leading `--`).\n * @param themeVars Lookup table (or undefined).\n * @returns Px value, or null.\n */\nfunction resolveVariableToPx(name: string, themeVars: ThemeVars | undefined): number | null {\n if (!themeVars) return null\n const raw = themeVars.get(name)\n if (raw === undefined) return null\n const trimmed = raw.trim()\n if (trimmed.endsWith('rem')) {\n const n = Number(trimmed.slice(0, -3))\n return Number.isFinite(n) ? n * REM_TO_PX : null\n }\n if (trimmed.endsWith('em')) {\n const n = Number(trimmed.slice(0, -2))\n return Number.isFinite(n) ? n * REM_TO_PX : null\n }\n if (trimmed.endsWith('px')) {\n const n = Number(trimmed.slice(0, -2))\n return Number.isFinite(n) ? n : null\n }\n const n = Number(trimmed)\n return Number.isFinite(n) ? n : null\n}\n\n/**\n * Whether the token list represents `100vh`.\n * @param tokens Whitespace-stripped token list.\n * @returns True when the only meaningful token is the length `100vh`.\n */\nfunction isViewportHeightHundred(tokens: readonly TokenOrValue[]): boolean {\n if (tokens.length !== 1) return false\n const token = tokens[0]!\n if (token.type !== 'length') return false\n return token.value.unit === 'vh' && token.value.value === 100\n}\n\n/**\n * Whether the rhs of `calc(100vh - <rhs>)` is the nested `(env(top) + env(bottom))`\n * parenthesised sum Tailwind emits for `h-screen-safe` and siblings.\n * @param tokens Whitespace-stripped rhs token list.\n * @returns True on match.\n */\nfunction isParenthesisedTopBottomSum(tokens: readonly TokenOrValue[]): boolean {\n // lightningcss emits bare `(...)` as a `parenthesis-block` token\n // followed by the inner tokens inline, then a `close-parenthesis`\n // token at the matching depth. Strip whitespace + the grouping tokens\n // and check that what remains is exactly `env(top) + env(bottom)`\n // (either order).\n const meaningful: TokenOrValue[] = []\n for (const token of tokens) {\n if (isWhitespaceToken(token)) continue\n if (token.type === 'token') {\n const t = token.value.type\n if (t === 'parenthesis-block' || t === 'close-parenthesis') continue\n }\n meaningful.push(token)\n }\n if (meaningful.length !== 3) return false\n const plus = meaningful[1]!\n if (plus.type !== 'token' || plus.value.type !== 'delim' || (plus.value.value as string) !== '+') return false\n const first = envSide(meaningful[0]!)\n const last = envSide(meaningful[2]!)\n return (first === 't' && last === 'b') || (first === 'b' && last === 't')\n}\n\nexport { detectSafeAreaMarker }\n\n"],"names":[],"mappings":";;AAAA;;;;;;;;;;;;;;;;;;;;AAoBG;AAKH;AACA,MAAM,QAAQ,GAA6C;AACzD,IAAA,qBAAqB,EAAE,GAAG;AAC1B,IAAA,uBAAuB,EAAE,GAAG;AAC5B,IAAA,wBAAwB,EAAE,GAAG;AAC7B,IAAA,sBAAsB,EAAE,GAAG;CAC5B;AAED;AACA,MAAM,SAAS,GAAG,EAAE;AASpB;;;;;;;AAOG;AACH,SAAS,oBAAoB,CAAC,MAA+B,EAAE,SAAqB,EAAA;AAClF,IAAA,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC;AACrC,IAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;;AAGnC,IAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;QACtB,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAC/B,IAAI,IAAI,KAAK,IAAI;AAAE,YAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;IAC5C;;AAGA,IAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE;QAC/C,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QACtC,IAAI,KAAK,EAAE;AACT,YAAA,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC;AACjE,YAAA,IAAI,MAAM;AAAE,gBAAA,OAAO,MAAM;QAC3B;IACF;AAEA,IAAA,OAAO,IAAI;AACb;AAEA;;;AAGG;AACH,SAAS,UAAU,CAAC,KAAmB,EAAA;AACrC,IAAA,OAAO,KAAK,CAAC,IAAI,KAAK,UAAU;AAClC;AAEA;;;;AAIG;AACH,SAAS,aAAa,CAAC,KAAmB,EAAA;AACxC,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;AAAE,QAAA,OAAO,IAAI;AAC1C,IAAA,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE;AAChE;AAEA;;;;;;;;AAQG;AACH,SAAS,eAAe,CAAC,IAAY,EAAE,IAA6B,EAAE,SAAgC,EAAA;IACpG,IAAI,IAAI,KAAK,KAAK;AAAE,QAAA,OAAO,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC;IAClD,IAAI,IAAI,KAAK,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC;AACpD,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;;;AAQG;AACH,SAAS,MAAM,CAAC,IAA6B,EAAE,SAAgC,EAAA;AAC7E,IAAA,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC;AACtC,IAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;IACnC,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;AAC/C,IAAA,IAAI,CAAC,UAAU;AAAE,QAAA,OAAO,IAAI;AAC5B,IAAA,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IACrC,IAAI,SAAS,KAAK,IAAI;AAAE,QAAA,OAAO,IAAI;IACnC,MAAM,GAAG,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,SAAS,CAAC;IAChD,IAAI,GAAG,KAAK,IAAI;AAAE,QAAA,OAAO,IAAI;IAC7B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,EAAE;AACvC;AAEA;;;;;;;AAOG;AACH,SAAS,OAAO,CAAC,IAA6B,EAAE,SAAgC,EAAA;IAC9E,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,uBAAuB,CAAC,IAAI,CAAC;IACvD,IAAI,SAAS,KAAK,EAAE;AAAE,QAAA,OAAO,gBAAgB,CAAC,IAAI,EAAE,SAAS,CAAC;AAC9D,IAAA,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACrD,IAAA,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;AACtD,IAAA,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC;AACrD,IAAA,IAAI,MAAM;AAAE,QAAA,OAAO,MAAM;AACzB,IAAA,IAAI,IAAI,KAAK,GAAG,IAAI,uBAAuB,CAAC,GAAG,CAAC,IAAI,2BAA2B,CAAC,GAAG,CAAC;AAAE,QAAA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE;AACzH,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;;AAOG;AACH,SAAS,gBAAgB,CAAC,IAA6B,EAAE,SAAgC,EAAA;AACvF,IAAA,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC;AACrC,IAAA,IAAI,CAAC,KAAK;AAAE,QAAA,OAAO,IAAI;IACvB,OAAO,oBAAoB,CAAC,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;AACjD;AAEA;;;;;;;;;AASG;AACH,SAAS,WAAW,CAClB,GAA4B,EAC5B,GAA4B,EAC5B,IAAsB,EACtB,SAAgC,EAAA;AAEhC,IAAA,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;IAC9D,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;IAC7B,IAAI,IAAI,KAAK,IAAI;AAAE,QAAA,OAAO,IAAI;AAC9B,IAAA,MAAM,MAAM,GAAG,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,EAAE,SAAS,CAAC;IACnD,IAAI,MAAM,KAAK,IAAI;AAAE,QAAA,OAAO,IAAI;IAChC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,KAAK,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE;AAClE;AAEA;;;;AAIG;AACH,SAAS,OAAO,CAAC,KAAmB,EAAA;AAClC,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK;AAAE,QAAA,OAAO,IAAI;AACrC,IAAA,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,KAAK;AAC5B,IAAA,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;AAAE,QAAA,OAAO,IAAI;IACnC,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI;AACrC;AAEA;;;;;AAKG;AACH,SAAS,eAAe,CAAC,MAA+B,EAAA;IACtD,MAAM,GAAG,GAAmB,EAAE;AAC9B,IAAA,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;QAC1B,IAAI,iBAAiB,CAAC,KAAK,CAAC;YAAE;AAC9B,QAAA,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;IACjB;AACA,IAAA,OAAO,GAAG;AACZ;AAEA;;;;;AAKG;AACH,SAAS,iBAAiB,CAAC,MAA+B,EAAA;AACxD,IAAA,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC;AACxC,IAAA,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAE,GAAG,IAAI;AACpD;AAEA;;;;;AAKG;AACH,SAAS,iBAAiB,CAAC,KAAmB,EAAA;AAC5C,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;AAAE,QAAA,OAAO,KAAK;AACxC,IAAA,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK;IACvB,OAAO,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;AACjE;AAEA;;;;;;AAMG;AACH,SAAS,kBAAkB,CAAC,IAA6B,EAAA;;;;AAIvD,IAAA,MAAM,KAAK,GAAqB,CAAC,EAAE,CAAC;AACpC,IAAA,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE;AACxB,QAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE;AAC1D,YAAA,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACd;QACF;QACA,KAAK,CAAC,EAAE,CAAC,EAAE,CAAE,CAAC,IAAI,CAAC,KAAK,CAAC;IAC3B;AACA,IAAA,OAAO,KAAK;AACd;AAEA;;;;AAIG;AACH,SAAS,uBAAuB,CAAC,IAA6B,EAAA;AAC5D,IAAA,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;QAC9C,MAAM,KAAK,GAAG,QAAS;AACvB,QAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;YAAE;AAC5B,QAAA,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO;YAAE;AAClC,QAAA,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAe;AACzC,QAAA,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG;AAAE,YAAA,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;IAC3D;AACA,IAAA,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC;AACnB;AAEA;;;;;;AAMG;AACH,SAAS,cAAc,CAAC,MAA+B,EAAE,SAAgC,EAAA;AACvF,IAAA,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC;AACxC,IAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;IACtC,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAE,EAAE,SAAS,CAAC;AACjD;AAEA;;;;;;;AAOG;AACH,SAAS,eAAe,CAAC,KAAmB,EAAE,SAAgC,EAAA;AAC5E,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE;QAC3B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK;QACnC,IAAI,IAAI,KAAK,IAAI;AAAE,YAAA,OAAO,KAAK;AAC/B,QAAA,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,KAAK,GAAG,SAAS;AAC7D,QAAA,OAAO,IAAI;IACb;AACA,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE;AAC3D,QAAA,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK;IAC1B;AACA,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE;QAC5D,OAAO,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC;IAC7D;AACA,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK;AAAE,QAAA,OAAO,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC;AACvF,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;;AAOG;AACH,SAAS,kBAAkB,CAAC,IAA6B,EAAE,SAAgC,EAAA;AACzF,IAAA,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC;AACtC,IAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;AACzB,QAAA,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAE;QACxB,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,IAAK,GAAG,CAAC,KAAK,CAAC,KAAgB,KAAK,GAAG,EAAE;YAC7F,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAE,EAAE,SAAS,CAAC;YACrD,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAE,EAAE,SAAS,CAAC;AACtD,YAAA,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO,IAAI,GAAG,KAAK;QAC1D;IACF;AACA,IAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAE,EAAE,SAAS,CAAC;AAC1E,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;;AAOG;AACH,SAAS,mBAAmB,CAAC,IAAY,EAAE,SAAgC,EAAA;AACzE,IAAA,IAAI,CAAC,SAAS;AAAE,QAAA,OAAO,IAAI;IAC3B,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;IAC/B,IAAI,GAAG,KAAK,SAAS;AAAE,QAAA,OAAO,IAAI;AAClC,IAAA,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE;AAC1B,IAAA,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;AAC3B,QAAA,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtC,QAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,GAAG,IAAI;IAClD;AACA,IAAA,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAC1B,QAAA,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtC,QAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,GAAG,IAAI;IAClD;AACA,IAAA,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAC1B,QAAA,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtC,QAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI;IACtC;AACA,IAAA,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC;AACzB,IAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI;AACtC;AAEA;;;;AAIG;AACH,SAAS,uBAAuB,CAAC,MAA+B,EAAA;AAC9D,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,KAAK;AACrC,IAAA,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE;AACxB,IAAA,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;AAAE,QAAA,OAAO,KAAK;AACzC,IAAA,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,GAAG;AAC/D;AAEA;;;;;AAKG;AACH,SAAS,2BAA2B,CAAC,MAA+B,EAAA;;;;;;IAMlE,MAAM,UAAU,GAAmB,EAAE;AACrC,IAAA,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;QAC1B,IAAI,iBAAiB,CAAC,KAAK,CAAC;YAAE;AAC9B,QAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE;AAC1B,YAAA,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI;AAC1B,YAAA,IAAI,CAAC,KAAK,mBAAmB,IAAI,CAAC,KAAK,mBAAmB;gBAAE;QAC9D;AACA,QAAA,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;IACxB;AACA,IAAA,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,KAAK;AACzC,IAAA,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAE;IAC3B,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,IAAK,IAAI,CAAC,KAAK,CAAC,KAAgB,KAAK,GAAG;AAAE,QAAA,OAAO,KAAK;IAC9G,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC;IACrC,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC;AACpC,IAAA,OAAO,CAAC,KAAK,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,MAAM,KAAK,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC;AAC3E;;;;"}
1
+ {"version":3,"file":"safe-area.cjs","sources":["../../../../../src/core/parser/safe-area.ts"],"sourcesContent":["/**\n * Detect `env(safe-area-inset-*)` usage in a lightningcss TokenOrValue\n * tree and convert the containing declaration into a runtime-resolved\n * {@link SafeAreaMarker}.\n *\n * Three shapes the NativeWind-compatible `*-safe` utilities produce\n * through Tailwind v4's compiler:\n * - Pure `env(safe-area-inset-top)` → `{ __safe: 't' }`\n * - `max(env(safe-area-inset-top), 16px)` → `{ __safe: 't', or: 16 }`\n * - `calc(env(safe-area-inset-top) + 16px)` → `{ __safe: 't', offset: 16 }`\n *\n * The `h-screen-safe` / `min-h-screen-safe` / `max-h-screen-safe` shape\n * — `calc(100vh - (env(top) + env(bottom)))` — reports as\n * `{ __safe: 'screen-minus-y' }`; the runtime resolves against\n * `Dimensions.get('window').height` minus the current top + bottom\n * insets.\n *\n * Unrecognised compound shapes (e.g. `env(safe-area-inset-top, 12px)`\n * with a fallback) return `null` so the caller falls back to the\n * regular token-serialisation path.\n */\n\nimport type { TokenOrValue } from 'lightningcss'\nimport type { SafeAreaMarker } from './types'\n\n/** Map UA env names to the compact side tag used in the marker. */\nconst SIDE_TAG: Record<string, SafeAreaMarker['__safe']> = {\n 'safe-area-inset-top': 't',\n 'safe-area-inset-right': 'r',\n 'safe-area-inset-bottom': 'b',\n 'safe-area-inset-left': 'l',\n}\n\n/** Rem → px factor used across the parser (matches `tokens.ts`). */\nconst REM_TO_PX = 16\n\n/**\n * Theme-var lookup table we optionally receive. Values are stored as\n * raw CSS value strings (`'0.25rem'`, `'#fff'`, …); the safe-area\n * detector only needs the ones that resolve to lengths.\n */\ntype ThemeVars = ReadonlyMap<string, string>\n\n/**\n * Try to compile a token list into a {@link SafeAreaMarker}. Returns\n * `null` when the tokens don't match any recognised safe-area pattern\n * — the caller should fall back to the regular unparsed-value path.\n * @param tokens Declaration value token list from lightningcss.\n * @param themeVars\n * @returns Marker object, or `null`.\n */\nfunction detectSafeAreaMarker(tokens: readonly TokenOrValue[], themeVars?: ThemeVars): SafeAreaMarker | null {\n const nonWs = stripWhitespace(tokens)\n if (nonWs.length === 0) return null\n\n // Shape 1: pure env(safe-area-inset-*), optionally with a CSS fallback —\n // `env(safe-area-inset-top, 12px)`. The fallback is the author's intended\n // floor when the inset is unavailable, so capture it as `or` (max(inset, N))\n // instead of silently dropping it and collapsing to 0.\n if (nonWs.length === 1) {\n const side = envSide(nonWs[0]!)\n if (side !== null) {\n const or = envFallbackPx(nonWs[0]!, themeVars)\n return or === null ? { __safe: side } : { __safe: side, or }\n }\n }\n\n // Shape 2 / 3: max(env(...), n) or calc(env(...) + n)\n if (nonWs.length === 1 && isFunction(nonWs[0]!)) {\n const inner = functionInner(nonWs[0]!)\n if (inner) {\n const marker = tryFromFunction(inner.name, inner.args, themeVars)\n if (marker) return marker\n }\n }\n\n return null\n}\n\n/**\n * Short-circuit whether this token is a `function` token.\n * @param token\n */\nfunction isFunction(token: TokenOrValue): boolean {\n return token.type === 'function'\n}\n\n/**\n * Pull `(name, args-without-whitespace)` out of a function token.\n * @param token A function-type TokenOrValue.\n * @returns Inner record, or null when the shape is unexpected.\n */\nfunction functionInner(token: TokenOrValue): { name: string; args: readonly TokenOrValue[] } | null {\n if (token.type !== 'function') return null\n return { name: token.value.name, args: token.value.arguments }\n}\n\n/**\n * Pattern-match `max(env(...), n)` / `calc(env(...) + n)` / nested\n * screen-minus-y calc. Operates on the function's already-whitespace-\n * trimmed arg list so every branch can index by position.\n * @param name Function name (`max` / `calc`).\n * @param args Raw argument tokens (whitespace still present).\n * @param themeVars\n * @returns Marker, or null when not recognised.\n */\nfunction tryFromFunction(name: string, args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {\n if (name === 'max') return tryMax(args, themeVars)\n if (name === 'calc') return tryCalc(args, themeVars)\n return null\n}\n\n/**\n * `max(env(safe-area-inset-*), <length>)` — Tailwind v4's `*-safe-or-n`\n * shape. Optionally `max(env(...), calc(var(--spacing) * n))` — the\n * calc inside has already been resolved to a bare length by Tailwind's\n * compiler step.\n * @param args Whitespace-preserving argument tokens.\n * @param themeVars\n * @returns Marker, or null.\n */\nfunction tryMax(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {\n const parts = splitTopLevelComma(args)\n if (parts.length !== 2) return null\n const firstToken = onlyNonWhitespace(parts[0]!)\n if (!firstToken) return null\n const firstSide = envSide(firstToken)\n if (firstSide === null) return null\n const rhs = coerceLengthPx(parts[1]!, themeVars)\n if (rhs === null) return null\n return { __safe: firstSide, or: rhs }\n}\n\n/**\n * Recognise:\n * - `calc(env(safe-area-inset-*) + n)` — `*-safe-offset-n`\n * - `calc(100vh - (env(...-top) + env(...-bottom)))` — `h-screen-safe`\n * @param args Whitespace-preserving calc argument tokens.\n * @param themeVars\n * @returns Marker, or null.\n */\nfunction tryCalc(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {\n const [signIndex, sign] = findTopLevelPlusOrMinus(args)\n if (signIndex === -1) return reduceNestedCalc(args, themeVars)\n const lhs = stripWhitespace(args.slice(0, signIndex))\n const rhs = stripWhitespace(args.slice(signIndex + 1))\n const offset = matchOffset(lhs, rhs, sign, themeVars)\n if (offset) return offset\n if (sign === '-' && isViewportHeightHundred(lhs) && isParenthesisedTopBottomSum(rhs)) return { __safe: 'screen-minus-y' }\n return null\n}\n\n/**\n * Fall-through arm of {@link tryCalc}: when there's no +/− at the calc\n * body's top level, try to interpret the whole body as a nested\n * function (e.g. a bare `calc(max(...))`).\n * @param args Calc arguments.\n * @param themeVars Optional theme-vars table for length resolution.\n * @returns Marker or null.\n */\nfunction reduceNestedCalc(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {\n const inner = onlyNonWhitespace(args)\n if (!inner) return null\n return detectSafeAreaMarker([inner], themeVars)\n}\n\n/**\n * Try to match `calc(env(side) ± amount)` against the two-side split of\n * the calc body. Returns a marker with positive / negative `offset`\n * based on the sign, or null when the shape doesn't fit.\n * @param lhs Left-hand tokens (whitespace already stripped).\n * @param rhs Right-hand tokens (whitespace already stripped).\n * @param sign The `+` or `-` delim captured between them.\n * @param themeVars Optional theme-vars table.\n * @returns Offset marker, or null.\n */\nfunction matchOffset(\n lhs: readonly TokenOrValue[],\n rhs: readonly TokenOrValue[],\n sign: '+' | '-' | null,\n themeVars: ThemeVars | undefined,\n): SafeAreaMarker | null {\n if (!sign || lhs.length !== 1 || rhs.length !== 1) return null\n const side = envSide(lhs[0]!)\n if (side === null) return null\n const amount = coerceLengthPx([rhs[0]!], themeVars)\n if (amount === null) return null\n return { __safe: side, offset: sign === '+' ? amount : -amount }\n}\n\n/**\n * Check whether a token is the env() that names one of the four safe-area sides.\n * @param token One TokenOrValue.\n * @returns The compact side tag, or `null` when the token isn't a safe-area env reference.\n */\nfunction envSide(token: TokenOrValue): SafeAreaMarker['__safe'] | null {\n if (token.type !== 'env') return null\n const { name } = token.value\n if (name.type !== 'ua') return null\n return SIDE_TAG[name.value] ?? null\n}\n\n/**\n * Read an `env(side, <fallback>)` token's CSS fallback as a pixel floor.\n * Returns null when there's no fallback or it isn't a plain length — the\n * caller then emits the bare marker.\n * @param token One TokenOrValue (expected to be an `env` token).\n * @param themeVars Optional theme-vars table for resolving `var()` in the fallback.\n * @returns Fallback length in px, or null.\n */\nfunction envFallbackPx(token: TokenOrValue, themeVars: ThemeVars | undefined): number | null {\n if (token.type !== 'env') return null\n const { fallback } = token.value\n if (!fallback || fallback.length === 0) return null\n return coerceLengthPx(stripWhitespace(fallback), themeVars)\n}\n\n/**\n * Drop whitespace / comment tokens from a list so downstream branches\n * can pattern-match by index without counting spaces.\n * @param tokens Raw token list.\n * @returns Copy with whitespace tokens removed.\n */\nfunction stripWhitespace(tokens: readonly TokenOrValue[]): readonly TokenOrValue[] {\n const out: TokenOrValue[] = []\n for (const token of tokens) {\n if (isWhitespaceToken(token)) continue\n out.push(token)\n }\n return out\n}\n\n/**\n * Like {@link stripWhitespace} but returns the single non-whitespace\n * token (or null when there's zero or more than one).\n * @param tokens Raw token list.\n * @returns The single meaningful token, or null.\n */\nfunction onlyNonWhitespace(tokens: readonly TokenOrValue[]): TokenOrValue | null {\n const stripped = stripWhitespace(tokens)\n return stripped.length === 1 ? stripped[0]! : null\n}\n\n/**\n * Whether the token is pure whitespace / comment — ignorable when\n * matching by position.\n * @param token One TokenOrValue.\n * @returns True when the token carries no semantic value.\n */\nfunction isWhitespaceToken(token: TokenOrValue): boolean {\n if (token.type !== 'token') return false\n const { value } = token\n return value.type === 'white-space' || value.type === 'comment'\n}\n\n/**\n * Split `a, b` top-level argument lists into their slices. Respects\n * nested function/paren groups so commas inside an inner `calc(a, b)`\n * don't cause an outer split.\n * @param args Raw argument token list.\n * @returns Array of slices (one per top-level comma-separated segment).\n */\nfunction splitTopLevelComma(args: readonly TokenOrValue[]): readonly TokenOrValue[][] {\n // Functions already arrive as atomic `{type:'function', value}` nodes,\n // so commas inside them are never in `args` — a flat scan for the\n // raw `comma` token is enough.\n const parts: TokenOrValue[][] = [[]]\n for (const token of args) {\n if (token.type === 'token' && token.value.type === 'comma') {\n parts.push([])\n continue\n }\n parts.at(-1)!.push(token)\n }\n return parts\n}\n\n/**\n * Find the first top-level `+` / `-` delim in a calc body.\n * @param args Raw argument token list.\n * @returns Tuple of `[index, sign]` or `[-1, null]` when not found.\n */\nfunction findTopLevelPlusOrMinus(args: readonly TokenOrValue[]): [number, '+' | '-' | null] {\n for (const [index, argument] of args.entries()) {\n const token = argument!\n if (token.type !== 'token') continue\n if (token.value.type !== 'delim') continue\n const delim = token.value.value as string\n if (delim === '+' || delim === '-') return [index, delim]\n }\n return [-1, null]\n}\n\n/**\n * Coerce a token list that should represent a CSS length (px / rem /\n * integer-number with no unit) into a px value.\n * @param tokens Tokens for the length fragment.\n * @param themeVars\n * @returns Px value, or null when the shape is unrecognised.\n */\nfunction coerceLengthPx(tokens: readonly TokenOrValue[], themeVars: ThemeVars | undefined): number | null {\n const stripped = stripWhitespace(tokens)\n if (stripped.length !== 1) return null\n return coerceTokenToPx(stripped[0]!, themeVars)\n}\n\n/**\n * Convert one token to a px number, handling the lengths Tailwind v4\n * commonly emits after compile: `length` (px/rem), bare numbers,\n * `calc(0.25rem * 4)` nested functions.\n * @param token One TokenOrValue.\n * @param themeVars\n * @returns Px value, or null.\n */\nfunction coerceTokenToPx(token: TokenOrValue, themeVars: ThemeVars | undefined): number | null {\n if (token.type === 'length') {\n const { unit, value } = token.value\n if (unit === 'px') return value\n if (unit === 'rem' || unit === 'em') return value * REM_TO_PX\n return null\n }\n if (token.type === 'token' && token.value.type === 'number') {\n return token.value.value\n }\n if (token.type === 'function' && token.value.name === 'calc') {\n return evaluateSimpleCalc(token.value.arguments, themeVars)\n }\n if (token.type === 'var') return resolveVariableToPx(token.value.name.ident, themeVars)\n return null\n}\n\n/**\n * Evaluate a trivial `calc(A * B)` where A / B are lengths or numbers\n * Tailwind compiles down to — the exact shape `var(--spacing)` resolves\n * to after `theme(inline)` substitution.\n * @param args Raw calc argument token list.\n * @param themeVars\n * @returns Px value, or null.\n */\nfunction evaluateSimpleCalc(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): number | null {\n const stripped = stripWhitespace(args)\n if (stripped.length === 3) {\n const mul = stripped[1]!\n if (mul.type === 'token' && mul.value.type === 'delim' && (mul.value.value as string) === '*') {\n const left = coerceTokenToPx(stripped[0]!, themeVars)\n const right = coerceTokenToPx(stripped[2]!, themeVars)\n if (left !== null && right !== null) return left * right\n }\n }\n if (stripped.length === 1) return coerceTokenToPx(stripped[0]!, themeVars)\n return null\n}\n\n/**\n * Resolve a `var(--name)` reference to a px value using the supplied\n * theme-var table. Returns null when the name isn't registered or the\n * value isn't a recognisable length.\n * @param name Custom-property name (with leading `--`).\n * @param themeVars Lookup table (or undefined).\n * @returns Px value, or null.\n */\nfunction resolveVariableToPx(name: string, themeVars: ThemeVars | undefined): number | null {\n if (!themeVars) return null\n const raw = themeVars.get(name)\n if (raw === undefined) return null\n const trimmed = raw.trim()\n if (trimmed.endsWith('rem')) {\n const n = Number(trimmed.slice(0, -3))\n return Number.isFinite(n) ? n * REM_TO_PX : null\n }\n if (trimmed.endsWith('em')) {\n const n = Number(trimmed.slice(0, -2))\n return Number.isFinite(n) ? n * REM_TO_PX : null\n }\n if (trimmed.endsWith('px')) {\n const n = Number(trimmed.slice(0, -2))\n return Number.isFinite(n) ? n : null\n }\n const n = Number(trimmed)\n return Number.isFinite(n) ? n : null\n}\n\n/**\n * Whether the token list represents `100vh`.\n * @param tokens Whitespace-stripped token list.\n * @returns True when the only meaningful token is the length `100vh`.\n */\nfunction isViewportHeightHundred(tokens: readonly TokenOrValue[]): boolean {\n if (tokens.length !== 1) return false\n const token = tokens[0]!\n if (token.type !== 'length') return false\n return token.value.unit === 'vh' && token.value.value === 100\n}\n\n/**\n * Whether the rhs of `calc(100vh - <rhs>)` is the nested `(env(top) + env(bottom))`\n * parenthesised sum Tailwind emits for `h-screen-safe` and siblings.\n * @param tokens Whitespace-stripped rhs token list.\n * @returns True on match.\n */\nfunction isParenthesisedTopBottomSum(tokens: readonly TokenOrValue[]): boolean {\n // lightningcss emits bare `(...)` as a `parenthesis-block` token\n // followed by the inner tokens inline, then a `close-parenthesis`\n // token at the matching depth. Strip whitespace + the grouping tokens\n // and check that what remains is exactly `env(top) + env(bottom)`\n // (either order).\n const meaningful: TokenOrValue[] = []\n for (const token of tokens) {\n if (isWhitespaceToken(token)) continue\n if (token.type === 'token') {\n const t = token.value.type\n if (t === 'parenthesis-block' || t === 'close-parenthesis') continue\n }\n meaningful.push(token)\n }\n if (meaningful.length !== 3) return false\n const plus = meaningful[1]!\n if (plus.type !== 'token' || plus.value.type !== 'delim' || (plus.value.value as string) !== '+') return false\n const first = envSide(meaningful[0]!)\n const last = envSide(meaningful[2]!)\n return (first === 't' && last === 'b') || (first === 'b' && last === 't')\n}\n\nexport { detectSafeAreaMarker }\n\n"],"names":[],"mappings":"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"}
@@ -29,11 +29,12 @@ const CLASS_IN_SELECTOR = /\.([A-Za-z_][\w-]*)/g;
29
29
  * @param start Start index of the block body (0 for top-level).
30
30
  * @param scheme Active scheme name for declarations inside this scope.
31
31
  * @param table Destination table, mutated in place.
32
+ * @param schemeClasses Selector-class → scheme-name map (from `@custom-variant` selectors).
32
33
  */
33
- function walkBlocks(source, start, scheme, table) {
34
+ function walkBlocks(source, start, scheme, table, schemeClasses) {
34
35
  let index = start;
35
36
  while (index < source.length) {
36
- const step = nextWalkStep(source, index, scheme, table);
37
+ const step = nextWalkStep(source, index, scheme, table, schemeClasses);
37
38
  if (step.next === -1)
38
39
  return;
39
40
  index = step.next;
@@ -45,9 +46,10 @@ function walkBlocks(source, start, scheme, table) {
45
46
  * @param index Current scan index.
46
47
  * @param scheme Active scheme name.
47
48
  * @param table Destination table.
49
+ * @param schemeClasses Selector-class → scheme-name map (from `@custom-variant` selectors).
48
50
  * @returns Step descriptor with the next scan index (or -1 for EOF).
49
51
  */
50
- function nextWalkStep(source, index, scheme, table) {
52
+ function nextWalkStep(source, index, scheme, table, schemeClasses) {
51
53
  // Find the next TOP-LEVEL `--` (outside any `( ... )`). That's the only
52
54
  // place a custom-property declaration can start. `--foo` that appears
53
55
  // inside `var(--foo)` or `calc(... --value(integer) ...)` is part of a
@@ -62,7 +64,7 @@ function nextWalkStep(source, index, scheme, table) {
62
64
  return { next: consumeDeclaration(source, atIndex, scheme, table) };
63
65
  }
64
66
  if (openIndex !== -1 && openIndex < blockClose) {
65
- return { next: enterBlock(source, index, openIndex, scheme, table) };
67
+ return { next: enterBlock(source, index, openIndex, scheme, table, schemeClasses) };
66
68
  }
67
69
  return { next: -1 };
68
70
  }
@@ -115,9 +117,10 @@ function isDeclarationNext(atIndex, openIndex, blockClose) {
115
117
  * @param openIndex Index of the opening brace.
116
118
  * @param scheme Scheme active in the parent scope.
117
119
  * @param table Destination table.
120
+ * @param schemeClasses Selector-class → scheme-name map (from `@custom-variant` selectors).
118
121
  * @returns Index past the matching closing brace.
119
122
  */
120
- function enterBlock(source, index, openIndex, scheme, table) {
123
+ function enterBlock(source, index, openIndex, scheme, table, schemeClasses) {
121
124
  const header = source.slice(index, openIndex).trim();
122
125
  // Skip blocks that define utilities / at-rules that carry declarations
123
126
  // meant for a downstream compiler, not custom-property values for the
@@ -126,10 +129,56 @@ function enterBlock(source, index, openIndex, scheme, table) {
126
129
  // top-level declaration walker and spill into the extracted theme.
127
130
  if (isNonThemeAtRule(header))
128
131
  return skipMatchingBrace(source, openIndex + 1);
129
- const childScheme = variantNameOf(header) ?? scheme;
130
- walkBlocks(source, openIndex + 1, childScheme, table);
132
+ // Scheme scope switches on EITHER an `@variant <name> {` block OR a plain
133
+ // selector block targeting a scheme class (`.dark { }` Tailwind v4's
134
+ // standard dark-mode shape, often wrapped in `@layer base`). Without the
135
+ // selector case, `.dark`'s overrides poured into `base` and overwrote the
136
+ // light defaults, so every scheme rendered the dark values.
137
+ const childScheme = variantNameOf(header) ?? schemeFromSelector(header, schemeClasses) ?? scheme;
138
+ walkBlocks(source, openIndex + 1, childScheme, table, schemeClasses);
131
139
  return skipMatchingBrace(source, openIndex + 1);
132
140
  }
141
+ /**
142
+ * Map a plain selector block header to the scheme it overrides, using the
143
+ * class → scheme map derived from `@custom-variant` declarations. Returns the
144
+ * first scheme class found in the selector (`.dark { … }` → `dark`,
145
+ * `.scheme-dark, .scheme-dark * { … }` → `dark`), or null for at-rules and
146
+ * non-scheme selectors (which keep the parent scheme).
147
+ * @param header Block header text (the selector before the `{`).
148
+ * @param schemeClasses Class-name → scheme-name map.
149
+ * @returns Scheme name, or null.
150
+ */
151
+ function schemeFromSelector(header, schemeClasses) {
152
+ if (header.startsWith('@') || schemeClasses.size === 0)
153
+ return null;
154
+ for (const match of header.matchAll(CLASS_IN_SELECTOR)) {
155
+ const mapped = schemeClasses.get(match[1]);
156
+ if (mapped)
157
+ return mapped;
158
+ }
159
+ return null;
160
+ }
161
+ /**
162
+ * Build a `<selector-class> → <scheme-name>` map from every
163
+ * `@custom-variant <name> (<class-selector>);` declaration. Unlike
164
+ * {@link extractSchemeAliases} this KEEPS the `class === scheme` case
165
+ * (`.dark → dark`) — that's exactly the mapping needed to attribute a
166
+ * `.dark { … }` override block to the dark scheme.
167
+ * @param css Pre-stripped CSS source.
168
+ * @returns Class-name → scheme-name map.
169
+ */
170
+ function buildSchemeClassMap(css) {
171
+ const map = new Map();
172
+ for (const match of css.matchAll(CUSTOM_VARIANT_WITH_SELECTOR)) {
173
+ const scheme = match[1];
174
+ const selector = match[2];
175
+ if (!isSchemeSelector(selector))
176
+ continue;
177
+ for (const cls of selector.matchAll(CLASS_IN_SELECTOR))
178
+ map.set(cls[1], scheme);
179
+ }
180
+ return map;
181
+ }
133
182
  /**
134
183
  * Whether a block header belongs to an at-rule whose body should be
135
184
  * ignored by the theme-var extractor. `@utility` / `@media` / `@keyframes`
@@ -345,7 +394,8 @@ const BASE_SCHEME = 'base';
345
394
  function extractThemeVars(css) {
346
395
  const table = new Map();
347
396
  const stripped = stripComments(css);
348
- walkBlocks(stripped, 0, BASE_SCHEME, table);
397
+ const schemeClasses = buildSchemeClassMap(stripped);
398
+ walkBlocks(stripped, 0, BASE_SCHEME, table, schemeClasses);
349
399
  return table;
350
400
  }
351
401
  /**