rnwind 0.0.2 → 0.0.3

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 (80) hide show
  1. package/lib/cjs/core/parser/color.cjs +53 -24
  2. package/lib/cjs/core/parser/color.cjs.map +1 -1
  3. package/lib/cjs/core/parser/layout-dispatcher.cjs +20 -0
  4. package/lib/cjs/core/parser/layout-dispatcher.cjs.map +1 -1
  5. package/lib/cjs/core/parser/length.cjs +20 -6
  6. package/lib/cjs/core/parser/length.cjs.map +1 -1
  7. package/lib/cjs/core/parser/length.d.ts +6 -3
  8. package/lib/cjs/core/parser/shorthand.cjs +37 -5
  9. package/lib/cjs/core/parser/shorthand.cjs.map +1 -1
  10. package/lib/cjs/core/parser/shorthand.d.ts +11 -5
  11. package/lib/cjs/core/parser/theme-vars.cjs +53 -0
  12. package/lib/cjs/core/parser/theme-vars.cjs.map +1 -1
  13. package/lib/cjs/core/parser/theme-vars.d.ts +21 -0
  14. package/lib/cjs/core/parser/tokens.cjs +183 -1
  15. package/lib/cjs/core/parser/tokens.cjs.map +1 -1
  16. package/lib/cjs/core/parser/tw-parser.cjs +140 -27
  17. package/lib/cjs/core/parser/tw-parser.cjs.map +1 -1
  18. package/lib/cjs/core/parser/tw-parser.d.ts +21 -5
  19. package/lib/cjs/core/parser/typography-dispatcher.cjs +16 -1
  20. package/lib/cjs/core/parser/typography-dispatcher.cjs.map +1 -1
  21. package/lib/cjs/core/style-builder/build-style.cjs +73 -26
  22. package/lib/cjs/core/style-builder/build-style.cjs.map +1 -1
  23. package/lib/cjs/metro/state.cjs +52 -2
  24. package/lib/cjs/metro/state.cjs.map +1 -1
  25. package/lib/cjs/metro/state.d.ts +17 -1
  26. package/lib/cjs/metro/transform-ast.cjs +238 -21
  27. package/lib/cjs/metro/transform-ast.cjs.map +1 -1
  28. package/lib/cjs/metro/transform-ast.d.ts +15 -0
  29. package/lib/cjs/metro/transformer.cjs +29 -2
  30. package/lib/cjs/metro/transformer.cjs.map +1 -1
  31. package/lib/cjs/metro/with-config.cjs +1 -1
  32. package/lib/cjs/metro/with-config.cjs.map +1 -1
  33. package/lib/cjs/metro/with-config.d.ts +22 -0
  34. package/lib/esm/core/parser/color.mjs +53 -24
  35. package/lib/esm/core/parser/color.mjs.map +1 -1
  36. package/lib/esm/core/parser/layout-dispatcher.mjs +20 -0
  37. package/lib/esm/core/parser/layout-dispatcher.mjs.map +1 -1
  38. package/lib/esm/core/parser/length.d.ts +6 -3
  39. package/lib/esm/core/parser/length.mjs +20 -6
  40. package/lib/esm/core/parser/length.mjs.map +1 -1
  41. package/lib/esm/core/parser/shorthand.d.ts +11 -5
  42. package/lib/esm/core/parser/shorthand.mjs +37 -5
  43. package/lib/esm/core/parser/shorthand.mjs.map +1 -1
  44. package/lib/esm/core/parser/theme-vars.d.ts +21 -0
  45. package/lib/esm/core/parser/theme-vars.mjs +53 -1
  46. package/lib/esm/core/parser/theme-vars.mjs.map +1 -1
  47. package/lib/esm/core/parser/tokens.mjs +183 -1
  48. package/lib/esm/core/parser/tokens.mjs.map +1 -1
  49. package/lib/esm/core/parser/tw-parser.d.ts +21 -5
  50. package/lib/esm/core/parser/tw-parser.mjs +141 -28
  51. package/lib/esm/core/parser/tw-parser.mjs.map +1 -1
  52. package/lib/esm/core/parser/typography-dispatcher.mjs +16 -1
  53. package/lib/esm/core/parser/typography-dispatcher.mjs.map +1 -1
  54. package/lib/esm/core/style-builder/build-style.mjs +73 -26
  55. package/lib/esm/core/style-builder/build-style.mjs.map +1 -1
  56. package/lib/esm/metro/state.d.ts +17 -1
  57. package/lib/esm/metro/state.mjs +51 -3
  58. package/lib/esm/metro/state.mjs.map +1 -1
  59. package/lib/esm/metro/transform-ast.d.ts +15 -0
  60. package/lib/esm/metro/transform-ast.mjs +238 -21
  61. package/lib/esm/metro/transform-ast.mjs.map +1 -1
  62. package/lib/esm/metro/transformer.mjs +30 -3
  63. package/lib/esm/metro/transformer.mjs.map +1 -1
  64. package/lib/esm/metro/with-config.d.ts +22 -0
  65. package/lib/esm/metro/with-config.mjs +1 -1
  66. package/lib/esm/metro/with-config.mjs.map +1 -1
  67. package/package.json +2 -1
  68. package/src/core/parser/color.ts +52 -18
  69. package/src/core/parser/layout-dispatcher.ts +19 -0
  70. package/src/core/parser/length.ts +20 -6
  71. package/src/core/parser/shorthand.ts +35 -5
  72. package/src/core/parser/theme-vars.ts +53 -0
  73. package/src/core/parser/tokens.ts +171 -1
  74. package/src/core/parser/tw-parser.ts +147 -28
  75. package/src/core/parser/typography-dispatcher.ts +15 -1
  76. package/src/core/style-builder/build-style.ts +84 -26
  77. package/src/metro/state.ts +49 -1
  78. package/src/metro/transform-ast.ts +249 -18
  79. package/src/metro/transformer.ts +28 -3
  80. package/src/metro/with-config.ts +23 -1
@@ -72,6 +72,25 @@ function culoriHexFor(color) {
72
72
  }
73
73
  }
74
74
  }
75
+ /**
76
+ * Composite a culori-produced sRGB hex with the source alpha into the RN
77
+ * color string. Shared tail for every culori-backed conversion (lab
78
+ * family, XYZ, wide-gamut RGB): opaque → the hex as-is; translucent →
79
+ * `rgba(...)` rebuilt from the hex channels.
80
+ * @param hex sRGB hex from culori, or `null` when culori rejected the color.
81
+ * @param alpha Source alpha (0–1).
82
+ * @returns RN color string.
83
+ */
84
+ function withAlpha(hex, alpha) {
85
+ if (!hex)
86
+ return alpha < 1 ? 'rgba(0, 0, 0, 0)' : 'transparent';
87
+ if (alpha >= 1)
88
+ return hex;
89
+ const back = culori.rgb(hex);
90
+ if (!back)
91
+ return hex;
92
+ return rgbIntsToString(clampByte(back.r * 255), clampByte(back.g * 255), clampByte(back.b * 255), alpha);
93
+ }
75
94
  /**
76
95
  * Convert a LAB / LCH / OKLAB / OKLCH color to sRGB hex via culori. RN
77
96
  * can't evaluate these modern color spaces at paint time; compile-time
@@ -80,15 +99,23 @@ function culoriHexFor(color) {
80
99
  * @returns Hex or rgba string in sRGB.
81
100
  */
