rnwind 0.0.7 → 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 +161 -10
  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 +43 -60
  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 +162 -11
  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 +43 -60
  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 +160 -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 +45 -61
  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
@@ -1,5 +1,5 @@
1
1
  import { kebabToCamel } from './case-convert.mjs';
2
- import { cssColorToString } from './color.mjs';
2
+ import { cssColorToString, normalizeColorString } from './color.mjs';
3
3
  import { lengthPercentageOrAutoToValue, dimensionPercentageToNumber, gapValueToValue, sizeLikeToValue } from './length.mjs';
4
4
  import { flexToEntries, expandBorderRadius, expandLogicalBlock, expandLogicalInline, expandFourSided, expandGap, expandBorderColor } from './shorthand.mjs';
5
5
  import { serializeTokens, substituteThemeVars, coerceUnparsedValue, coerceFontFamily } from './tokens.mjs';
@@ -24,6 +24,71 @@ const UNSUPPORTED_LOGICAL_PROPS = new Set([
24
24
  'border-block-start-style',
25
25
  'border-block-end-style',
26
26
  ]);
27
+ /**
28
+ * Web-only CSS properties Tailwind v4 emits that have NO React Native style
29
+ * equivalent. Without this denylist they reach the generic `kebabToCamel`
30
+ * fallback and emit dead keys (`objectPosition`, `textWrap`, `willChange`,
31
+ * `float`, `columns`, `-webkit-line-clamp` → `WebkitLineClamp`, …) that bloat
32
+ * every StyleSheet and read as "supported" when they do nothing. Dropping the
33
+ * property name (kebab-case, pre-camel) is safe: it only excludes known
34
+ * web-only props — anything RN supports is handled by a typed branch above.
35
+ * (line-clamp's real RN behaviour comes from `numberOfLines` in text-truncate.)
36
+ */
37
+ const RN_UNSUPPORTED_PROPERTIES = new Set([
38
+ 'object-position',
39
+ 'text-wrap',
40
+ 'will-change',
41
+ 'columns',
42
+ 'float',
43
+ 'clear',
44
+ 'table-layout',
45
+ 'caption-side',
46
+ 'transform-style',
47
+ 'background-blend-mode',
48
+ 'scroll-behavior',
49
+ 'overscroll-behavior',
50
+ 'overscroll-behavior-x',
51
+ 'overscroll-behavior-y',
52
+ 'scroll-snap-type',
53
+ 'scroll-snap-align',
54
+ 'scroll-snap-stop',
55
+ 'break-after',
56
+ 'break-before',
57
+ 'break-inside',
58
+ 'content',
59
+ 'field-sizing',
60
+ 'forced-color-adjust',
61
+ 'text-shadow',
62
+ 'touch-action',
63
+ 'backdrop-filter',
64
+ '-webkit-backdrop-filter',
65
+ '-webkit-line-clamp',
66
+ '-webkit-box-orient',
67
+ '-webkit-font-smoothing',
68
+ '-moz-osx-font-smoothing',
69
+ ]);
70
+ /** CSS single-sided logical-inline property → RN writing-direction Yoga key. */
71
+ const LOGICAL_INLINE_TO_RN = {
72
+ 'margin-inline-start': 'marginStart',
73
+ 'margin-inline-end': 'marginEnd',
74
+ 'padding-inline-start': 'paddingStart',
75
+ 'padding-inline-end': 'paddingEnd',
76
+ };
77
+ /**
78
+ * Logical border-COLOR property → physical RN side key(s). Custom `@theme`
79
+ * tokens reach the unparsed path as `border-inline-color: var(--color-x)`,
80
+ * which a plain `kebabToCamel` would turn into `borderInlineColor` — a key RN
81
+ * silently drops, so the border color never paints. Lower to the physical
82
+ * keys RN actually honors, matching the typed `dispatchBorderDeclaration`.
83
+ */
84
+ const LOGICAL_BORDER_COLOR_SIDES = {
85
+ 'border-inline-color': ['borderLeftColor', 'borderRightColor'],
86
+ 'border-block-color': ['borderTopColor', 'borderBottomColor'],
87
+ 'border-inline-start-color': ['borderLeftColor'],
88
+ 'border-inline-end-color': ['borderRightColor'],
89
+ 'border-block-start-color': ['borderTopColor'],
90
+ 'border-block-end-color': ['borderBottomColor'],
91
+ };
27
92
  /**
28
93
  * Pick the closest predefined CSS easing keyword for a `cubic-bezier`
29
94
  * control-point set. Mirrors {@link snapCubicBezierToKeyword} in
@@ -73,6 +138,25 @@ function coerceCubicBezierString(value) {
73
138
  const [, x1, y1, x2, y2] = match;
74
139
  return snapBezier(Number(x1), Number(y1), Number(x2), Number(y2));
75
140
  }
141
+ /**
142
+ * Whether `text` has a whitespace char OUTSIDE any parenthesised group —
143
+ * the signature of a multi-token CSS value (`2px solid #000`) rather than a
144
+ * single color (`#000`, `rgb(1 2 3)`, `red`).
145
+ * @param text Resolved value text.
146
+ * @returns True when a top-level space is present.
147
+ */
148
+ function hasTopLevelSpace(text) {
149
+ let depth = 0;
150
+ for (const ch of text.trim()) {
151
+ if (ch === '(')
152
+ depth += 1;
153
+ else if (ch === ')')
154
+ depth = Math.max(0, depth - 1);
155
+ else if (depth === 0 && (ch === ' ' || ch === '\t' || ch === '\n'))
156
+ return true;
157
+ }
158
+ return false;
159
+ }
76
160
  /**
77
161
  * Fast-path check for the handful of color property names Tailwind emits.
78
162
  * @param property Kebab-case CSS property name.
@@ -81,6 +165,11 @@ function coerceCubicBezierString(value) {
81
165
  function isColorProperty(property) {
82
166
  return (property === 'color' ||
83
167
  property === 'background-color' ||
168
+ // SVG paint props (`fill-<token>` / `stroke-<token>` via react-native-svg) —
169
+ // they don't end in `-color`, so without this they'd skip normalization and
170
+ // leak a raw `oklch(…)` string for custom `@theme` tokens.
171
+ property === 'fill' ||
172
+ property === 'stroke' ||
84
173
  (property.startsWith('border-') && property.endsWith('-color')) ||
85
174
  property.endsWith('-color'));
86
175
  }
@@ -97,6 +186,8 @@ function isColorProperty(property) {
97
186
  function unparsedToEntries(property, tokens, themeVars) {
98
187
  if (property.length === 0)
99
188
  return [];
189
+ if (RN_UNSUPPORTED_PROPERTIES.has(property))
190
+ return [];
100
191
  // Safe-area detection runs BEFORE token serialization because
101
192
  // `env()` serializes to an empty string, which would strip the side
102
193
  // info we need. If the tokens encode a recognised `env(safe-area-inset-*)`
@@ -113,12 +204,15 @@ function unparsedToEntries(property, tokens, themeVars) {
113
204
  const coerced = coerceUnparsedValue(text);
114
205
  if (coerced === null)
115
206
  return [];
116
- // Skip values that didn't resolve past their `var()` wrapper they
117
- // came from a `@property --tw-*` token without a real fallback.
118
- // Tailwind v4's `border-N` emits `border-style: var(--tw-border-style)`
119
- // expecting the cascade to fill it in; in RN we drop them and rely on
120
- // RN's default (solid).
121
- if (typeof coerced === 'string' && coerced.startsWith('var('))
207
+ // Skip values still carrying an unresolved `var(--tw-*)` ANYWHERE in the
208
+ // string — they came from a `@property --tw-*` composable with no real
209
+ // fallback (e.g. `filter: blur(8px) var(--tw-brightness) …`,
210
+ // `transform: rotateX(45deg) var(--tw-rotate-y) …`, `touch-action`,
211
+ // `scroll-snap-type`). RN can't evaluate the cascade, so a leaked `var()`
212
+ // makes the whole declaration an invalid string RN rejects — drop it and
213
+ // rely on RN's default rather than emit garbage. `var(--color-*)` refs are
214
+ // already substituted above, so anything left is a genuine composable miss.
215
+ if (typeof coerced === 'string' && coerced.includes('var('))
122
216
  return [];
123
217
  // RN `fontFamily` is a single typeface, not a CSS fallback list — take
124
218
  // the first family so `--font-x: "Name", sans-serif` works out of the box.
@@ -137,9 +231,23 @@ function unparsedToEntries(property, tokens, themeVars) {
137
231
  return [[kebabToCamel(property), coerceCubicBezierString(coerced)]];
138
232
  }
139
233
  if (isColorProperty(property) && typeof coerced === 'string') {
140
- // Resolved user-theme color strings (e.g. `#ff0099`) go straight to
141
- // the RN style no further conversion needed.
142
- return [[kebabToCamel(property), coerced]];
234
+ // A color is a single token. Tailwind compiles an arbitrary shorthand like
235
+ // `border-[2px_solid_#000]` to `border-color: 2px solid #000` (invalid for
236
+ // a color property → unparsed), which would otherwise emit
237
+ // `borderColor: "2px solid #000000"` — a string RN rejects. A top-level
238
+ // space (outside parens — `rgb(1 2 3)` keeps its inner spaces) means it's a
239
+ // multi-token shorthand, not a color: drop it.
240
+ if (hasTopLevelSpace(coerced))
241
+ return [];
242
+ // Lower modern color spaces (`oklch(…)`, `lab(…)`, `color(p3 …)`) that
243
+ // RN can't paint to sRGB; hex/rgb/hsl/named pass through unchanged.
244
+ const color = normalizeColorString(coerced) ?? coerced;
245
+ // Logical border-color utilities must lower to physical RN side keys —
246
+ // RN ignores `borderInlineColor` / `borderInlineStartColor`.
247
+ const sides = LOGICAL_BORDER_COLOR_SIDES[property];
248
+ if (sides)
249
+ return sides.map((key) => [key, color]);
250
+ return [[kebabToCamel(property), color]];
143
251
  }
144
252
  return [[kebabToCamel(property), coerced]];
145
253
  }
@@ -336,7 +444,8 @@ function declarationToRnEntries(decl, themeVars) {
336
444
  return [['flexBasis', v]];
337
445
  }
338
446
  default: {
339
- return (dispatchLayoutDeclaration(decl) ??
447
+ return (dispatchLogicalInline(decl) ??
448
+ dispatchLayoutDeclaration(decl) ??
340
449
  dispatchTypographyDeclaration(decl) ??
341
450
  dispatchColorPropertyDeclaration(decl) ??
342
451
  dispatchBorderDeclaration(decl) ??
@@ -345,6 +454,48 @@ function declarationToRnEntries(decl, themeVars) {
345
454
  }
346
455
  }
347
456
  }
457
+ /**
458
+ * Map single-sided CSS logical-inline props to RN's writing-direction-aware
459
+ * Yoga keys: `ms-2` → `marginStart`, `pe-4` → `paddingEnd`, `start-2` →
460
+ * `start`, `end-3` → `end`. RN resolves start/end against the layout
461
+ * direction, so these stay RTL-correct. Returns null for any other property
462
+ * (so the dispatch chain continues).
463
+ * @param decl One declaration from a lightningcss style rule.
464
+ * @returns RN entries, or null when not a logical-inline property.
465
+ */
466
+ function dispatchLogicalInline(decl) {
467
+ switch (decl.property) {
468
+ case 'margin-inline-start':
469
+ case 'margin-inline-end':
470
+ case 'padding-inline-start':
471
+ case 'padding-inline-end': {
472
+ const v = lengthPercentageOrAutoToValue(decl.value);
473
+ return v === null ? [] : [[LOGICAL_INLINE_TO_RN[decl.property], v]];
474
+ }
475
+ case 'inset-inline-start': {
476
+ const v = lengthPercentageOrAutoToValue(decl.value);
477
+ return v === null ? [] : [['start', v]];
478
+ }
479
+ case 'inset-inline-end': {
480
+ const v = lengthPercentageOrAutoToValue(decl.value);
481
+ return v === null ? [] : [['end', v]];
482
+ }
483
+ // Logical border-radius corners (`rounded-s/e/ss/se/ee/es-*`). RN has
484
+ // matching keys — `kebabToCamel('border-start-start-radius')` is exactly
485
+ // `borderStartStartRadius`. Value is a `[x, y]` tuple like physical corners.
486
+ case 'border-start-start-radius':
487
+ case 'border-start-end-radius':
488
+ case 'border-end-start-radius':
489
+ case 'border-end-end-radius': {
490
+ const [xAxis] = decl.value;
491
+ const v = dimensionPercentageToNumber(xAxis);
492
+ return v === null ? [] : [[kebabToCamel(decl.property), v]];
493
+ }
494
+ default: {
495
+ return null;
496
+ }
497
+ }
498
+ }
348
499
 
349
500
  export { declarationToRnEntries };
350
501
  //# sourceMappingURL=declaration.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"declaration.mjs","sources":["../../../../../src/core/parser/declaration.ts"],"sourcesContent":["/* eslint-disable sonarjs/cognitive-complexity -- the main Declaration → RN-entries dispatcher is intentionally a flat switch so each branch keeps its narrowed value type */\nimport type { Declaration as LcDeclaration, TokenOrValue } from 'lightningcss'\nimport { kebabToCamel } from './case-convert'\nimport { cssColorToString } from './color'\nimport { dimensionPercentageToNumber, gapValueToValue, lengthPercentageOrAutoToValue, sizeLikeToValue } from './length'\nimport {\n expandBorderColor,\n expandBorderRadius,\n expandFourSided,\n expandGap,\n expandLogicalBlock,\n expandLogicalInline,\n flexToEntries,\n} from './shorthand'\nimport { coerceFontFamily, coerceUnparsedValue, serializeTokens, substituteThemeVars } from './tokens'\nimport { displayToEntries, fontSizeToPx, fontWeightToValue, zIndexToNumber } from './typography'\nimport { dispatchMotionDeclaration } from './motion-dispatcher'\nimport { dispatchTypographyDeclaration } from './typography-dispatcher'\nimport { dispatchLayoutDeclaration } from './layout-dispatcher'\nimport { dispatchColorPropertyDeclaration } from './color-properties-dispatcher'\nimport { dispatchBorderDeclaration } from './border-dispatcher'\nimport { detectSafeAreaMarker } from './safe-area'\nimport type { RNEntry } from './types'\n\n/** CSS timing-function properties that need `cubic-bezier(...)` snapping. */\nconst TIMING_FUNCTION_PROPS = new Set(['transition-timing-function', 'animation-timing-function'])\n\n/** CSS easing keywords Reanimated v4's CSS engine accepts as strings. */\nconst CSS_EASING_KEYWORDS = new Set(['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end'])\n\n/** CSS properties with no useful RN equivalent — silently dropped. */\nconst UNSUPPORTED_LOGICAL_PROPS = new Set([\n 'border-inline-style',\n 'border-block-style',\n 'border-inline-start-style',\n 'border-inline-end-style',\n 'border-block-start-style',\n 'border-block-end-style',\n])\n\n/**\n * Pick the closest predefined CSS easing keyword for a `cubic-bezier`\n * control-point set. Mirrors {@link snapCubicBezierToKeyword} in\n * `animation.ts` — kept here so the unparsed-string path doesn't need\n * to import a typed-only helper.\n * @param x1 First control-point x (0–1).\n * @param y1 First control-point y (0–1).\n * @param x2 Second control-point x (0–1).\n * @param y2 Second control-point y (0–1).\n * @returns CSS easing keyword.\n */\nfunction snapBezier(x1: number, y1: number, x2: number, y2: number): string {\n const tol = 0.01\n const eq = (a: number, b: number): boolean => Math.abs(a - b) < tol\n if (eq(x1, 0) && eq(y1, 0) && eq(x2, 1) && eq(y2, 1)) return 'linear'\n if (eq(x1, 0.25) && eq(y1, 0.1) && eq(x2, 0.25) && eq(y2, 1)) return 'ease'\n if (eq(x1, 0.4) && eq(y1, 0) && eq(x2, 1) && eq(y2, 1)) return 'ease-in'\n if (eq(x1, 0) && eq(y1, 0) && eq(x2, 0.2) && eq(y2, 1)) return 'ease-out'\n if (eq(x1, 0.4) && eq(y1, 0) && eq(x2, 0.2) && eq(y2, 1)) return 'ease-in-out'\n const startsFlat = x1 < 0.1\n const endsFlat = x2 > 0.9\n if (startsFlat && !endsFlat) return 'ease-out'\n if (!startsFlat && endsFlat) return 'ease-in'\n return 'ease-in-out'\n}\n\n/**\n * Snap a cubic-bezier expression string to the closest CSS keyword\n * Reanimated v4's CSS engine accepts. Strings that already are keywords\n * pass through unchanged.\n * @param value Resolved value text from an unparsed timing-function declaration.\n * @returns CSS easing keyword.\n */\nfunction coerceCubicBezierString(value: string): string {\n const text = value.trim()\n if (CSS_EASING_KEYWORDS.has(text)) return text\n const match = /^cubic-bezier\\(\\s*([\\d.-]+)\\s*,\\s*([\\d.-]+)\\s*,\\s*([\\d.-]+)\\s*,\\s*([\\d.-]+)\\s*\\)$/.exec(text)\n if (!match) return 'ease-in-out'\n const [, x1, y1, x2, y2] = match\n return snapBezier(Number(x1), Number(y1), Number(x2), Number(y2))\n}\n\n/**\n * Fast-path check for the handful of color property names Tailwind emits.\n * @param property Kebab-case CSS property name.\n * @returns Whether the property's value should be treated as a color.\n */\nfunction isColorProperty(property: string): boolean {\n return (\n property === 'color' ||\n property === 'background-color' ||\n (property.startsWith('border-') && property.endsWith('-color')) ||\n property.endsWith('-color')\n )\n}\n\n/**\n * Convert an unparsed declaration (typical Tailwind v4 output containing\n * `var()` / `calc()`) into RN entries. Serializes the token list, then\n * coerces the flat string into a number / keyword / length via a tight\n * set of shapes Tailwind actually emits.\n * @param property Real property name (kebab-case).\n * @param tokens Token list from lightningcss.\n * @param themeVars Optional lookup table for resolving `var(--x)` references.\n * @returns RN entries — usually one, empty when unusable.\n */\nfunction unparsedToEntries(\n property: string,\n tokens: readonly TokenOrValue[],\n themeVars: ReadonlyMap<string, string> | undefined,\n): readonly RNEntry[] {\n if (property.length === 0) return []\n // Safe-area detection runs BEFORE token serialization because\n // `env()` serializes to an empty string, which would strip the side\n // info we need. If the tokens encode a recognised `env(safe-area-inset-*)`\n // pattern (pure / `max(..., n)` / `calc(...+n)` / `h-screen-safe`),\n // emit a runtime-resolved marker instead.\n if (UNSUPPORTED_LOGICAL_PROPS.has(property)) return []\n const safe = detectSafeAreaMarker(tokens, themeVars)\n if (safe !== null) return [[kebabToCamel(property), safe]]\n let text = serializeTokens(tokens)\n if (themeVars && themeVars.size > 0) text = substituteThemeVars(text, themeVars)\n const coerced = coerceUnparsedValue(text)\n if (coerced === null) return []\n // Skip values that didn't resolve past their `var()` wrapper — they\n // came from a `@property --tw-*` token without a real fallback.\n // Tailwind v4's `border-N` emits `border-style: var(--tw-border-style)`\n // expecting the cascade to fill it in; in RN we drop them and rely on\n // RN's default (solid).\n if (typeof coerced === 'string' && coerced.startsWith('var(')) return []\n // RN `fontFamily` is a single typeface, not a CSS fallback list — take\n // the first family so `--font-x: \"Name\", sans-serif` works out of the box.\n if (property === 'font-family' && typeof coerced === 'string') {\n return [['fontFamily', coerceFontFamily(coerced)]]\n }\n // Logical-direction CSS properties RN doesn't have direct equivalents\n // for. Keep the dropped names in one place so it's easy to audit.\n if (UNSUPPORTED_LOGICAL_PROPS.has(property)) return []\n if (TIMING_FUNCTION_PROPS.has(property) && typeof coerced === 'string') {\n // `transition-colors` and similar emit `var(--tw-ease, cubic-bezier(...))`\n // which serializes to a cubic-bezier STRING after substitution.\n // Reanimated v4's CSS engine rejects those — snap to the closest\n // predefined keyword (same logic as the typed `EasingFunction` path).\n return [[kebabToCamel(property), coerceCubicBezierString(coerced)]]\n }\n if (isColorProperty(property) && typeof coerced === 'string') {\n // Resolved user-theme color strings (e.g. `#ff0099`) go straight to\n // the RN style — no further conversion needed.\n return [[kebabToCamel(property), coerced]]\n }\n return [[kebabToCamel(property), coerced]]\n}\n\n/**\n * Convert one lightningcss `Declaration` into zero-or-more RN style\n * entries. Shorthand declarations (padding/margin/border-radius/flex) can\n * emit multiple entries; skipped or unsupported properties emit none.\n *\n * The switch branches on `decl.property` so TypeScript narrows\n * `decl.value` to the exact typed shape for each branch — no casts\n * required. Unknown properties fall through to `[]`.\n * @param decl One declaration from a lightningcss style rule.\n * @param themeVars Optional lookup table for resolving `var(--x)` references inside unparsed values.\n * @returns Array of `[key, value]` entries.\n */\nexport function declarationToRnEntries(decl: LcDeclaration, themeVars?: ReadonlyMap<string, string>): readonly RNEntry[] {\n switch (decl.property) {\n case 'custom': {\n // Lightningcss routes two shapes through `custom`:\n // - Actual CSS custom properties (`--my-var`): no RN meaning, drop.\n // - Real properties it doesn't have a dedicated typed entry for\n // (e.g. `object-fit`, `pointer-events`, future CSS keyword-only\n // props): treat like an unparsed declaration so the keyword\n // surfaces in the RN style.\n const customName = decl.value.name\n if (customName.startsWith('--')) return []\n return unparsedToEntries(customName, decl.value.value ?? [], themeVars)\n }\n case 'unparsed': {\n return unparsedToEntries(decl.value.propertyId.property, decl.value.value, themeVars)\n }\n case 'color':\n case 'background-color':\n case 'border-top-color':\n case 'border-right-color':\n case 'border-bottom-color':\n case 'border-left-color': {\n // `background-color` narrows to `CssColor | 'background'` — the\n // literal keyword means UA default. Skip the keyword.\n if (typeof decl.value === 'string') return []\n return [[kebabToCamel(decl.property), cssColorToString(decl.value)]]\n }\n case 'border-color': {\n return expandBorderColor(decl.value)\n }\n case 'opacity': {\n // Lightningcss hands us an `f32` for opacity, so values like `0.8`\n // round-trip as `0.800000011920929`. Snap to 4 decimals to match the\n // rest of the parser's numeric convention.\n return [[decl.property, Math.round(decl.value * 10_000) / 10_000]]\n }\n case 'z-index': {\n return [['zIndex', zIndexToNumber(decl.value)]]\n }\n case 'top':\n case 'right':\n case 'bottom':\n case 'left': {\n const v = lengthPercentageOrAutoToValue(decl.value)\n if (v === null) return []\n return [[decl.property, v]]\n }\n case 'inset': {\n const top = lengthPercentageOrAutoToValue(decl.value.top)\n const right = lengthPercentageOrAutoToValue(decl.value.right)\n const bottom = lengthPercentageOrAutoToValue(decl.value.bottom)\n const left = lengthPercentageOrAutoToValue(decl.value.left)\n if (top === null || right === null || bottom === null || left === null) return []\n return [\n ['top', top],\n ['right', right],\n ['bottom', bottom],\n ['left', left],\n ]\n }\n case 'inset-inline': {\n const start = lengthPercentageOrAutoToValue(decl.value.inlineStart)\n const end = lengthPercentageOrAutoToValue(decl.value.inlineEnd)\n if (start === null || end === null) return []\n return [\n ['left', start],\n ['right', end],\n ]\n }\n case 'inset-block': {\n const start = lengthPercentageOrAutoToValue(decl.value.blockStart)\n const end = lengthPercentageOrAutoToValue(decl.value.blockEnd)\n if (start === null || end === null) return []\n return [\n ['top', start],\n ['bottom', end],\n ]\n }\n case 'width':\n case 'height':\n case 'min-width':\n case 'min-height':\n case 'max-width':\n case 'max-height': {\n const v = sizeLikeToValue(decl.value)\n if (v === null) return []\n return [[kebabToCamel(decl.property), v]]\n }\n case 'gap': {\n return expandGap(decl.value)\n }\n case 'row-gap':\n case 'column-gap': {\n const v = gapValueToValue(decl.value)\n if (v === null) return []\n return [[kebabToCamel(decl.property), v]]\n }\n case 'font-style': {\n return [['fontStyle', decl.value.type]]\n }\n case 'display': {\n return displayToEntries(decl.value)\n }\n case 'position': {\n return [['position', decl.value.type]]\n }\n case 'font-size': {\n const px = fontSizeToPx(decl.value)\n if (px === null) return []\n return [['fontSize', px]]\n }\n case 'font-weight': {\n return [['fontWeight', fontWeightToValue(decl.value)]]\n }\n case 'padding': {\n return expandFourSided('padding', decl.value)\n }\n case 'margin': {\n return expandFourSided('margin', decl.value)\n }\n case 'padding-inline': {\n return expandLogicalInline('padding', decl.value)\n }\n case 'padding-block': {\n return expandLogicalBlock('padding', decl.value)\n }\n case 'margin-inline': {\n return expandLogicalInline('margin', decl.value)\n }\n case 'margin-block': {\n return expandLogicalBlock('margin', decl.value)\n }\n case 'padding-top':\n case 'padding-right':\n case 'padding-bottom':\n case 'padding-left':\n case 'margin-top':\n case 'margin-right':\n case 'margin-bottom':\n case 'margin-left': {\n const v = lengthPercentageOrAutoToValue(decl.value)\n if (v === null) return []\n return [[kebabToCamel(decl.property), v]]\n }\n case 'border-radius': {\n return expandBorderRadius(decl.value)\n }\n case 'border-top-left-radius':\n case 'border-top-right-radius':\n case 'border-bottom-left-radius':\n case 'border-bottom-right-radius': {\n const [xAxis] = decl.value\n const v = dimensionPercentageToNumber(xAxis)\n if (v === null) return []\n return [[kebabToCamel(decl.property), v]]\n }\n case 'flex': {\n return flexToEntries(decl.value)\n }\n case 'flex-grow':\n case 'flex-shrink': {\n return [[kebabToCamel(decl.property), decl.value]]\n }\n case 'flex-basis': {\n const v = lengthPercentageOrAutoToValue(decl.value)\n if (v === null) return []\n return [['flexBasis', v]]\n }\n default: {\n return (\n dispatchLayoutDeclaration(decl) ??\n dispatchTypographyDeclaration(decl) ??\n dispatchColorPropertyDeclaration(decl) ??\n dispatchBorderDeclaration(decl) ??\n dispatchMotionDeclaration(decl) ??\n []\n )\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"declaration.mjs","sources":["../../../../../src/core/parser/declaration.ts"],"sourcesContent":["/* eslint-disable sonarjs/cognitive-complexity -- the main Declaration → RN-entries dispatcher is intentionally a flat switch so each branch keeps its narrowed value type */\nimport type { Declaration as LcDeclaration, TokenOrValue } from 'lightningcss'\nimport { kebabToCamel } from './case-convert'\nimport { cssColorToString, normalizeColorString } from './color'\nimport { dimensionPercentageToNumber, gapValueToValue, lengthPercentageOrAutoToValue, sizeLikeToValue } from './length'\nimport {\n expandBorderColor,\n expandBorderRadius,\n expandFourSided,\n expandGap,\n expandLogicalBlock,\n expandLogicalInline,\n flexToEntries,\n} from './shorthand'\nimport { coerceFontFamily, coerceUnparsedValue, serializeTokens, substituteThemeVars } from './tokens'\nimport { displayToEntries, fontSizeToPx, fontWeightToValue, zIndexToNumber } from './typography'\nimport { dispatchMotionDeclaration } from './motion-dispatcher'\nimport { dispatchTypographyDeclaration } from './typography-dispatcher'\nimport { dispatchLayoutDeclaration } from './layout-dispatcher'\nimport { dispatchColorPropertyDeclaration } from './color-properties-dispatcher'\nimport { dispatchBorderDeclaration } from './border-dispatcher'\nimport { detectSafeAreaMarker } from './safe-area'\nimport type { RNEntry } from './types'\n\n/** CSS timing-function properties that need `cubic-bezier(...)` snapping. */\nconst TIMING_FUNCTION_PROPS = new Set(['transition-timing-function', 'animation-timing-function'])\n\n/** CSS easing keywords Reanimated v4's CSS engine accepts as strings. */\nconst CSS_EASING_KEYWORDS = new Set(['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end'])\n\n/** CSS properties with no useful RN equivalent — silently dropped. */\nconst UNSUPPORTED_LOGICAL_PROPS = new Set([\n 'border-inline-style',\n 'border-block-style',\n 'border-inline-start-style',\n 'border-inline-end-style',\n 'border-block-start-style',\n 'border-block-end-style',\n])\n\n/**\n * Web-only CSS properties Tailwind v4 emits that have NO React Native style\n * equivalent. Without this denylist they reach the generic `kebabToCamel`\n * fallback and emit dead keys (`objectPosition`, `textWrap`, `willChange`,\n * `float`, `columns`, `-webkit-line-clamp` → `WebkitLineClamp`, …) that bloat\n * every StyleSheet and read as \"supported\" when they do nothing. Dropping the\n * property name (kebab-case, pre-camel) is safe: it only excludes known\n * web-only props — anything RN supports is handled by a typed branch above.\n * (line-clamp's real RN behaviour comes from `numberOfLines` in text-truncate.)\n */\nconst RN_UNSUPPORTED_PROPERTIES: ReadonlySet<string> = new Set([\n 'object-position',\n 'text-wrap',\n 'will-change',\n 'columns',\n 'float',\n 'clear',\n 'table-layout',\n 'caption-side',\n 'transform-style',\n 'background-blend-mode',\n 'scroll-behavior',\n 'overscroll-behavior',\n 'overscroll-behavior-x',\n 'overscroll-behavior-y',\n 'scroll-snap-type',\n 'scroll-snap-align',\n 'scroll-snap-stop',\n 'break-after',\n 'break-before',\n 'break-inside',\n 'content',\n 'field-sizing',\n 'forced-color-adjust',\n 'text-shadow',\n 'touch-action',\n 'backdrop-filter',\n '-webkit-backdrop-filter',\n '-webkit-line-clamp',\n '-webkit-box-orient',\n '-webkit-font-smoothing',\n '-moz-osx-font-smoothing',\n])\n\n/** CSS single-sided logical-inline property → RN writing-direction Yoga key. */\nconst LOGICAL_INLINE_TO_RN: Record<string, string> = {\n 'margin-inline-start': 'marginStart',\n 'margin-inline-end': 'marginEnd',\n 'padding-inline-start': 'paddingStart',\n 'padding-inline-end': 'paddingEnd',\n}\n\n/**\n * Logical border-COLOR property → physical RN side key(s). Custom `@theme`\n * tokens reach the unparsed path as `border-inline-color: var(--color-x)`,\n * which a plain `kebabToCamel` would turn into `borderInlineColor` — a key RN\n * silently drops, so the border color never paints. Lower to the physical\n * keys RN actually honors, matching the typed `dispatchBorderDeclaration`.\n */\nconst LOGICAL_BORDER_COLOR_SIDES: Record<string, readonly string[]> = {\n 'border-inline-color': ['borderLeftColor', 'borderRightColor'],\n 'border-block-color': ['borderTopColor', 'borderBottomColor'],\n 'border-inline-start-color': ['borderLeftColor'],\n 'border-inline-end-color': ['borderRightColor'],\n 'border-block-start-color': ['borderTopColor'],\n 'border-block-end-color': ['borderBottomColor'],\n}\n\n/**\n * Pick the closest predefined CSS easing keyword for a `cubic-bezier`\n * control-point set. Mirrors {@link snapCubicBezierToKeyword} in\n * `animation.ts` — kept here so the unparsed-string path doesn't need\n * to import a typed-only helper.\n * @param x1 First control-point x (0–1).\n * @param y1 First control-point y (0–1).\n * @param x2 Second control-point x (0–1).\n * @param y2 Second control-point y (0–1).\n * @returns CSS easing keyword.\n */\nfunction snapBezier(x1: number, y1: number, x2: number, y2: number): string {\n const tol = 0.01\n const eq = (a: number, b: number): boolean => Math.abs(a - b) < tol\n if (eq(x1, 0) && eq(y1, 0) && eq(x2, 1) && eq(y2, 1)) return 'linear'\n if (eq(x1, 0.25) && eq(y1, 0.1) && eq(x2, 0.25) && eq(y2, 1)) return 'ease'\n if (eq(x1, 0.4) && eq(y1, 0) && eq(x2, 1) && eq(y2, 1)) return 'ease-in'\n if (eq(x1, 0) && eq(y1, 0) && eq(x2, 0.2) && eq(y2, 1)) return 'ease-out'\n if (eq(x1, 0.4) && eq(y1, 0) && eq(x2, 0.2) && eq(y2, 1)) return 'ease-in-out'\n const startsFlat = x1 < 0.1\n const endsFlat = x2 > 0.9\n if (startsFlat && !endsFlat) return 'ease-out'\n if (!startsFlat && endsFlat) return 'ease-in'\n return 'ease-in-out'\n}\n\n/**\n * Snap a cubic-bezier expression string to the closest CSS keyword\n * Reanimated v4's CSS engine accepts. Strings that already are keywords\n * pass through unchanged.\n * @param value Resolved value text from an unparsed timing-function declaration.\n * @returns CSS easing keyword.\n */\nfunction coerceCubicBezierString(value: string): string {\n const text = value.trim()\n if (CSS_EASING_KEYWORDS.has(text)) return text\n const match = /^cubic-bezier\\(\\s*([\\d.-]+)\\s*,\\s*([\\d.-]+)\\s*,\\s*([\\d.-]+)\\s*,\\s*([\\d.-]+)\\s*\\)$/.exec(text)\n if (!match) return 'ease-in-out'\n const [, x1, y1, x2, y2] = match\n return snapBezier(Number(x1), Number(y1), Number(x2), Number(y2))\n}\n\n/**\n * Whether `text` has a whitespace char OUTSIDE any parenthesised group —\n * the signature of a multi-token CSS value (`2px solid #000`) rather than a\n * single color (`#000`, `rgb(1 2 3)`, `red`).\n * @param text Resolved value text.\n * @returns True when a top-level space is present.\n */\nfunction hasTopLevelSpace(text: string): boolean {\n let depth = 0\n for (const ch of text.trim()) {\n if (ch === '(') depth += 1\n else if (ch === ')') depth = Math.max(0, depth - 1)\n else if (depth === 0 && (ch === ' ' || ch === '\\t' || ch === '\\n')) return true\n }\n return false\n}\n\n/**\n * Fast-path check for the handful of color property names Tailwind emits.\n * @param property Kebab-case CSS property name.\n * @returns Whether the property's value should be treated as a color.\n */\nfunction isColorProperty(property: string): boolean {\n return (\n property === 'color' ||\n property === 'background-color' ||\n // SVG paint props (`fill-<token>` / `stroke-<token>` via react-native-svg) —\n // they don't end in `-color`, so without this they'd skip normalization and\n // leak a raw `oklch(…)` string for custom `@theme` tokens.\n property === 'fill' ||\n property === 'stroke' ||\n (property.startsWith('border-') && property.endsWith('-color')) ||\n property.endsWith('-color')\n )\n}\n\n/**\n * Convert an unparsed declaration (typical Tailwind v4 output containing\n * `var()` / `calc()`) into RN entries. Serializes the token list, then\n * coerces the flat string into a number / keyword / length via a tight\n * set of shapes Tailwind actually emits.\n * @param property Real property name (kebab-case).\n * @param tokens Token list from lightningcss.\n * @param themeVars Optional lookup table for resolving `var(--x)` references.\n * @returns RN entries — usually one, empty when unusable.\n */\nfunction unparsedToEntries(\n property: string,\n tokens: readonly TokenOrValue[],\n themeVars: ReadonlyMap<string, string> | undefined,\n): readonly RNEntry[] {\n if (property.length === 0) return []\n if (RN_UNSUPPORTED_PROPERTIES.has(property)) return []\n // Safe-area detection runs BEFORE token serialization because\n // `env()` serializes to an empty string, which would strip the side\n // info we need. If the tokens encode a recognised `env(safe-area-inset-*)`\n // pattern (pure / `max(..., n)` / `calc(...+n)` / `h-screen-safe`),\n // emit a runtime-resolved marker instead.\n if (UNSUPPORTED_LOGICAL_PROPS.has(property)) return []\n const safe = detectSafeAreaMarker(tokens, themeVars)\n if (safe !== null) return [[kebabToCamel(property), safe]]\n let text = serializeTokens(tokens)\n if (themeVars && themeVars.size > 0) text = substituteThemeVars(text, themeVars)\n const coerced = coerceUnparsedValue(text)\n if (coerced === null) return []\n // Skip values still carrying an unresolved `var(--tw-*)` ANYWHERE in the\n // string — they came from a `@property --tw-*` composable with no real\n // fallback (e.g. `filter: blur(8px) var(--tw-brightness) …`,\n // `transform: rotateX(45deg) var(--tw-rotate-y) …`, `touch-action`,\n // `scroll-snap-type`). RN can't evaluate the cascade, so a leaked `var()`\n // makes the whole declaration an invalid string RN rejects — drop it and\n // rely on RN's default rather than emit garbage. `var(--color-*)` refs are\n // already substituted above, so anything left is a genuine composable miss.\n if (typeof coerced === 'string' && coerced.includes('var(')) return []\n // RN `fontFamily` is a single typeface, not a CSS fallback list — take\n // the first family so `--font-x: \"Name\", sans-serif` works out of the box.\n if (property === 'font-family' && typeof coerced === 'string') {\n return [['fontFamily', coerceFontFamily(coerced)]]\n }\n // Logical-direction CSS properties RN doesn't have direct equivalents\n // for. Keep the dropped names in one place so it's easy to audit.\n if (UNSUPPORTED_LOGICAL_PROPS.has(property)) return []\n if (TIMING_FUNCTION_PROPS.has(property) && typeof coerced === 'string') {\n // `transition-colors` and similar emit `var(--tw-ease, cubic-bezier(...))`\n // which serializes to a cubic-bezier STRING after substitution.\n // Reanimated v4's CSS engine rejects those — snap to the closest\n // predefined keyword (same logic as the typed `EasingFunction` path).\n return [[kebabToCamel(property), coerceCubicBezierString(coerced)]]\n }\n if (isColorProperty(property) && typeof coerced === 'string') {\n // A color is a single token. Tailwind compiles an arbitrary shorthand like\n // `border-[2px_solid_#000]` to `border-color: 2px solid #000` (invalid for\n // a color property → unparsed), which would otherwise emit\n // `borderColor: \"2px solid #000000\"` — a string RN rejects. A top-level\n // space (outside parens — `rgb(1 2 3)` keeps its inner spaces) means it's a\n // multi-token shorthand, not a color: drop it.\n if (hasTopLevelSpace(coerced)) return []\n // Lower modern color spaces (`oklch(…)`, `lab(…)`, `color(p3 …)`) that\n // RN can't paint to sRGB; hex/rgb/hsl/named pass through unchanged.\n const color = normalizeColorString(coerced) ?? coerced\n // Logical border-color utilities must lower to physical RN side keys —\n // RN ignores `borderInlineColor` / `borderInlineStartColor`.\n const sides = LOGICAL_BORDER_COLOR_SIDES[property]\n if (sides) return sides.map((key): RNEntry => [key, color])\n return [[kebabToCamel(property), color]]\n }\n return [[kebabToCamel(property), coerced]]\n}\n\n/**\n * Convert one lightningcss `Declaration` into zero-or-more RN style\n * entries. Shorthand declarations (padding/margin/border-radius/flex) can\n * emit multiple entries; skipped or unsupported properties emit none.\n *\n * The switch branches on `decl.property` so TypeScript narrows\n * `decl.value` to the exact typed shape for each branch — no casts\n * required. Unknown properties fall through to `[]`.\n * @param decl One declaration from a lightningcss style rule.\n * @param themeVars Optional lookup table for resolving `var(--x)` references inside unparsed values.\n * @returns Array of `[key, value]` entries.\n */\nexport function declarationToRnEntries(decl: LcDeclaration, themeVars?: ReadonlyMap<string, string>): readonly RNEntry[] {\n switch (decl.property) {\n case 'custom': {\n // Lightningcss routes two shapes through `custom`:\n // - Actual CSS custom properties (`--my-var`): no RN meaning, drop.\n // - Real properties it doesn't have a dedicated typed entry for\n // (e.g. `object-fit`, `pointer-events`, future CSS keyword-only\n // props): treat like an unparsed declaration so the keyword\n // surfaces in the RN style.\n const customName = decl.value.name\n if (customName.startsWith('--')) return []\n return unparsedToEntries(customName, decl.value.value ?? [], themeVars)\n }\n case 'unparsed': {\n return unparsedToEntries(decl.value.propertyId.property, decl.value.value, themeVars)\n }\n case 'color':\n case 'background-color':\n case 'border-top-color':\n case 'border-right-color':\n case 'border-bottom-color':\n case 'border-left-color': {\n // `background-color` narrows to `CssColor | 'background'` — the\n // literal keyword means UA default. Skip the keyword.\n if (typeof decl.value === 'string') return []\n return [[kebabToCamel(decl.property), cssColorToString(decl.value)]]\n }\n case 'border-color': {\n return expandBorderColor(decl.value)\n }\n case 'opacity': {\n // Lightningcss hands us an `f32` for opacity, so values like `0.8`\n // round-trip as `0.800000011920929`. Snap to 4 decimals to match the\n // rest of the parser's numeric convention.\n return [[decl.property, Math.round(decl.value * 10_000) / 10_000]]\n }\n case 'z-index': {\n return [['zIndex', zIndexToNumber(decl.value)]]\n }\n case 'top':\n case 'right':\n case 'bottom':\n case 'left': {\n const v = lengthPercentageOrAutoToValue(decl.value)\n if (v === null) return []\n return [[decl.property, v]]\n }\n case 'inset': {\n const top = lengthPercentageOrAutoToValue(decl.value.top)\n const right = lengthPercentageOrAutoToValue(decl.value.right)\n const bottom = lengthPercentageOrAutoToValue(decl.value.bottom)\n const left = lengthPercentageOrAutoToValue(decl.value.left)\n if (top === null || right === null || bottom === null || left === null) return []\n return [\n ['top', top],\n ['right', right],\n ['bottom', bottom],\n ['left', left],\n ]\n }\n case 'inset-inline': {\n const start = lengthPercentageOrAutoToValue(decl.value.inlineStart)\n const end = lengthPercentageOrAutoToValue(decl.value.inlineEnd)\n if (start === null || end === null) return []\n return [\n ['left', start],\n ['right', end],\n ]\n }\n case 'inset-block': {\n const start = lengthPercentageOrAutoToValue(decl.value.blockStart)\n const end = lengthPercentageOrAutoToValue(decl.value.blockEnd)\n if (start === null || end === null) return []\n return [\n ['top', start],\n ['bottom', end],\n ]\n }\n case 'width':\n case 'height':\n case 'min-width':\n case 'min-height':\n case 'max-width':\n case 'max-height': {\n const v = sizeLikeToValue(decl.value)\n if (v === null) return []\n return [[kebabToCamel(decl.property), v]]\n }\n case 'gap': {\n return expandGap(decl.value)\n }\n case 'row-gap':\n case 'column-gap': {\n const v = gapValueToValue(decl.value)\n if (v === null) return []\n return [[kebabToCamel(decl.property), v]]\n }\n case 'font-style': {\n return [['fontStyle', decl.value.type]]\n }\n case 'display': {\n return displayToEntries(decl.value)\n }\n case 'position': {\n return [['position', decl.value.type]]\n }\n case 'font-size': {\n const px = fontSizeToPx(decl.value)\n if (px === null) return []\n return [['fontSize', px]]\n }\n case 'font-weight': {\n return [['fontWeight', fontWeightToValue(decl.value)]]\n }\n case 'padding': {\n return expandFourSided('padding', decl.value)\n }\n case 'margin': {\n return expandFourSided('margin', decl.value)\n }\n case 'padding-inline': {\n return expandLogicalInline('padding', decl.value)\n }\n case 'padding-block': {\n return expandLogicalBlock('padding', decl.value)\n }\n case 'margin-inline': {\n return expandLogicalInline('margin', decl.value)\n }\n case 'margin-block': {\n return expandLogicalBlock('margin', decl.value)\n }\n case 'padding-top':\n case 'padding-right':\n case 'padding-bottom':\n case 'padding-left':\n case 'margin-top':\n case 'margin-right':\n case 'margin-bottom':\n case 'margin-left': {\n const v = lengthPercentageOrAutoToValue(decl.value)\n if (v === null) return []\n return [[kebabToCamel(decl.property), v]]\n }\n case 'border-radius': {\n return expandBorderRadius(decl.value)\n }\n case 'border-top-left-radius':\n case 'border-top-right-radius':\n case 'border-bottom-left-radius':\n case 'border-bottom-right-radius': {\n const [xAxis] = decl.value\n const v = dimensionPercentageToNumber(xAxis)\n if (v === null) return []\n return [[kebabToCamel(decl.property), v]]\n }\n case 'flex': {\n return flexToEntries(decl.value)\n }\n case 'flex-grow':\n case 'flex-shrink': {\n return [[kebabToCamel(decl.property), decl.value]]\n }\n case 'flex-basis': {\n const v = lengthPercentageOrAutoToValue(decl.value)\n if (v === null) return []\n return [['flexBasis', v]]\n }\n default: {\n return (\n dispatchLogicalInline(decl) ??\n dispatchLayoutDeclaration(decl) ??\n dispatchTypographyDeclaration(decl) ??\n dispatchColorPropertyDeclaration(decl) ??\n dispatchBorderDeclaration(decl) ??\n dispatchMotionDeclaration(decl) ??\n []\n )\n }\n }\n}\n\n/**\n * Map single-sided CSS logical-inline props to RN's writing-direction-aware\n * Yoga keys: `ms-2` → `marginStart`, `pe-4` → `paddingEnd`, `start-2` →\n * `start`, `end-3` → `end`. RN resolves start/end against the layout\n * direction, so these stay RTL-correct. Returns null for any other property\n * (so the dispatch chain continues).\n * @param decl One declaration from a lightningcss style rule.\n * @returns RN entries, or null when not a logical-inline property.\n */\nfunction dispatchLogicalInline(decl: LcDeclaration): readonly RNEntry[] | null {\n switch (decl.property) {\n case 'margin-inline-start':\n case 'margin-inline-end':\n case 'padding-inline-start':\n case 'padding-inline-end': {\n const v = lengthPercentageOrAutoToValue(decl.value)\n return v === null ? [] : [[LOGICAL_INLINE_TO_RN[decl.property], v]]\n }\n case 'inset-inline-start': {\n const v = lengthPercentageOrAutoToValue(decl.value)\n return v === null ? [] : [['start', v]]\n }\n case 'inset-inline-end': {\n const v = lengthPercentageOrAutoToValue(decl.value)\n return v === null ? [] : [['end', v]]\n }\n // Logical border-radius corners (`rounded-s/e/ss/se/ee/es-*`). RN has\n // matching keys — `kebabToCamel('border-start-start-radius')` is exactly\n // `borderStartStartRadius`. Value is a `[x, y]` tuple like physical corners.\n case 'border-start-start-radius':\n case 'border-start-end-radius':\n case 'border-end-start-radius':\n case 'border-end-end-radius': {\n const [xAxis] = decl.value\n const v = dimensionPercentageToNumber(xAxis)\n return v === null ? [] : [[kebabToCamel(decl.property), v]]\n }\n default: {\n return null\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -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 };