82
101
  function labFamilyToHex(color) {
83
- const hex = culoriHexFor(color);
84
- if (!hex)
85
- return color.alpha < 1 ? 'rgba(0, 0, 0, 0)' : 'transparent';
86
- if (color.alpha >= 1)
87
- return hex;
88
- const back = culori.rgb(hex);
89
- if (!back)
90
- return hex;
91
- return rgbIntsToString(clampByte(back.r * 255), clampByte(back.g * 255), clampByte(back.b * 255), color.alpha);
102
+ return withAlpha(culoriHexFor(color), color.alpha);
103
+ }
104
+ /**
105
+ * Convert a wide-gamut `color(<space> r g b)` triple to sRGB hex via
106
+ * culori. The channels are NOT sRGB — each space (display-p3, rec2020,
107
+ * a98-rgb, prophoto-rgb, srgb-linear) carries its own primaries / transfer
108
+ * function, so a bare `channel * 255` would mis-paint. culori does the
109
+ * gamut + gamma conversion to sRGB.
110
+ * @param mode culori mode key for the source space.
111
+ * @param r Source red (0–1).
112
+ * @param g Source green (0–1).
113
+ * @param b Source blue (0–1).
114
+ * @param alpha Alpha channel (0–1).
115
+ * @returns sRGB color string RN accepts.
116
+ */
117
+ function wideGamutToHex(mode, r, g, b, alpha) {
118
+ return withAlpha(culori.formatHex({ mode, r, g, b }) ?? null, alpha);
92
119
  }
93
120
  /**
94
121
  * Convert a CSS `color(xyz-d50 …)` / `color(xyz-d65 …)` value to sRGB hex
@@ -103,15 +130,7 @@ function labFamilyToHex(color) {
103
130
  */
104
131
  function xyzToHex(color) {
105
132
  const mode = color.type === 'xyz-d50' ? 'xyz50' : 'xyz65';
106
- const hex = culori.formatHex({ mode, x: color.x, y: color.y, z: color.z }) ?? null;
107
- if (!hex)
108
- return color.alpha < 1 ? 'rgba(0, 0, 0, 0)' : 'transparent';
109
- if (color.alpha >= 1)
110
- return hex;
111
- const back = culori.rgb(hex);
112
- if (!back)
113
- return hex;
114
- return rgbIntsToString(clampByte(back.r * 255), clampByte(back.g * 255), clampByte(back.b * 255), color.alpha);
133
+ return withAlpha(culori.formatHex({ mode, x: color.x, y: color.y, z: color.z }) ?? null, color.alpha);
115
134
  }
116
135
  /**
117
136
  * Convert a lightningcss `CssColor` to an RN-safe color string. RGB
@@ -136,14 +155,24 @@ function cssColorToString(color) {
136
155
  case 'oklch': {
137
156
  return labFamilyToHex(color);
138
157
  }
139
- case 'srgb':
140
- case 'srgb-linear':
141
- case 'display-p3':
142
- case 'a98-rgb':
143
- case 'prophoto-rgb':
144
- case 'rec2020': {
158
+ case 'srgb': {
145
159
  return floatRgbToString(color.r, color.g, color.b, color.alpha);
146
160
  }
161
+ case 'srgb-linear': {
162
+ return wideGamutToHex('lrgb', color.r, color.g, color.b, color.alpha);
163
+ }
164
+ case 'display-p3': {
165
+ return wideGamutToHex('p3', color.r, color.g, color.b, color.alpha);
166
+ }
167
+ case 'a98-rgb': {
168
+ return wideGamutToHex('a98', color.r, color.g, color.b, color.alpha);
169
+ }
170
+ case 'prophoto-rgb': {
171
+ return wideGamutToHex('prophoto', color.r, color.g, color.b, color.alpha);
172
+ }
173
+ case 'rec2020': {
174
+ return wideGamutToHex('rec2020', color.r, color.g, color.b, color.alpha);
175
+ }
147
176
  case 'xyz-d50':
148
177
  case 'xyz-d65': {
149
178
  return xyzToHex(color);
@@ -1 +1 @@
1
- {"version":3,"file":"color.cjs","sources":["../../../../../src/core/parser/color.ts"],"sourcesContent":["import type { CssColor, LABColor } from 'lightningcss'\nimport { formatHex, rgb as culoriRgb } from 'culori'\n\n/**\n * Clamp a 0-255 float to the integer byte range RN color strings accept.\n * @param value Unclamped float (may be negative or above 255).\n * @returns Integer in `[0, 255]`.\n */\nfunction clampByte(value: number): number {\n if (value < 0) return 0\n if (value > 255) return 255\n return Math.round(value)\n}\n\n/**\n * Two-digit hex encoding of a 0-255 byte.\n * @param byte Byte value (may be out-of-range — clamped).\n * @returns Zero-padded two-char hex string.\n */\nfunction byteToHex(byte: number): string {\n const hex = clampByte(byte).toString(16)\n return hex.length === 1 ? `0${hex}` : hex\n}\n\n/**\n * Format an integer-RGB triple + alpha as `#rrggbb` or `rgba(r, g, b, a)`.\n * @param r 0-255 red.\n * @param g 0-255 green.\n * @param b 0-255 blue.\n * @param alpha 0-1 alpha.\n * @returns Color string.\n */\nfunction rgbIntsToString(r: number, g: number, b: number, alpha: number): string {\n if (alpha >= 1) return `#${byteToHex(r)}${byteToHex(g)}${byteToHex(b)}`\n return `rgba(${r}, ${g}, ${b}, ${alpha})`\n}\n\n/**\n * Format a float-RGB triple + alpha (CSS `color(srgb …)` forms) as\n * hex/rgba.\n * @param r 0-1 red.\n * @param g 0-1 green.\n * @param b 0-1 blue.\n * @param alpha 0-1 alpha.\n * @returns Color string.\n */\nfunction floatRgbToString(r: number, g: number, b: number, alpha: number): string {\n return rgbIntsToString(clampByte(r * 255), clampByte(g * 255), clampByte(b * 255), alpha)\n}\n\n/**\n * Dispatch a LAB-family color through culori's hex formatter.\n * @param color Typed LAB-family color.\n * @returns `#rrggbb` string, or `null` when culori couldn't convert.\n */\nfunction culoriHexFor(color: LABColor): string | null {\n switch (color.type) {\n case 'oklch': {\n return formatHex({ mode: 'oklch', l: color.l, c: color.c, h: color.h }) ?? null\n }\n case 'oklab': {\n return formatHex({ mode: 'oklab', l: color.l, a: color.a, b: color.b }) ?? null\n }\n case 'lab': {\n return formatHex({ mode: 'lab', l: color.l, a: color.a, b: color.b }) ?? null\n }\n case 'lch': {\n return formatHex({ mode: 'lch', l: color.l, c: color.c, h: color.h }) ?? null\n }\n default: {\n return null\n }\n }\n}\n\n/**\n * Convert a LAB / LCH / OKLAB / OKLCH color to sRGB hex via culori. RN\n * can't evaluate these modern color spaces at paint time; compile-time\n * lowering to sRGB is the only portable path.\n * @param color Typed lab-family color.\n * @returns Hex or rgba string in sRGB.\n */\nfunction labFamilyToHex(color: LABColor): string {\n const hex = culoriHexFor(color)\n if (!hex) return color.alpha < 1 ? 'rgba(0, 0, 0, 0)' : 'transparent'\n if (color.alpha >= 1) return hex\n const back = culoriRgb(hex)\n if (!back) return hex\n return rgbIntsToString(clampByte(back.r * 255), clampByte(back.g * 255), clampByte(back.b * 255), color.alpha)\n}\n\n/**\n * Convert a CSS `color(xyz-d50 …)` / `color(xyz-d65 …)` value to sRGB hex\n * via culori.\n * @param color Typed XYZ color (discriminated by `type`).\n * @param color.type Whether the color is in the D50 or D65 XYZ space.\n * @param color.x CIE X component (0–1).\n * @param color.y CIE Y component (0–1).\n * @param color.z CIE Z component (0–1).\n * @param color.alpha Alpha channel (0–1).\n * @returns `#rrggbb` string, or `'transparent'` when culori rejects it.\n */\nfunction xyzToHex(color: { type: 'xyz-d50' | 'xyz-d65'; x: number; y: number; z: number; alpha: number }): string {\n const mode = color.type === 'xyz-d50' ? 'xyz50' : 'xyz65'\n const hex = formatHex({ mode, x: color.x, y: color.y, z: color.z }) ?? null\n if (!hex) return color.alpha < 1 ? 'rgba(0, 0, 0, 0)' : 'transparent'\n if (color.alpha >= 1) return hex\n const back = culoriRgb(hex)\n if (!back) return hex\n return rgbIntsToString(clampByte(back.r * 255), clampByte(back.g * 255), clampByte(back.b * 255), color.alpha)\n}\n\n/**\n * Convert a lightningcss `CssColor` to an RN-safe color string. RGB\n * passes through unchanged. LAB / LCH / OKLAB / OKLCH / `color(xyz-…)`\n * forms go through culori to reach sRGB — RN's native view manager only\n * understands sRGB-family strings. SystemColor keywords (`'background'`,\n * `'canvas'`, …) pass through untouched; they have no RN analog and the\n * runtime ignores unknown color strings gracefully.\n * @param color Typed color value.\n * @returns Color string RN accepts.\n */\nexport function cssColorToString(color: CssColor): string {\n if (typeof color === 'string') return color\n switch (color.type) {\n case 'rgb': {\n return rgbIntsToString(color.r, color.g, color.b, color.alpha)\n }\n case 'lab':\n case 'lch':\n case 'oklab':\n case 'oklch': {\n return labFamilyToHex(color)\n }\n case 'srgb':\n case 'srgb-linear':\n case 'display-p3':\n case 'a98-rgb':\n case 'prophoto-rgb':\n case 'rec2020': {\n return floatRgbToString(color.r, color.g, color.b, color.alpha)\n }\n case 'xyz-d50':\n case 'xyz-d65': {\n return xyzToHex(color)\n }\n case 'currentcolor': {\n return 'currentColor'\n }\n case 'light-dark': {\n return cssColorToString(color.light)\n }\n default: {\n return 'transparent'\n }\n }\n}\n"],"names":["formatHex","culoriRgb"],"mappings":"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"}
1
+ {"version":3,"file":"color.cjs","sources":["../../../../../src/core/parser/color.ts"],"sourcesContent":["import type { CssColor, LABColor } from 'lightningcss'\nimport { formatHex, rgb as culoriRgb } from 'culori'\n\n/**\n * Clamp a 0-255 float to the integer byte range RN color strings accept.\n * @param value Unclamped float (may be negative or above 255).\n * @returns Integer in `[0, 255]`.\n */\nfunction clampByte(value: number): number {\n if (value < 0) return 0\n if (value > 255) return 255\n return Math.round(value)\n}\n\n/**\n * Two-digit hex encoding of a 0-255 byte.\n * @param byte Byte value (may be out-of-range — clamped).\n * @returns Zero-padded two-char hex string.\n */\nfunction byteToHex(byte: number): string {\n const hex = clampByte(byte).toString(16)\n return hex.length === 1 ? `0${hex}` : hex\n}\n\n/**\n * Format an integer-RGB triple + alpha as `#rrggbb` or `rgba(r, g, b, a)`.\n * @param r 0-255 red.\n * @param g 0-255 green.\n * @param b 0-255 blue.\n * @param alpha 0-1 alpha.\n * @returns Color string.\n */\nfunction rgbIntsToString(r: number, g: number, b: number, alpha: number): string {\n if (alpha >= 1) return `#${byteToHex(r)}${byteToHex(g)}${byteToHex(b)}`\n return `rgba(${r}, ${g}, ${b}, ${alpha})`\n}\n\n/**\n * Format a float-RGB triple + alpha (CSS `color(srgb …)` forms) as\n * hex/rgba.\n * @param r 0-1 red.\n * @param g 0-1 green.\n * @param b 0-1 blue.\n * @param alpha 0-1 alpha.\n * @returns Color string.\n */\nfunction floatRgbToString(r: number, g: number, b: number, alpha: number): string {\n return rgbIntsToString(clampByte(r * 255), clampByte(g * 255), clampByte(b * 255), alpha)\n}\n\n/**\n * Dispatch a LAB-family color through culori's hex formatter.\n * @param color Typed LAB-family color.\n * @returns `#rrggbb` string, or `null` when culori couldn't convert.\n */\nfunction culoriHexFor(color: LABColor): string | null {\n switch (color.type) {\n case 'oklch': {\n return formatHex({ mode: 'oklch', l: color.l, c: color.c, h: color.h }) ?? null\n }\n case 'oklab': {\n return formatHex({ mode: 'oklab', l: color.l, a: color.a, b: color.b }) ?? null\n }\n case 'lab': {\n return formatHex({ mode: 'lab', l: color.l, a: color.a, b: color.b }) ?? null\n }\n case 'lch': {\n return formatHex({ mode: 'lch', l: color.l, c: color.c, h: color.h }) ?? null\n }\n default: {\n return null\n }\n }\n}\n\n/**\n * Composite a culori-produced sRGB hex with the source alpha into the RN\n * color string. Shared tail for every culori-backed conversion (lab\n * family, XYZ, wide-gamut RGB): opaque → the hex as-is; translucent →\n * `rgba(...)` rebuilt from the hex channels.\n * @param hex sRGB hex from culori, or `null` when culori rejected the color.\n * @param alpha Source alpha (0–1).\n * @returns RN color string.\n */\nfunction withAlpha(hex: string | null, alpha: number): string {\n if (!hex) return alpha < 1 ? 'rgba(0, 0, 0, 0)' : 'transparent'\n if (alpha >= 1) return hex\n const back = culoriRgb(hex)\n if (!back) return hex\n return rgbIntsToString(clampByte(back.r * 255), clampByte(back.g * 255), clampByte(back.b * 255), alpha)\n}\n\n/**\n * Convert a LAB / LCH / OKLAB / OKLCH color to sRGB hex via culori. RN\n * can't evaluate these modern color spaces at paint time; compile-time\n * lowering to sRGB is the only portable path.\n * @param color Typed lab-family color.\n * @returns Hex or rgba string in sRGB.\n */\nfunction labFamilyToHex(color: LABColor): string {\n return withAlpha(culoriHexFor(color), color.alpha)\n}\n\n/**\n * Convert a wide-gamut `color(<space> r g b)` triple to sRGB hex via\n * culori. The channels are NOT sRGB — each space (display-p3, rec2020,\n * a98-rgb, prophoto-rgb, srgb-linear) carries its own primaries / transfer\n * function, so a bare `channel * 255` would mis-paint. culori does the\n * gamut + gamma conversion to sRGB.\n * @param mode culori mode key for the source space.\n * @param r Source red (0–1).\n * @param g Source green (0–1).\n * @param b Source blue (0–1).\n * @param alpha Alpha channel (0–1).\n * @returns sRGB color string RN accepts.\n */\nfunction wideGamutToHex(mode: 'lrgb' | 'p3' | 'a98' | 'prophoto' | 'rec2020', r: number, g: number, b: number, alpha: number): string {\n return withAlpha(formatHex({ mode, r, g, b }) ?? null, alpha)\n}\n\n/**\n * Convert a CSS `color(xyz-d50 …)` / `color(xyz-d65 …)` value to sRGB hex\n * via culori.\n * @param color Typed XYZ color (discriminated by `type`).\n * @param color.type Whether the color is in the D50 or D65 XYZ space.\n * @param color.x CIE X component (0–1).\n * @param color.y CIE Y component (0–1).\n * @param color.z CIE Z component (0–1).\n * @param color.alpha Alpha channel (0–1).\n * @returns `#rrggbb` string, or `'transparent'` when culori rejects it.\n */\nfunction xyzToHex(color: { type: 'xyz-d50' | 'xyz-d65'; x: number; y: number; z: number; alpha: number }): string {\n const mode = color.type === 'xyz-d50' ? 'xyz50' : 'xyz65'\n return withAlpha(formatHex({ mode, x: color.x, y: color.y, z: color.z }) ?? null, color.alpha)\n}\n\n/**\n * Convert a lightningcss `CssColor` to an RN-safe color string. RGB\n * passes through unchanged. LAB / LCH / OKLAB / OKLCH / `color(xyz-…)`\n * forms go through culori to reach sRGB — RN's native view manager only\n * understands sRGB-family strings. SystemColor keywords (`'background'`,\n * `'canvas'`, …) pass through untouched; they have no RN analog and the\n * runtime ignores unknown color strings gracefully.\n * @param color Typed color value.\n * @returns Color string RN accepts.\n */\nexport function cssColorToString(color: CssColor): string {\n if (typeof color === 'string') return color\n switch (color.type) {\n case 'rgb': {\n return rgbIntsToString(color.r, color.g, color.b, color.alpha)\n }\n case 'lab':\n case 'lch':\n case 'oklab':\n case 'oklch': {\n return labFamilyToHex(color)\n }\n case 'srgb': {\n return floatRgbToString(color.r, color.g, color.b, color.alpha)\n }\n case 'srgb-linear': {\n return wideGamutToHex('lrgb', color.r, color.g, color.b, color.alpha)\n }\n case 'display-p3': {\n return wideGamutToHex('p3', color.r, color.g, color.b, color.alpha)\n }\n case 'a98-rgb': {\n return wideGamutToHex('a98', color.r, color.g, color.b, color.alpha)\n }\n case 'prophoto-rgb': {\n return wideGamutToHex('prophoto', color.r, color.g, color.b, color.alpha)\n }\n case 'rec2020': {\n return wideGamutToHex('rec2020', color.r, color.g, color.b, color.alpha)\n }\n case 'xyz-d50':\n case 'xyz-d65': {\n return xyzToHex(color)\n }\n case 'currentcolor': {\n return 'currentColor'\n }\n case 'light-dark': {\n return cssColorToString(color.light)\n }\n default: {\n return 'transparent'\n }\n }\n}\n"],"names":["formatHex","culoriRgb"],"mappings":"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"}
@@ -90,6 +90,26 @@ function dispatchLayoutDeclaration(decl) {
90
90
  const v = mapJustifyKeyword(decl.value);
91
91
  return v === null ? [] : [['justifyContent', v]];
92
92
  }