@@ -1,4 +1,6 @@
1
- import { cssColorToString } from './color.mjs';
1
+ import { formatHex } from 'culori';
2
+ import { cssColorToString, normalizeColorString } from './color.mjs';
3
+ import { serializeTokens, substituteThemeVars } from './tokens.mjs';
2
4
 
3
5
  /**
4
6
  * Gradient-atom extractor.
@@ -28,9 +30,10 @@ import { cssColorToString } from './color.mjs';
28
30
  * return the atom's role + data. Returns `null` for rules that don't
29
31
  * belong to a gradient utility.
30
32
  * @param declarations Declarations from one lightningcss style rule.
33
+ * @param themeVars
31
34
  * @returns Gradient info, or null.
32
35
  */
33
- function detectGradientAtom(declarations) {
36
+ function detectGradientAtom(declarations, themeVars) {
34
37
  for (const decl of declarations) {
35
38
  if (decl.property !== 'custom')
36
39
  continue;
@@ -39,26 +42,50 @@ function detectGradientAtom(declarations) {
39
42
  if (!name.startsWith('--tw-gradient-'))
40
43
  continue;
41
44
  if (name === '--tw-gradient-from')
42
- return fromColor('from', custom.value);
45
+ return fromColor('from', custom.value, themeVars);
43
46
  if (name === '--tw-gradient-via')
44
- return fromColor('via', custom.value);
47
+ return fromColor('via', custom.value, themeVars);
45
48
  if (name === '--tw-gradient-to')
46
- return fromColor('to', custom.value);
49
+ return fromColor('to', custom.value, themeVars);
47
50
  if (name === '--tw-gradient-position')
48
51
  return fromDirection(custom.value);
49
52
  }
50
53
  return null;
51
54
  }
52
55
  /**
53
- * Extract a single color token from a custom-property value list and
54
- * return the `{role, color}` record. Returns `null` when no color token
55
- * is present (defensive Tailwind always emits one, but future output
56
- * shapes may not).
56
+ * Lower a resolved gradient-stop color string to one RN's `LinearGradient`
57
+ * `colors=` array accepts. Hex/rgb/hsl pass through; modern spaces
58
+ * (`oklch(…)`, `lab(…)`, `color(p3 …)`) and named colors go through culori.
59
+ * @param text Resolved color text (post theme-var substitution).
60
+ * @returns sRGB color string, or null when unparseable.
61
+ */
62
+ function resolveGradientColor(text) {
63
+ if (text.startsWith('#') || text.startsWith('rgb') || text.startsWith('hsl'))
64
+ return text;
65
+ const normalized = normalizeColorString(text);
66
+ if (normalized)
67
+ return normalized;
68
+ try {
69
+ const hex = formatHex(text);
70
+ return typeof hex === 'string' ? hex : null;
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ /**
77
+ * Extract a stop color from a `--tw-gradient-*` value list. The default
78
+ * Tailwind palette is inlined to a typed `color` token; CUSTOM `@theme`
79
+ * tokens arrive as an unresolved `var(--color-x)` token list, so when no
80
+ * typed color is present we serialize, substitute `themeVars`, and lower
81
+ * the result to sRGB — without this, `from-<token>` / `via-<token>` /
82
+ * `to-<token>` dropped the stop entirely.
57
83
  * @param role Target role (`from` / `via` / `to`).
58
84
  * @param tokens Value tokens from the `--tw-gradient-*` declaration.
85
+ * @param themeVars Per-scheme var table for resolving `var(--color-x)`.
59
86
  * @returns Gradient info, or null.
60
87
  */
61
- function fromColor(role, tokens) {
88
+ function fromColor(role, tokens, themeVars) {
62
89
  if (!tokens)
63
90
  return null;
64
91
  for (const token of tokens) {
@@ -69,7 +96,14 @@ function fromColor(role, tokens) {
69
96
  return null;
70
97
  return { role, color };
71
98
  }
72
- return null;
99
+ // Custom @theme token: value is `var(--color-x)` — serialize + resolve.
100
+ let text = serializeTokens(tokens).trim();
101
+ if (themeVars && themeVars.size > 0)
102
+ text = substituteThemeVars(text, themeVars);
103
+ if (text.length === 0 || text.startsWith('var('))
104
+ return null;
105
+ const color = resolveGradientColor(text);
106
+ return color ? { role, color } : null;
73
107
  }
74
108
  /**
75
109
  * Interpret a `--tw-gradient-position` value list as a direction tag.
@@ -1 +1 @@
1
- {"version":3,"file":"gradient.mjs","sources":["../../../../../src/core/parser/gradient.ts"],"sourcesContent":["/**\n * Gradient-atom extractor.\n *\n * Tailwind v4's gradient system is encoded as a set of CSS custom\n * properties Tailwind writes onto specific utility class rules:\n *\n * .from-red-500 → `--tw-gradient-from: #ef4444`\n * .via-green-500 → `--tw-gradient-via: #22c55e`\n * .to-blue-500 → `--tw-gradient-to: #3b82f6`\n * .bg-gradient-to-r → `--tw-gradient-position: to right`\n * .bg-linear-to-br → `--tw-gradient-position: to bottom right`\n *\n * None of these set properties React Native can render (`backgroundImage:\n * linear-gradient(...)` is web-only). Instead, rnwind treats them as\n * **metadata**: the transformer strips gradient atoms from the JSX\n * site's className and emits `colors={[...]} start={...} end={...}`\n * props on the original component (user supplies `<LinearGradient>`\n * from `expo-linear-gradient` or similar).\n *\n * This module walks a lightningcss declaration list, notices the\n * `--tw-gradient-*` writes, and surfaces them as a compact\n * {@link GradientAtomInfo} record the transformer can read per atom.\n */\n\nimport type { Declaration as LcDeclaration, TokenOrValue } from 'lightningcss'\nimport { cssColorToString } from './color'\n\n/**\n * The four roles an atom can play in a Tailwind v4 gradient. `from`,\n * `via`, `to` carry a color. `direction` carries one of the 8 stock\n * points (or a bare angle when Tailwind v4's `bg-linear-[angle]`\n * syntax is used).\n */\nexport type GradientAtomInfo =\n | { readonly role: 'from'; readonly color: string }\n | { readonly role: 'via'; readonly color: string }\n | { readonly role: 'to'; readonly color: string }\n | { readonly role: 'direction'; readonly dir: GradientDirection }\n\n/**\n * Eight stock corner directions Tailwind v4 ships. The transformer\n * maps each to a `(start, end)` pair of unit-square points the\n * expo-linear-gradient API expects. Unknown directions are surfaced as\n * `'unknown'` so the transformer can skip them gracefully.\n */\nexport type GradientDirection =\n | 'to-r'\n | 'to-l'\n | 'to-t'\n | 'to-b'\n | 'to-tr'\n | 'to-tl'\n | 'to-br'\n | 'to-bl'\n | 'unknown'\n\n/**\n * Inspect a rule's declaration list for `--tw-gradient-*` writes and\n * return the atom's role + data. Returns `null` for rules that don't\n * belong to a gradient utility.\n * @param declarations Declarations from one lightningcss style rule.\n * @returns Gradient info, or null.\n */\nfunction detectGradientAtom(declarations: readonly LcDeclaration[]): GradientAtomInfo | null {\n for (const decl of declarations) {\n if (decl.property !== 'custom') continue\n const custom = decl.value as { name: { name: string } | string; value?: readonly TokenOrValue[] }\n const name = typeof custom.name === 'string' ? custom.name : custom.name.name\n if (!name.startsWith('--tw-gradient-')) continue\n if (name === '--tw-gradient-from') return fromColor('from', custom.value)\n if (name === '--tw-gradient-via') return fromColor('via', custom.value)\n if (name === '--tw-gradient-to') return fromColor('to', custom.value)\n if (name === '--tw-gradient-position') return fromDirection(custom.value)\n }\n return null\n}\n\n/**\n * Extract a single color token from a custom-property value list and\n * return the `{role, color}` record. Returns `null` when no color token\n * is present (defensive — Tailwind always emits one, but future output\n * shapes may not).\n * @param role Target role (`from` / `via` / `to`).\n * @param tokens Value tokens from the `--tw-gradient-*` declaration.\n * @returns Gradient info, or null.\n */\nfunction fromColor(role: 'from' | 'via' | 'to', tokens: readonly TokenOrValue[] | undefined): GradientAtomInfo | null {\n if (!tokens) return null\n for (const token of tokens) {\n if (token.type !== 'color') continue\n const color = cssColorToString(token.value as never)\n if (!color) return null\n return { role, color } as GradientAtomInfo\n }\n return null\n}\n\n/**\n * Interpret a `--tw-gradient-position` value list as a direction tag.\n * Tailwind emits plain idents (`to`, `right`, `bottom`, …) for the\n * 8 stock corners.\n * @param tokens Value tokens from `--tw-gradient-position`.\n * @returns Direction record, or null when unrecognised.\n */\nfunction fromDirection(tokens: readonly TokenOrValue[] | undefined): GradientAtomInfo | null {\n if (!tokens) return null\n const idents: string[] = []\n for (const token of tokens) {\n if (token.type !== 'token') continue\n if (token.value.type === 'ident') idents.push(token.value.value as string)\n }\n const dir = directionFromIdents(idents)\n if (!dir) return null\n return { role: 'direction', dir }\n}\n\n/** Table of Tailwind-space direction idents → compact rnwind tag. */\nconst DIRECTION_TABLE: Record<string, GradientDirection> = {\n right: 'to-r',\n left: 'to-l',\n top: 'to-t',\n bottom: 'to-b',\n 'top-right': 'to-tr',\n 'top-left': 'to-tl',\n 'bottom-right': 'to-br',\n 'bottom-left': 'to-bl',\n}\n\n/**\n * Collapse the ident list Tailwind emits (`['to', 'right']`,\n * `['to', 'bottom', 'right']`, etc.) into the compact tag the\n * transformer hoists.\n * @param idents Ident tokens from the declaration value.\n * @returns One of the eight stock directions, `'unknown'`, or null.\n */\nfunction directionFromIdents(idents: readonly string[]): GradientDirection | null {\n if (idents.length === 0) return null\n if (idents[0] !== 'to') return 'unknown'\n // Tailwind v4 appends `in <colourspace>` for colour interpolation\n // (`to right in oklab`). Strip everything from the `in` keyword on\n // — it's irrelevant for the point mapping.\n const inIndex = idents.indexOf('in')\n const rest = idents.slice(1, inIndex === -1 ? idents.length : inIndex)\n if (rest.length === 0 || rest.length > 2) return 'unknown'\n return DIRECTION_TABLE[rest.join('-')] ?? 'unknown'\n}\n\nexport { detectGradientAtom }\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gradient.mjs","sources":["../../../../../src/core/parser/gradient.ts"],"sourcesContent":["/**\n * Gradient-atom extractor.\n *\n * Tailwind v4's gradient system is encoded as a set of CSS custom\n * properties Tailwind writes onto specific utility class rules:\n *\n * .from-red-500 → `--tw-gradient-from: #ef4444`\n * .via-green-500 → `--tw-gradient-via: #22c55e`\n * .to-blue-500 → `--tw-gradient-to: #3b82f6`\n * .bg-gradient-to-r → `--tw-gradient-position: to right`\n * .bg-linear-to-br → `--tw-gradient-position: to bottom right`\n *\n * None of these set properties React Native can render (`backgroundImage:\n * linear-gradient(...)` is web-only). Instead, rnwind treats them as\n * **metadata**: the transformer strips gradient atoms from the JSX\n * site's className and emits `colors={[...]} start={...} end={...}`\n * props on the original component (user supplies `<LinearGradient>`\n * from `expo-linear-gradient` or similar).\n *\n * This module walks a lightningcss declaration list, notices the\n * `--tw-gradient-*` writes, and surfaces them as a compact\n * {@link GradientAtomInfo} record the transformer can read per atom.\n */\n\nimport { formatHex as culoriFormatHex } from 'culori'\nimport type { Declaration as LcDeclaration, TokenOrValue } from 'lightningcss'\nimport { cssColorToString, normalizeColorString } from './color'\nimport { serializeTokens, substituteThemeVars } from './tokens'\n\n/**\n * The four roles an atom can play in a Tailwind v4 gradient. `from`,\n * `via`, `to` carry a color. `direction` carries one of the 8 stock\n * points (or a bare angle when Tailwind v4's `bg-linear-[angle]`\n * syntax is used).\n */\nexport type GradientAtomInfo =\n | { readonly role: 'from'; readonly color: string }\n | { readonly role: 'via'; readonly color: string }\n | { readonly role: 'to'; readonly color: string }\n | { readonly role: 'direction'; readonly dir: GradientDirection }\n\n/**\n * Eight stock corner directions Tailwind v4 ships. The transformer\n * maps each to a `(start, end)` pair of unit-square points the\n * expo-linear-gradient API expects. Unknown directions are surfaced as\n * `'unknown'` so the transformer can skip them gracefully.\n */\nexport type GradientDirection =\n | 'to-r'\n | 'to-l'\n | 'to-t'\n | 'to-b'\n | 'to-tr'\n | 'to-tl'\n | 'to-br'\n | 'to-bl'\n | 'unknown'\n\n/**\n * Inspect a rule's declaration list for `--tw-gradient-*` writes and\n * return the atom's role + data. Returns `null` for rules that don't\n * belong to a gradient utility.\n * @param declarations Declarations from one lightningcss style rule.\n * @param themeVars\n * @returns Gradient info, or null.\n */\nfunction detectGradientAtom(\n declarations: readonly LcDeclaration[],\n themeVars?: ReadonlyMap<string, string>,\n): GradientAtomInfo | null {\n for (const decl of declarations) {\n if (decl.property !== 'custom') continue\n const custom = decl.value as { name: { name: string } | string; value?: readonly TokenOrValue[] }\n const name = typeof custom.name === 'string' ? custom.name : custom.name.name\n if (!name.startsWith('--tw-gradient-')) continue\n if (name === '--tw-gradient-from') return fromColor('from', custom.value, themeVars)\n if (name === '--tw-gradient-via') return fromColor('via', custom.value, themeVars)\n if (name === '--tw-gradient-to') return fromColor('to', custom.value, themeVars)\n if (name === '--tw-gradient-position') return fromDirection(custom.value)\n }\n return null\n}\n\n/**\n * Lower a resolved gradient-stop color string to one RN's `LinearGradient`\n * `colors=` array accepts. Hex/rgb/hsl pass through; modern spaces\n * (`oklch(…)`, `lab(…)`, `color(p3 …)`) and named colors go through culori.\n * @param text Resolved color text (post theme-var substitution).\n * @returns sRGB color string, or null when unparseable.\n */\nfunction resolveGradientColor(text: string): string | null {\n if (text.startsWith('#') || text.startsWith('rgb') || text.startsWith('hsl')) return text\n const normalized = normalizeColorString(text)\n if (normalized) return normalized\n try {\n const hex = culoriFormatHex(text)\n return typeof hex === 'string' ? hex : null\n } catch {\n return null\n }\n}\n\n/**\n * Extract a stop color from a `--tw-gradient-*` value list. The default\n * Tailwind palette is inlined to a typed `color` token; CUSTOM `@theme`\n * tokens arrive as an unresolved `var(--color-x)` token list, so when no\n * typed color is present we serialize, substitute `themeVars`, and lower\n * the result to sRGB — without this, `from-<token>` / `via-<token>` /\n * `to-<token>` dropped the stop entirely.\n * @param role Target role (`from` / `via` / `to`).\n * @param tokens Value tokens from the `--tw-gradient-*` declaration.\n * @param themeVars Per-scheme var table for resolving `var(--color-x)`.\n * @returns Gradient info, or null.\n */\nfunction fromColor(\n role: 'from' | 'via' | 'to',\n tokens: readonly TokenOrValue[] | undefined,\n themeVars?: ReadonlyMap<string, string>,\n): GradientAtomInfo | null {\n if (!tokens) return null\n for (const token of tokens) {\n if (token.type !== 'color') continue\n const color = cssColorToString(token.value as never)\n if (!color) return null\n return { role, color } as GradientAtomInfo\n }\n // Custom @theme token: value is `var(--color-x)` — serialize + resolve.\n let text = serializeTokens(tokens).trim()\n if (themeVars && themeVars.size > 0) text = substituteThemeVars(text, themeVars)\n if (text.length === 0 || text.startsWith('var(')) return null\n const color = resolveGradientColor(text)\n return color ? ({ role, color } as GradientAtomInfo) : null\n}\n\n/**\n * Interpret a `--tw-gradient-position` value list as a direction tag.\n * Tailwind emits plain idents (`to`, `right`, `bottom`, …) for the\n * 8 stock corners.\n * @param tokens Value tokens from `--tw-gradient-position`.\n * @returns Direction record, or null when unrecognised.\n */\nfunction fromDirection(tokens: readonly TokenOrValue[] | undefined): GradientAtomInfo | null {\n if (!tokens) return null\n const idents: string[] = []\n for (const token of tokens) {\n if (token.type !== 'token') continue\n if (token.value.type === 'ident') idents.push(token.value.value as string)\n }\n const dir = directionFromIdents(idents)\n if (!dir) return null\n return { role: 'direction', dir }\n}\n\n/** Table of Tailwind-space direction idents → compact rnwind tag. */\nconst DIRECTION_TABLE: Record<string, GradientDirection> = {\n right: 'to-r',\n left: 'to-l',\n top: 'to-t',\n bottom: 'to-b',\n 'top-right': 'to-tr',\n 'top-left': 'to-tl',\n 'bottom-right': 'to-br',\n 'bottom-left': 'to-bl',\n}\n\n/**\n * Collapse the ident list Tailwind emits (`['to', 'right']`,\n * `['to', 'bottom', 'right']`, etc.) into the compact tag the\n * transformer hoists.\n * @param idents Ident tokens from the declaration value.\n * @returns One of the eight stock directions, `'unknown'`, or null.\n */\nfunction directionFromIdents(idents: readonly string[]): GradientDirection | null {\n if (idents.length === 0) return null\n if (idents[0] !== 'to') return 'unknown'\n // Tailwind v4 appends `in <colourspace>` for colour interpolation\n // (`to right in oklab`). Strip everything from the `in` keyword on\n // — it's irrelevant for the point mapping.\n const inIndex = idents.indexOf('in')\n const rest = idents.slice(1, inIndex === -1 ? idents.length : inIndex)\n if (rest.length === 0 || rest.length > 2) return 'unknown'\n return DIRECTION_TABLE[rest.join('-')] ?? 'unknown'\n}\n\nexport { detectGradientAtom }\n"],"names":["culoriFormatHex"],"mappings":"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"}
@@ -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.
@@ -35,17 +35,13 @@ function keyframesName(raw) {
35
35
  return raw.value;
36
36
  }
37
37
  /**
38
- * Render a keyframe step's selector list back to CSS-text (`'from'`,
39
- * `'to'`, or `'50%'`). Timeline-range keyframe selectors (CSS Scroll /
40
- * View Timelines) can't run in React Native — those steps are skipped.
41
- * @param selectors Step selectors.
42
- * @returns Step offset, or `null` when unrepresentable in RN.
38
+ * Render ONE keyframe selector to its CSS offset text, or `null` when it's a
39
+ * timeline-range selector RN can't run.
40
+ * @param selector A single keyframe selector.
41
+ * @returns Offset text (`'from'` / `'to'` / `'50%'`), or `null`.
43
42
  */
44
- function keyframeSelectorOffset(selectors) {
45
- const [head] = selectors;
46
- if (!head)
47
- return null;
48
- switch (head.type) {
43
+ function offsetForSelector(selector) {
44
+ switch (selector.type) {
49
45
  case 'from': {
50
46
  return 'from';
51
47
  }
@@ -53,13 +49,32 @@ function keyframeSelectorOffset(selectors) {
53
49
  return 'to';
54
50
  }
55
51
  case 'percentage': {
56
- return `${head.value * 100}%`;
52
+ return `${selector.value * 100}%`;
57
53
  }
58
54
  default: {
59
55
  return null;
60
56
  }
61
57
  }
62
58
  }
59
+ /**
60
+ * Render EVERY representable offset in a frame's selector list. Tailwind
61
+ * collapses shared steps into one frame with multiple selectors — `animate-ping`
62
+ * emits `75%, 100% { … }`, `animate-bounce` emits `0%, 100% { … }`. Reading only
63
+ * the first selector (the old singular helper) dropped the terminal `100%`, so
64
+ * the looping animation never defined its end/return-to-start state. Returns all
65
+ * offsets the frame's style applies to; timeline-range selectors are skipped.
66
+ * @param selectors Step selectors for one frame.
67
+ * @returns Every representable offset (may be empty).
68
+ */
69
+ function keyframeSelectorOffsets(selectors) {
70
+ const offsets = [];
71
+ for (const selector of selectors) {
72
+ const offset = offsetForSelector(selector);
73
+ if (offset !== null)
74
+ offsets.push(offset);
75
+ }
76
+ return offsets;
77
+ }
63
78
  /**
64
79
  * Extract the referenced `@keyframes` name from a declaration whose
65
80
  * property is `animation-name` or a shorthand `animation` that names one.
@@ -87,5 +102,5 @@ function pickAnimationName(decl) {
87
102
  return null;
88
103
  }
89
104
 
90
- export { keyframeSelectorOffset, keyframesName, pickAnimationName };
105
+ export { keyframeSelectorOffsets, keyframesName, pickAnimationName };
91
106
  //# sourceMappingURL=keyframes.mjs.map