93
+ case 'overflow': {
94
+ // 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]];
104
+ }
105
+ case 'overflow-x':
106
+ case 'overflow-y': {
107
+ // Tailwind's `overflow-x-*` / `overflow-y-*` emit these longhands,
108
+ // not the `overflow` shorthand. RN has only a single `overflow`,
109
+ // so collapse both axes onto it (last one declared wins via the
110
+ // normal entry-merge order).
111
+ return typeof decl.value === 'string' ? [['overflow', decl.value]] : [];
112
+ }
93
113
  default: {
94
114
  return null;
95
115
  }
@@ -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 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 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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"}
@@ -12,24 +12,38 @@ var constants = require('./constants.cjs');
12
12
  function roundFloat(n) {
13
13
  return Math.round(n * 10_000) / 10_000;
14
14
  }
15
+ /**
16
+ * "Fully rounded" sentinel — emitted for Tailwind's `rounded-full` (and
17
+ * any other utility expanding to `calc(infinity * 1px)`). RN can't
18
+ * render `Infinity` as a style value (the StyleSheet validator silently
19
+ * drops it), but it accepts a finite large pixel count and renders the
20
+ * same pill / circle shape. 9999 covers every realistic phone screen.
21
+ */
22
+ const FULLY_ROUNDED_PX = 9999;
15
23
  /**
16
24
  * Convert a lightningcss `LengthValue` to a pixel number. Handles the
17
- * units Tailwind emits: px, rem, em. Unknown units pass through as the
18
- * raw numeric value so a later pass can warn.
25
+ * units Tailwind emits: px, rem, em. Tailwind v4's "fully rounded"
26
+ * expansion (`calc(infinity * 1px)`) lands here as `value === Infinity`
27
+ * — we clamp to a finite sentinel so RN can render it. Other non-finite
28
+ * values (NaN from a malformed expression) are clamped to 0 rather
29
+ * than leaking through as `null` in the serialized RN style.
19
30
  * @param length Typed length value.
20
- * @returns Pixel number.
31
+ * @returns Finite pixel number.
21
32
  */
22
33
  function lengthToPx(length) {
34
+ const raw = length.value;
35
+ if (!Number.isFinite(raw))
36
+ return raw === Number.POSITIVE_INFINITY ? FULLY_ROUNDED_PX : 0;
23
37
  switch (length.unit) {
24
38
  case 'px': {
25
- return length.value;
39
+ return raw;
26
40
  }
27
41
  case 'rem':
28
42
  case 'em': {
29
- return length.value * constants.REM_TO_PX;
43
+ return raw * constants.REM_TO_PX;
30
44
  }
31
45
  default: {
32
- return length.value;
46
+ return raw;
33
47
  }
34
48
  }
35
49
  }
@@ -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 * Convert a lightningcss `LengthValue` to a pixel number. Handles the\n * units Tailwind emits: px, rem, em. Unknown units pass through as the\n * raw numeric value so a later pass can warn.\n * @param length Typed length value.\n * @returns Pixel number.\n */\nexport function lengthToPx(length: LengthValue): number {\n switch (length.unit) {\n case 'px': {\n return length.value\n }\n case 'rem':\n case 'em': {\n return length.value * REM_TO_PX\n }\n default: {\n return length.value\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;AACG,SAAU,UAAU,CAAC,MAAmB,EAAA;AAC5C,IAAA,QAAQ,MAAM,CAAC,IAAI;QACjB,KAAK,IAAI,EAAE;YACT,OAAO,MAAM,CAAC,KAAK;QACrB;AACA,QAAA,KAAK,KAAK;QACV,KAAK,IAAI,EAAE;AACT,YAAA,OAAO,MAAM,CAAC,KAAK,GAAGA,mBAAS;QACjC;QACA,SAAS;YACP,OAAO,MAAM,CAAC,KAAK;QACrB;;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 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;;;;;;;;"}
@@ -5,10 +5,13 @@ type DimensionPercentage = LcDimensionPercentage;
5
5
  type LengthResult = number | string | null;
6
6
  /**
7
7
  * Convert a lightningcss `LengthValue` to a pixel number. Handles the
8
- * units Tailwind emits: px, rem, em. Unknown units pass through as the
9
- * raw numeric value so a later pass can warn.
8
+ * units Tailwind emits: px, rem, em. Tailwind v4's "fully rounded"
9
+ * expansion (`calc(infinity * 1px)`) lands here as `value === Infinity`
10
+ * — we clamp to a finite sentinel so RN can render it. Other non-finite
11
+ * values (NaN from a malformed expression) are clamped to 0 rather
12
+ * than leaking through as `null` in the serialized RN style.
10
13
  * @param length Typed length value.
11
- * @returns Pixel number.
14
+ * @returns Finite pixel number.
12
15
  */
13
16
  export declare function lengthToPx(length: LengthValue): number;
14
17
  /**
@@ -5,8 +5,11 @@ var length = require('./length.cjs');
5
5
 
6
6
  /**
7
7
  * Expand `margin` / `padding` shorthand (`{top, right, bottom, left}`) to
8
- * RN entries. When all four sides share the same converted value, collapse
9
- * to the single-key shorthand RN accepts; otherwise emit four longhands.
8
+ * RN entries. Collapses progressively for a smaller emitted style:
9
+ * - all four equal single `padding` / `margin`
10
+ * - matching axes → `paddingHorizontal` + `paddingVertical`
11
+ * - one matching axis → that axis collapsed, opposite axis as longhands
12
+ * - otherwise → four longhands
10
13
  * @param property `'padding'` or `'margin'`.
11
14
  * @param value Typed shorthand record.
12
15
  * @returns RN entries.
@@ -20,6 +23,28 @@ function expandFourSided(property, value) {
20
23
  return [];
21
24
  if (top === right && right === bottom && bottom === left)
22
25
  return [[property, top]];
26
+ const horizontalEqual = left === right;
27
+ const verticalEqual = top === bottom;
28
+ if (horizontalEqual && verticalEqual) {
29
+ return [
30
+ [`${property}Vertical`, top],
31
+ [`${property}Horizontal`, left],
32
+ ];
33
+ }
34
+ if (horizontalEqual) {
35
+ return [
36
+ [`${property}Top`, top],
37
+ [`${property}Bottom`, bottom],
38
+ [`${property}Horizontal`, left],
39
+ ];
40
+ }
41
+ if (verticalEqual) {
42
+ return [
43
+ [`${property}Vertical`, top],
44
+ [`${property}Right`, right],
45
+ [`${property}Left`, left],
46
+ ];
47
+ }
23
48
  return [
24
49
  [`${property}Top`, top],
25
50
  [`${property}Right`, right],
@@ -29,8 +54,10 @@ function expandFourSided(property, value) {
29
54
  }
30
55
  /**
31
56
  * Expand `padding-inline` / `margin-inline` (logical property) into RN's
32
- * physical left / right pair. RN has no RTL-aware logical props at the
33
- * style-object level, so we lower at compile time.
57
+ * physical pair. RN has no RTL-aware logical props at the style-object
58
+ * level, so we lower at compile time. When both sides match, emit the
59
+ * single `paddingHorizontal` / `marginHorizontal` shorthand for a more
60
+ * compact style.
34
61
  * @param property `'padding'` or `'margin'`.
35
62
  * @param value Typed inline shorthand.
36
63
  * @returns RN entries.
@@ -40,6 +67,8 @@ function expandLogicalInline(property, value) {
40
67
  const end = length.lengthPercentageOrAutoToValue(value.inlineEnd);
41
68
  if (start === null || end === null)
42
69
  return [];
70
+ if (start === end)
71
+ return [[`${property}Horizontal`, start]];
43
72
  return [
44
73
  [`${property}Left`, start],
45
74
  [`${property}Right`, end],
@@ -47,7 +76,8 @@ function expandLogicalInline(property, value) {
47
76
  }
48
77
  /**
49
78
  * Expand `padding-block` / `margin-block` (logical property) into RN's
50
- * physical top / bottom pair.
79
+ * physical pair. When both sides match, emit `paddingVertical` /
80
+ * `marginVertical` for a more compact style.
51
81
  * @param property `'padding'` or `'margin'`.
52
82
  * @param value Typed block shorthand.
53
83
  * @returns RN entries.
@@ -57,6 +87,8 @@ function expandLogicalBlock(property, value) {
57
87
  const end = length.lengthPercentageOrAutoToValue(value.blockEnd);
58
88
  if (start === null || end === null)
59
89
  return [];
90
+ if (start === end)
91
+ return [[`${property}Vertical`, start]];
60
92
  return [
61
93
  [`${property}Top`, start],
62
94
  [`${property}Bottom`, end],
@@ -1 +1 @@
1
- {"version":3,"file":"shorthand.cjs","sources":["../../../../../src/core/parser/shorthand.ts"],"sourcesContent":["import type {\n BorderColor,\n BorderRadius,\n Flex,\n Gap,\n Margin,\n MarginBlock,\n MarginInline,\n Padding,\n PaddingBlock,\n PaddingInline,\n} from 'lightningcss'\nimport { cssColorToString } from './color'\nimport { dimensionPercentageToNumber, gapValueToValue, lengthPercentageOrAutoToValue } from './length'\nimport type { RNEntry } from './types'\n\n/**\n * Expand `margin` / `padding` shorthand (`{top, right, bottom, left}`) to\n * RN entries. When all four sides share the same converted value, collapse\n * to the single-key shorthand RN accepts; otherwise emit four longhands.\n * @param property `'padding'` or `'margin'`.\n * @param value Typed shorthand record.\n * @returns RN entries.\n */\nexport function expandFourSided(property: 'padding' | 'margin', value: Padding | Margin): readonly RNEntry[] {\n const top = lengthPercentageOrAutoToValue(value.top)\n const right = lengthPercentageOrAutoToValue(value.right)\n const bottom = lengthPercentageOrAutoToValue(value.bottom)\n const left = lengthPercentageOrAutoToValue(value.left)\n if (top === null || right === null || bottom === null || left === null) return []\n if (top === right && right === bottom && bottom === left) return [[property, top]]\n return [\n [`${property}Top`, top],\n [`${property}Right`, right],\n [`${property}Bottom`, bottom],\n [`${property}Left`, left],\n ]\n}\n\n/**\n * Expand `padding-inline` / `margin-inline` (logical property) into RN's\n * physical left / right pair. RN has no RTL-aware logical props at the\n * style-object level, so we lower at compile time.\n * @param property `'padding'` or `'margin'`.\n * @param value Typed inline shorthand.\n * @returns RN entries.\n */\nexport function expandLogicalInline(property: 'padding' | 'margin', value: PaddingInline | MarginInline): readonly RNEntry[] {\n const start = lengthPercentageOrAutoToValue(value.inlineStart)\n const end = lengthPercentageOrAutoToValue(value.inlineEnd)\n if (start === null || end === null) return []\n return [\n [`${property}Left`, start],\n [`${property}Right`, end],\n ]\n}\n\n/**\n * Expand `padding-block` / `margin-block` (logical property) into RN's\n * physical top / bottom pair.\n * @param property `'padding'` or `'margin'`.\n * @param value Typed block shorthand.\n * @returns RN entries.\n */\nexport function expandLogicalBlock(property: 'padding' | 'margin', value: PaddingBlock | MarginBlock): readonly RNEntry[] {\n const start = lengthPercentageOrAutoToValue(value.blockStart)\n const end = lengthPercentageOrAutoToValue(value.blockEnd)\n if (start === null || end === null) return []\n return [\n [`${property}Top`, start],\n [`${property}Bottom`, end],\n ]\n}\n\n/**\n * Expand a `border-radius` shorthand into RN corner entries. Each corner\n * is a 2-tuple `[x, y]` in lightningcss; RN exposes one radius per corner\n * so we use the x-axis. When all four corners match, collapse to the\n * single `borderRadius` key.\n * @param value Typed `BorderRadius` record.\n * @returns RN entries.\n */\nexport function expandBorderRadius(value: BorderRadius): readonly RNEntry[] {\n const corners: Array<[string, number | string | null]> = [\n ['borderTopLeftRadius', dimensionPercentageToNumber(value.topLeft[0])],\n ['borderTopRightRadius', dimensionPercentageToNumber(value.topRight[0])],\n ['borderBottomRightRadius', dimensionPercentageToNumber(value.bottomRight[0])],\n ['borderBottomLeftRadius', dimensionPercentageToNumber(value.bottomLeft[0])],\n ]\n const [first] = corners\n if (first?.[1] == null) return []\n if (corners.every(([, v]) => v === first[1])) return [['borderRadius', first[1]]]\n return corners.filter((entry): entry is [string, number | string] => entry[1] !== null)\n}\n\n/**\n * Expand a `border-color` shorthand into RN longhands. When all four\n * sides match, collapse to a single `borderColor`; otherwise emit per-side\n * props.\n * @param value Typed `BorderColor` record.\n * @returns RN entries.\n */\nexport function expandBorderColor(value: BorderColor): readonly RNEntry[] {\n const top = cssColorToString(value.top)\n const right = cssColorToString(value.right)\n const bottom = cssColorToString(value.bottom)\n const left = cssColorToString(value.left)\n if (top === right && right === bottom && bottom === left) return [['borderColor', top]]\n return [\n ['borderTopColor', top],\n ['borderRightColor', right],\n ['borderBottomColor', bottom],\n ['borderLeftColor', left],\n ]\n}\n\n/**\n * Expand `gap` shorthand (`{row, column}`) into RN entries. When both\n * axes equal the same value collapse to the single `gap` key; otherwise\n * emit `rowGap` + `columnGap`.\n * @param value Typed `Gap` record.\n * @returns RN entries.\n */\nexport function expandGap(value: Gap): readonly RNEntry[] {\n const row = gapValueToValue(value.row)\n const column = gapValueToValue(value.column)\n if (row === null || column === null) return []\n if (row === column) return [['gap', row]]\n return [\n ['rowGap', row],\n ['columnGap', column],\n ]\n}\n\n/**\n * Convert `Flex` shorthand to RN entries. When the shape matches `flex:\n * 1` (`{grow:1, shrink:1, basis: 0%}`), emit the single `flex` key RN\n * understands. Otherwise expand to the three longhands.\n * @param value Typed `Flex` record.\n * @returns RN entries.\n */\nexport function flexToEntries(value: Flex): readonly RNEntry[] {\n const basis = lengthPercentageOrAutoToValue(value.basis)\n if (basis === null) return []\n if (value.grow === 1 && value.shrink === 1 && basis === '0%') return [['flex', 1]]\n const entries: RNEntry[] = [\n ['flexGrow', value.grow],\n ['flexShrink', value.shrink],\n ]\n if (basis !== 'auto') entries.push(['flexBasis', basis])\n return entries\n}\n"],"names":["lengthPercentageOrAutoToValue","dimensionPercentageToNumber","cssColorToString","gapValueToValue"],"mappings":"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"}
1
+ {"version":3,"file":"shorthand.cjs","sources":["../../../../../src/core/parser/shorthand.ts"],"sourcesContent":["import type {\n BorderColor,\n BorderRadius,\n Flex,\n Gap,\n Margin,\n MarginBlock,\n MarginInline,\n Padding,\n PaddingBlock,\n PaddingInline,\n} from 'lightningcss'\nimport { cssColorToString } from './color'\nimport { dimensionPercentageToNumber, gapValueToValue, lengthPercentageOrAutoToValue } from './length'\nimport type { RNEntry } from './types'\n\n/**\n * Expand `margin` / `padding` shorthand (`{top, right, bottom, left}`) to\n * RN entries. Collapses progressively for a smaller emitted style:\n * - all four equal → single `padding` / `margin`\n * - matching axes → `paddingHorizontal` + `paddingVertical`\n * - one matching axis → that axis collapsed, opposite axis as longhands\n * - otherwise → four longhands\n * @param property `'padding'` or `'margin'`.\n * @param value Typed shorthand record.\n * @returns RN entries.\n */\nexport function expandFourSided(property: 'padding' | 'margin', value: Padding | Margin): readonly RNEntry[] {\n const top = lengthPercentageOrAutoToValue(value.top)\n const right = lengthPercentageOrAutoToValue(value.right)\n const bottom = lengthPercentageOrAutoToValue(value.bottom)\n const left = lengthPercentageOrAutoToValue(value.left)\n if (top === null || right === null || bottom === null || left === null) return []\n if (top === right && right === bottom && bottom === left) return [[property, top]]\n const horizontalEqual = left === right\n const verticalEqual = top === bottom\n if (horizontalEqual && verticalEqual) {\n return [\n [`${property}Vertical`, top],\n [`${property}Horizontal`, left],\n ]\n }\n if (horizontalEqual) {\n return [\n [`${property}Top`, top],\n [`${property}Bottom`, bottom],\n [`${property}Horizontal`, left],\n ]\n }\n if (verticalEqual) {\n return [\n [`${property}Vertical`, top],\n [`${property}Right`, right],\n [`${property}Left`, left],\n ]\n }\n return [\n [`${property}Top`, top],\n [`${property}Right`, right],\n [`${property}Bottom`, bottom],\n [`${property}Left`, left],\n ]\n}\n\n/**\n * Expand `padding-inline` / `margin-inline` (logical property) into RN's\n * physical pair. RN has no RTL-aware logical props at the style-object\n * level, so we lower at compile time. When both sides match, emit the\n * single `paddingHorizontal` / `marginHorizontal` shorthand for a more\n * compact style.\n * @param property `'padding'` or `'margin'`.\n * @param value Typed inline shorthand.\n * @returns RN entries.\n */\nexport function expandLogicalInline(property: 'padding' | 'margin', value: PaddingInline | MarginInline): readonly RNEntry[] {\n const start = lengthPercentageOrAutoToValue(value.inlineStart)\n const end = lengthPercentageOrAutoToValue(value.inlineEnd)\n if (start === null || end === null) return []\n if (start === end) return [[`${property}Horizontal`, start]]\n return [\n [`${property}Left`, start],\n [`${property}Right`, end],\n ]\n}\n\n/**\n * Expand `padding-block` / `margin-block` (logical property) into RN's\n * physical pair. When both sides match, emit `paddingVertical` /\n * `marginVertical` for a more compact style.\n * @param property `'padding'` or `'margin'`.\n * @param value Typed block shorthand.\n * @returns RN entries.\n */\nexport function expandLogicalBlock(property: 'padding' | 'margin', value: PaddingBlock | MarginBlock): readonly RNEntry[] {\n const start = lengthPercentageOrAutoToValue(value.blockStart)\n const end = lengthPercentageOrAutoToValue(value.blockEnd)\n if (start === null || end === null) return []\n if (start === end) return [[`${property}Vertical`, start]]\n return [\n [`${property}Top`, start],\n [`${property}Bottom`, end],\n ]\n}\n\n/**\n * Expand a `border-radius` shorthand into RN corner entries. Each corner\n * is a 2-tuple `[x, y]` in lightningcss; RN exposes one radius per corner\n * so we use the x-axis. When all four corners match, collapse to the\n * single `borderRadius` key.\n * @param value Typed `BorderRadius` record.\n * @returns RN entries.\n */\nexport function expandBorderRadius(value: BorderRadius): readonly RNEntry[] {\n const corners: Array<[string, number | string | null]> = [\n ['borderTopLeftRadius', dimensionPercentageToNumber(value.topLeft[0])],\n ['borderTopRightRadius', dimensionPercentageToNumber(value.topRight[0])],\n ['borderBottomRightRadius', dimensionPercentageToNumber(value.bottomRight[0])],\n ['borderBottomLeftRadius', dimensionPercentageToNumber(value.bottomLeft[0])],\n ]\n const [first] = corners\n if (first?.[1] == null) return []\n if (corners.every(([, v]) => v === first[1])) return [['borderRadius', first[1]]]\n return corners.filter((entry): entry is [string, number | string] => entry[1] !== null)\n}\n\n/**\n * Expand a `border-color` shorthand into RN longhands. When all four\n * sides match, collapse to a single `borderColor`; otherwise emit per-side\n * props.\n * @param value Typed `BorderColor` record.\n * @returns RN entries.\n */\nexport function expandBorderColor(value: BorderColor): readonly RNEntry[] {\n const top = cssColorToString(value.top)\n const right = cssColorToString(value.right)\n const bottom = cssColorToString(value.bottom)\n const left = cssColorToString(value.left)\n if (top === right && right === bottom && bottom === left) return [['borderColor', top]]\n return [\n ['borderTopColor', top],\n ['borderRightColor', right],\n ['borderBottomColor', bottom],\n ['borderLeftColor', left],\n ]\n}\n\n/**\n * Expand `gap` shorthand (`{row, column}`) into RN entries. When both\n * axes equal the same value collapse to the single `gap` key; otherwise\n * emit `rowGap` + `columnGap`.\n * @param value Typed `Gap` record.\n * @returns RN entries.\n */\nexport function expandGap(value: Gap): readonly RNEntry[] {\n const row = gapValueToValue(value.row)\n const column = gapValueToValue(value.column)\n if (row === null || column === null) return []\n if (row === column) return [['gap', row]]\n return [\n ['rowGap', row],\n ['columnGap', column],\n ]\n}\n\n/**\n * Convert `Flex` shorthand to RN entries. When the shape matches `flex:\n * 1` (`{grow:1, shrink:1, basis: 0%}`), emit the single `flex` key RN\n * understands. Otherwise expand to the three longhands.\n * @param value Typed `Flex` record.\n * @returns RN entries.\n */\nexport function flexToEntries(value: Flex): readonly RNEntry[] {\n const basis = lengthPercentageOrAutoToValue(value.basis)\n if (basis === null) return []\n if (value.grow === 1 && value.shrink === 1 && basis === '0%') return [['flex', 1]]\n const entries: RNEntry[] = [\n ['flexGrow', value.grow],\n ['flexShrink', value.shrink],\n ]\n if (basis !== 'auto') entries.push(['flexBasis', basis])\n return entries\n}\n"],"names":["lengthPercentageOrAutoToValue","dimensionPercentageToNumber","cssColorToString","gapValueToValue"],"mappings":"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"}
@@ -2,8 +2,11 @@ import type { BorderColor, BorderRadius, Flex, Gap, Margin, MarginBlock, MarginI
2
2
  import type { RNEntry } from './types';
3
3
  /**
4
4
  * Expand `margin` / `padding` shorthand (`{top, right, bottom, left}`) to
5
- * RN entries. When all four sides share the same converted value, collapse
6
- * to the single-key shorthand RN accepts; otherwise emit four longhands.
5
+ * RN entries. Collapses progressively for a smaller emitted style:
6
+ * - all four equal single `padding` / `margin`
7
+ * - matching axes → `paddingHorizontal` + `paddingVertical`
8
+ * - one matching axis → that axis collapsed, opposite axis as longhands
9
+ * - otherwise → four longhands
7
10
  * @param property `'padding'` or `'margin'`.
8
11
  * @param value Typed shorthand record.
9
12
  * @returns RN entries.
@@ -11,8 +14,10 @@ import type { RNEntry } from './types';
11
14
  export declare function expandFourSided(property: 'padding' | 'margin', value: Padding | Margin): readonly RNEntry[];
12
15
  /**
13
16
  * Expand `padding-inline` / `margin-inline` (logical property) into RN's
14
- * physical left / right pair. RN has no RTL-aware logical props at the
15
- * style-object level, so we lower at compile time.
17
+ * physical pair. RN has no RTL-aware logical props at the style-object
18
+ * level, so we lower at compile time. When both sides match, emit the
19
+ * single `paddingHorizontal` / `marginHorizontal` shorthand for a more
20
+ * compact style.
16
21
  * @param property `'padding'` or `'margin'`.
17
22
  * @param value Typed inline shorthand.
18
23
  * @returns RN entries.
@@ -20,7 +25,8 @@ export declare function expandFourSided(property: 'padding' | 'margin', value: P
20
25
  export declare function expandLogicalInline(property: 'padding' | 'margin', value: PaddingInline | MarginInline): readonly RNEntry[];
21
26
  /**
22
27
  * Expand `padding-block` / `margin-block` (logical property) into RN's
23
- * physical top / bottom pair.
28
+ * physical pair. When both sides match, emit `paddingVertical` /
29
+ * `marginVertical` for a more compact style.
24
30
  * @param property `'padding'` or `'margin'`.
25
31
  * @param value Typed block shorthand.
26
32
  * @returns RN entries.
@@ -378,6 +378,58 @@ function extractSchemeAliases(css) {
378
378
  }
379
379
  return aliases;
380
380
  }
381
+ /** Single class token (`.scheme-dark`) — non-global so `.test` is stateless. */
382
+ const CLASS_TOKEN = /\.[A-Za-z_][\w-]*/;
383
+ /**
384
+ * Whether a `@custom-variant` selector body targets an ancestor *class*
385
+ * container (`&:where(.scheme-dark, .scheme-dark *)`, `.dark`). That's
386
+ * the only shape rnwind resolves as a runtime scheme — the parser's
387
+ * nested-rule matcher keys schemes off `.class` selectors. Pseudo-class
388
+ * / `@media` / `@supports` custom-variants (`&:hover`, `@supports
389
+ * (display: grid)`) carry no class and are ordinary Tailwind variants,
390
+ * not schemes.
391
+ * @param selector Selector body captured after the variant name.
392
+ * @returns True when the selector contains at least one class token.
393
+ */
394
+ function isSchemeSelector(selector) {
395
+ return CLASS_TOKEN.test(selector);
396
+ }
397
+ /**
398
+ * Collect every scheme name declared via `@custom-variant <name>
399
+ * (<class-selector>);` in first-appearance order.
400
+ *
401
+ * A scheme can be declared this way WITHOUT a matching `@variant <name>
402
+ * { … }` override block — its values then come entirely from the base
403
+ * `@theme`. That's Tailwind v4's standard shape: light defaults sit in
404
+ * `@theme` and only `@variant dark { … }` overrides them. Such a scheme
405
+ * still has to register so the runtime can switch to it, but
406
+ * {@link extractThemeVars} (which only sees `@variant` blocks) never
407
+ * surfaces it. The parser unions these names into its declared-scheme
408
+ * list so the base-only scheme isn't dropped.
409
+ *
410
+ * Only class-container selectors count — `@custom-variant` is Tailwind's
411
+ * general variant mechanism, so hover / focus / media / supports
412
+ * variants must NOT inflate the scheme list (or the generated `Scheme`
413
+ * union). See {@link isSchemeSelector}.
414
+ * @param css Theme CSS source.
415
+ * @returns Ordered, de-duplicated `@custom-variant` scheme names.
416
+ */
417
+ function extractCustomVariantSchemes(css) {
418
+ const stripped = stripComments(css);
419
+ const seen = new Set();
420
+ const out = [];
421
+ for (const match of stripped.matchAll(CUSTOM_VARIANT_WITH_SELECTOR)) {
422
+ const name = match[1];
423
+ const selector = match[2];
424
+ if (name === BASE_SCHEME || seen.has(name))
425
+ continue;
426
+ if (!isSchemeSelector(selector))
427
+ continue;
428
+ seen.add(name);
429
+ out.push(name);
430
+ }
431
+ return out;
432
+ }
381
433
  /**
382
434
  * Rewrite the theme CSS so Tailwind's compiler accepts it:
383
435
  * 1. Strip every `@variant <name> { ... }` block — Tailwind rejects
@@ -409,6 +461,7 @@ function compileReadyTheme(css, table) {
409
461
 
410
462
  exports.BASE_SCHEME = BASE_SCHEME;
411
463
  exports.compileReadyTheme = compileReadyTheme;
464
+ exports.extractCustomVariantSchemes = extractCustomVariantSchemes;
412
465
  exports.extractSchemeAliases = extractSchemeAliases;
413
466
  exports.extractThemeVars = extractThemeVars;
414
467
  //# sourceMappingURL=theme-vars.cjs.map