rnwind 0.0.1 → 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 (330) hide show
  1. package/lib/cjs/core/parser/animation.cjs +427 -0
  2. package/lib/cjs/core/parser/animation.cjs.map +1 -0
  3. package/lib/cjs/core/parser/animation.d.ts +126 -0
  4. package/lib/cjs/core/parser/border-dispatcher.cjs +180 -0
  5. package/lib/cjs/core/parser/border-dispatcher.cjs.map +1 -0
  6. package/lib/cjs/core/parser/border-dispatcher.d.ts +15 -0
  7. package/lib/cjs/core/parser/case-convert.cjs +15 -0
  8. package/lib/cjs/core/parser/case-convert.cjs.map +1 -0
  9. package/lib/cjs/core/parser/case-convert.d.ts +6 -0
  10. package/lib/cjs/core/parser/color-properties-dispatcher.cjs +84 -0
  11. package/lib/cjs/core/parser/color-properties-dispatcher.cjs.map +1 -0
  12. package/lib/cjs/core/parser/color-properties-dispatcher.d.ts +19 -0
  13. package/lib/cjs/core/parser/color.cjs +193 -0
  14. package/lib/cjs/core/parser/color.cjs.map +1 -0
  15. package/lib/cjs/core/parser/color.d.ts +12 -0
  16. package/lib/cjs/core/parser/constants.cjs +21 -0
  17. package/lib/cjs/core/parser/constants.cjs.map +1 -0
  18. package/lib/cjs/core/parser/constants.d.ts +8 -0
  19. package/lib/cjs/core/parser/declaration.cjs +347 -0
  20. package/lib/cjs/core/parser/declaration.cjs.map +1 -0
  21. package/lib/cjs/core/parser/declaration.d.ts +15 -0
  22. package/lib/cjs/core/parser/gradient.cjs +132 -0
  23. package/lib/cjs/core/parser/gradient.cjs.map +1 -0
  24. package/lib/cjs/core/parser/gradient.d.ts +59 -0
  25. package/lib/cjs/core/parser/haptics.cjs +73 -0
  26. package/lib/cjs/core/parser/haptics.cjs.map +1 -0
  27. package/lib/cjs/core/parser/haptics.d.ts +47 -0
  28. package/lib/cjs/core/parser/index.d.ts +8 -0
  29. package/lib/cjs/core/parser/keyframes.cjs +95 -0
  30. package/lib/cjs/core/parser/keyframes.cjs.map +1 -0
  31. package/lib/cjs/core/parser/keyframes.d.ts +26 -0
  32. package/lib/cjs/core/parser/layout-dispatcher.cjs +120 -0
  33. package/lib/cjs/core/parser/layout-dispatcher.cjs.map +1 -0
  34. package/lib/cjs/core/parser/layout-dispatcher.d.ts +14 -0
  35. package/lib/cjs/core/parser/length.cjs +110 -0
  36. package/lib/cjs/core/parser/length.cjs.map +1 -0
  37. package/lib/cjs/core/parser/length.d.ts +51 -0
  38. package/lib/cjs/core/parser/motion-dispatcher.cjs +77 -0
  39. package/lib/cjs/core/parser/motion-dispatcher.cjs.map +1 -0
  40. package/lib/cjs/core/parser/motion-dispatcher.d.ts +11 -0
  41. package/lib/cjs/core/parser/property.cjs +22 -0
  42. package/lib/cjs/core/parser/property.cjs.map +1 -0
  43. package/lib/cjs/core/parser/property.d.ts +8 -0
  44. package/lib/cjs/core/parser/safe-area.cjs +404 -0
  45. package/lib/cjs/core/parser/safe-area.cjs.map +1 -0
  46. package/lib/cjs/core/parser/safe-area.d.ts +39 -0
  47. package/lib/cjs/core/parser/selector.cjs +22 -0
  48. package/lib/cjs/core/parser/selector.cjs.map +1 -0
  49. package/lib/cjs/core/parser/selector.d.ts +11 -0
  50. package/lib/cjs/core/parser/shorthand.cjs +188 -0
  51. package/lib/cjs/core/parser/shorthand.cjs.map +1 -0
  52. package/lib/cjs/core/parser/shorthand.d.ts +67 -0
  53. package/lib/cjs/core/parser/text-truncate.cjs +78 -0
  54. package/lib/cjs/core/parser/text-truncate.cjs.map +1 -0
  55. package/lib/cjs/core/parser/text-truncate.d.ts +44 -0
  56. package/lib/cjs/core/parser/theme-vars.cjs +467 -0
  57. package/lib/cjs/core/parser/theme-vars.cjs.map +1 -0
  58. package/lib/cjs/core/parser/theme-vars.d.ts +82 -0
  59. package/lib/cjs/core/parser/tokens.cjs +486 -0
  60. package/lib/cjs/core/parser/tokens.cjs.map +1 -0
  61. package/lib/cjs/core/parser/tokens.d.ts +45 -0
  62. package/lib/cjs/core/parser/transform.cjs +198 -0
  63. package/lib/cjs/core/parser/transform.cjs.map +1 -0
  64. package/lib/cjs/core/parser/transform.d.ts +36 -0
  65. package/lib/cjs/core/parser/tw-parser.cjs +1680 -0
  66. package/lib/cjs/core/parser/tw-parser.cjs.map +1 -0
  67. package/lib/cjs/core/parser/tw-parser.d.ts +210 -0
  68. package/lib/cjs/core/parser/types.d.ts +37 -0
  69. package/lib/cjs/core/parser/typography-dispatcher.cjs +108 -0
  70. package/lib/cjs/core/parser/typography-dispatcher.cjs.map +1 -0
  71. package/lib/cjs/core/parser/typography-dispatcher.d.ts +11 -0
  72. package/lib/cjs/core/parser/typography.cjs +97 -0
  73. package/lib/cjs/core/parser/typography.cjs.map +1 -0
  74. package/lib/cjs/core/parser/typography.d.ts +43 -0
  75. package/lib/cjs/core/style-builder/build-style.cjs +444 -0
  76. package/lib/cjs/core/style-builder/build-style.cjs.map +1 -0
  77. package/lib/cjs/core/style-builder/build-style.d.ts +54 -0
  78. package/lib/cjs/core/style-builder/index.d.ts +3 -0
  79. package/lib/cjs/core/style-builder/union-builder.cjs +326 -0
  80. package/lib/cjs/core/style-builder/union-builder.cjs.map +1 -0
  81. package/lib/cjs/core/style-builder/union-builder.d.ts +128 -0
  82. package/lib/cjs/core/types.d.ts +14 -0
  83. package/lib/cjs/metro/dts.cjs +127 -0
  84. package/lib/cjs/metro/dts.cjs.map +1 -0
  85. package/lib/cjs/metro/dts.d.ts +16 -0
  86. package/lib/cjs/metro/index.cjs +19 -0
  87. package/lib/cjs/metro/index.cjs.map +1 -0
  88. package/lib/cjs/metro/index.d.ts +9 -0
  89. package/lib/cjs/metro/resolver.cjs +47 -0
  90. package/lib/cjs/metro/resolver.cjs.map +1 -0
  91. package/lib/cjs/metro/resolver.d.ts +22 -0
  92. package/lib/cjs/metro/state.cjs +301 -0
  93. package/lib/cjs/metro/state.cjs.map +1 -0
  94. package/lib/cjs/metro/state.d.ts +88 -0
  95. package/lib/cjs/metro/transform-ast.cjs +1472 -0
  96. package/lib/cjs/metro/transform-ast.cjs.map +1 -0
  97. package/lib/cjs/metro/transform-ast.d.ts +88 -0
  98. package/lib/cjs/metro/transformer.cjs +372 -0
  99. package/lib/cjs/metro/transformer.cjs.map +1 -0
  100. package/lib/cjs/metro/transformer.d.ts +47 -0
  101. package/lib/cjs/metro/warn-unknown-classes.cjs +86 -0
  102. package/lib/cjs/metro/warn-unknown-classes.cjs.map +1 -0
  103. package/lib/cjs/metro/warn-unknown-classes.d.ts +21 -0
  104. package/lib/cjs/metro/with-config.cjs +196 -0
  105. package/lib/cjs/metro/with-config.cjs.map +1 -0
  106. package/lib/cjs/metro/with-config.d.ts +79 -0
  107. package/lib/cjs/runtime/chain-handlers.cjs +37 -0
  108. package/lib/cjs/runtime/chain-handlers.cjs.map +1 -0
  109. package/lib/cjs/runtime/chain-handlers.d.ts +33 -0
  110. package/lib/cjs/runtime/components/rnwind-provider.cjs +98 -0
  111. package/lib/cjs/runtime/components/rnwind-provider.cjs.map +1 -0
  112. package/lib/cjs/runtime/components/rnwind-provider.d.ts +84 -0
  113. package/lib/cjs/runtime/gradient-types.d.ts +58 -0
  114. package/lib/cjs/runtime/haptics.cjs +113 -0
  115. package/lib/cjs/runtime/haptics.cjs.map +1 -0
  116. package/lib/cjs/runtime/haptics.d.ts +48 -0
  117. package/lib/cjs/runtime/hooks/use-css.cjs +21 -0
  118. package/lib/cjs/runtime/hooks/use-css.cjs.map +1 -0
  119. package/lib/cjs/runtime/hooks/use-css.d.ts +11 -0
  120. package/lib/cjs/runtime/hooks/use-interact.cjs +46 -0
  121. package/lib/cjs/runtime/hooks/use-interact.cjs.map +1 -0
  122. package/lib/cjs/runtime/hooks/use-interact.d.ts +42 -0
  123. package/lib/cjs/runtime/hooks/use-scheme.cjs +68 -0
  124. package/lib/cjs/runtime/hooks/use-scheme.cjs.map +1 -0
  125. package/lib/cjs/runtime/hooks/use-scheme.d.ts +34 -0
  126. package/lib/cjs/runtime/index.cjs +45 -0
  127. package/lib/cjs/runtime/index.cjs.map +1 -0
  128. package/lib/cjs/runtime/index.d.ts +27 -0
  129. package/lib/cjs/runtime/interactive-box.cjs +35 -0
  130. package/lib/cjs/runtime/interactive-box.cjs.map +1 -0
  131. package/lib/cjs/runtime/interactive-box.d.ts +40 -0
  132. package/lib/cjs/runtime/lookup-css.cjs +542 -0
  133. package/lib/cjs/runtime/lookup-css.cjs.map +1 -0
  134. package/lib/cjs/runtime/lookup-css.d.ts +164 -0
  135. package/lib/cjs/runtime/types.d.ts +29 -0
  136. package/lib/cjs/testing/index.cjs +367 -0
  137. package/lib/cjs/testing/index.cjs.map +1 -0
  138. package/lib/cjs/testing/index.d.ts +145 -0
  139. package/lib/esm/core/parser/animation.d.ts +126 -0
  140. package/lib/esm/core/parser/animation.mjs +408 -0
  141. package/lib/esm/core/parser/animation.mjs.map +1 -0
  142. package/lib/esm/core/parser/border-dispatcher.d.ts +15 -0
  143. package/lib/esm/core/parser/border-dispatcher.mjs +178 -0
  144. package/lib/esm/core/parser/border-dispatcher.mjs.map +1 -0
  145. package/lib/esm/core/parser/case-convert.d.ts +6 -0
  146. package/lib/esm/core/parser/case-convert.mjs +13 -0
  147. package/lib/esm/core/parser/case-convert.mjs.map +1 -0
  148. package/lib/esm/core/parser/color-properties-dispatcher.d.ts +19 -0
  149. package/lib/esm/core/parser/color-properties-dispatcher.mjs +82 -0
  150. package/lib/esm/core/parser/color-properties-dispatcher.mjs.map +1 -0
  151. package/lib/esm/core/parser/color.d.ts +12 -0
  152. package/lib/esm/core/parser/color.mjs +191 -0
  153. package/lib/esm/core/parser/color.mjs.map +1 -0
  154. package/lib/esm/core/parser/constants.d.ts +8 -0
  155. package/lib/esm/core/parser/constants.mjs +13 -0
  156. package/lib/esm/core/parser/constants.mjs.map +1 -0
  157. package/lib/esm/core/parser/declaration.d.ts +15 -0
  158. package/lib/esm/core/parser/declaration.mjs +345 -0
  159. package/lib/esm/core/parser/declaration.mjs.map +1 -0
  160. package/lib/esm/core/parser/gradient.d.ts +59 -0
  161. package/lib/esm/core/parser/gradient.mjs +130 -0
  162. package/lib/esm/core/parser/gradient.mjs.map +1 -0
  163. package/lib/esm/core/parser/haptics.d.ts +47 -0
  164. package/lib/esm/core/parser/haptics.mjs +71 -0
  165. package/lib/esm/core/parser/haptics.mjs.map +1 -0
  166. package/lib/esm/core/parser/index.d.ts +8 -0
  167. package/lib/esm/core/parser/keyframes.d.ts +26 -0
  168. package/lib/esm/core/parser/keyframes.mjs +91 -0
  169. package/lib/esm/core/parser/keyframes.mjs.map +1 -0
  170. package/lib/esm/core/parser/layout-dispatcher.d.ts +14 -0
  171. package/lib/esm/core/parser/layout-dispatcher.mjs +118 -0
  172. package/lib/esm/core/parser/layout-dispatcher.mjs.map +1 -0
  173. package/lib/esm/core/parser/length.d.ts +51 -0
  174. package/lib/esm/core/parser/length.mjs +104 -0
  175. package/lib/esm/core/parser/length.mjs.map +1 -0
  176. package/lib/esm/core/parser/motion-dispatcher.d.ts +11 -0
  177. package/lib/esm/core/parser/motion-dispatcher.mjs +75 -0
  178. package/lib/esm/core/parser/motion-dispatcher.mjs.map +1 -0
  179. package/lib/esm/core/parser/property.d.ts +8 -0
  180. package/lib/esm/core/parser/property.mjs +20 -0
  181. package/lib/esm/core/parser/property.mjs.map +1 -0
  182. package/lib/esm/core/parser/safe-area.d.ts +39 -0
  183. package/lib/esm/core/parser/safe-area.mjs +402 -0
  184. package/lib/esm/core/parser/safe-area.mjs.map +1 -0
  185. package/lib/esm/core/parser/selector.d.ts +11 -0
  186. package/lib/esm/core/parser/selector.mjs +20 -0
  187. package/lib/esm/core/parser/selector.mjs.map +1 -0
  188. package/lib/esm/core/parser/shorthand.d.ts +67 -0
  189. package/lib/esm/core/parser/shorthand.mjs +180 -0
  190. package/lib/esm/core/parser/shorthand.mjs.map +1 -0
  191. package/lib/esm/core/parser/text-truncate.d.ts +44 -0
  192. package/lib/esm/core/parser/text-truncate.mjs +75 -0
  193. package/lib/esm/core/parser/text-truncate.mjs.map +1 -0
  194. package/lib/esm/core/parser/theme-vars.d.ts +82 -0
  195. package/lib/esm/core/parser/theme-vars.mjs +461 -0
  196. package/lib/esm/core/parser/theme-vars.mjs.map +1 -0
  197. package/lib/esm/core/parser/tokens.d.ts +45 -0
  198. package/lib/esm/core/parser/tokens.mjs +480 -0
  199. package/lib/esm/core/parser/tokens.mjs.map +1 -0
  200. package/lib/esm/core/parser/transform.d.ts +36 -0
  201. package/lib/esm/core/parser/transform.mjs +193 -0
  202. package/lib/esm/core/parser/transform.mjs.map +1 -0
  203. package/lib/esm/core/parser/tw-parser.d.ts +210 -0
  204. package/lib/esm/core/parser/tw-parser.mjs +1678 -0
  205. package/lib/esm/core/parser/tw-parser.mjs.map +1 -0
  206. package/lib/esm/core/parser/types.d.ts +37 -0
  207. package/lib/esm/core/parser/typography-dispatcher.d.ts +11 -0
  208. package/lib/esm/core/parser/typography-dispatcher.mjs +106 -0
  209. package/lib/esm/core/parser/typography-dispatcher.mjs.map +1 -0
  210. package/lib/esm/core/parser/typography.d.ts +43 -0
  211. package/lib/esm/core/parser/typography.mjs +91 -0
  212. package/lib/esm/core/parser/typography.mjs.map +1 -0
  213. package/lib/esm/core/style-builder/build-style.d.ts +54 -0
  214. package/lib/esm/core/style-builder/build-style.mjs +442 -0
  215. package/lib/esm/core/style-builder/build-style.mjs.map +1 -0
  216. package/lib/esm/core/style-builder/index.d.ts +3 -0
  217. package/lib/esm/core/style-builder/union-builder.d.ts +128 -0
  218. package/lib/esm/core/style-builder/union-builder.mjs +324 -0
  219. package/lib/esm/core/style-builder/union-builder.mjs.map +1 -0
  220. package/lib/esm/core/types.d.ts +14 -0
  221. package/lib/esm/metro/dts.d.ts +16 -0
  222. package/lib/esm/metro/dts.mjs +125 -0
  223. package/lib/esm/metro/dts.mjs.map +1 -0
  224. package/lib/esm/metro/index.d.ts +9 -0
  225. package/lib/esm/metro/index.mjs +6 -0
  226. package/lib/esm/metro/index.mjs.map +1 -0
  227. package/lib/esm/metro/resolver.d.ts +22 -0
  228. package/lib/esm/metro/resolver.mjs +43 -0
  229. package/lib/esm/metro/resolver.mjs.map +1 -0
  230. package/lib/esm/metro/state.d.ts +88 -0
  231. package/lib/esm/metro/state.mjs +291 -0
  232. package/lib/esm/metro/state.mjs.map +1 -0
  233. package/lib/esm/metro/transform-ast.d.ts +88 -0
  234. package/lib/esm/metro/transform-ast.mjs +1451 -0
  235. package/lib/esm/metro/transform-ast.mjs.map +1 -0
  236. package/lib/esm/metro/transformer.d.ts +47 -0
  237. package/lib/esm/metro/transformer.mjs +349 -0
  238. package/lib/esm/metro/transformer.mjs.map +1 -0
  239. package/lib/esm/metro/warn-unknown-classes.d.ts +21 -0
  240. package/lib/esm/metro/warn-unknown-classes.mjs +84 -0
  241. package/lib/esm/metro/warn-unknown-classes.mjs.map +1 -0
  242. package/lib/esm/metro/with-config.d.ts +79 -0
  243. package/lib/esm/metro/with-config.mjs +194 -0
  244. package/lib/esm/metro/with-config.mjs.map +1 -0
  245. package/lib/esm/runtime/chain-handlers.d.ts +33 -0
  246. package/lib/esm/runtime/chain-handlers.mjs +34 -0
  247. package/lib/esm/runtime/chain-handlers.mjs.map +1 -0
  248. package/lib/esm/runtime/components/rnwind-provider.d.ts +84 -0
  249. package/lib/esm/runtime/components/rnwind-provider.mjs +94 -0
  250. package/lib/esm/runtime/components/rnwind-provider.mjs.map +1 -0
  251. package/lib/esm/runtime/gradient-types.d.ts +58 -0
  252. package/lib/esm/runtime/haptics.d.ts +48 -0
  253. package/lib/esm/runtime/haptics.mjs +110 -0
  254. package/lib/esm/runtime/haptics.mjs.map +1 -0
  255. package/lib/esm/runtime/hooks/use-css.d.ts +11 -0
  256. package/lib/esm/runtime/hooks/use-css.mjs +19 -0
  257. package/lib/esm/runtime/hooks/use-css.mjs.map +1 -0
  258. package/lib/esm/runtime/hooks/use-interact.d.ts +42 -0
  259. package/lib/esm/runtime/hooks/use-interact.mjs +44 -0
  260. package/lib/esm/runtime/hooks/use-interact.mjs.map +1 -0
  261. package/lib/esm/runtime/hooks/use-scheme.d.ts +34 -0
  262. package/lib/esm/runtime/hooks/use-scheme.mjs +63 -0
  263. package/lib/esm/runtime/hooks/use-scheme.mjs.map +1 -0
  264. package/lib/esm/runtime/index.d.ts +27 -0
  265. package/lib/esm/runtime/index.mjs +18 -0
  266. package/lib/esm/runtime/index.mjs.map +1 -0
  267. package/lib/esm/runtime/interactive-box.d.ts +40 -0
  268. package/lib/esm/runtime/interactive-box.mjs +33 -0
  269. package/lib/esm/runtime/interactive-box.mjs.map +1 -0
  270. package/lib/esm/runtime/lookup-css.d.ts +164 -0
  271. package/lib/esm/runtime/lookup-css.mjs +531 -0
  272. package/lib/esm/runtime/lookup-css.mjs.map +1 -0
  273. package/lib/esm/runtime/types.d.ts +29 -0
  274. package/lib/esm/testing/index.d.ts +145 -0
  275. package/lib/esm/testing/index.mjs +344 -0
  276. package/lib/esm/testing/index.mjs.map +1 -0
  277. package/package.json +80 -13
  278. package/preset.css +1171 -0
  279. package/src/core/parser/animation.ts +404 -0
  280. package/src/core/parser/border-dispatcher.ts +176 -0
  281. package/src/core/parser/case-convert.ts +10 -0
  282. package/src/core/parser/color-properties-dispatcher.ts +78 -0
  283. package/src/core/parser/color.ts +191 -0
  284. package/src/core/parser/constants.ts +11 -0
  285. package/src/core/parser/declaration.ts +340 -0
  286. package/src/core/parser/gradient.ts +148 -0
  287. package/src/core/parser/haptics.ts +88 -0
  288. package/src/core/parser/index.ts +8 -0
  289. package/src/core/parser/keyframes.ts +84 -0
  290. package/src/core/parser/layout-dispatcher.ts +111 -0
  291. package/src/core/parser/length.ts +114 -0
  292. package/src/core/parser/motion-dispatcher.ts +89 -0
  293. package/src/core/parser/property.ts +15 -0
  294. package/src/core/parser/safe-area.ts +404 -0
  295. package/src/core/parser/selector.ts +17 -0
  296. package/src/core/parser/shorthand.ts +182 -0
  297. package/src/core/parser/text-truncate.ts +79 -0
  298. package/src/core/parser/theme-vars.ts +465 -0
  299. package/src/core/parser/tokens.ts +456 -0
  300. package/src/core/parser/transform.ts +195 -0
  301. package/src/core/parser/tw-parser.ts +1828 -0
  302. package/src/core/parser/types.ts +45 -0
  303. package/src/core/parser/typography-dispatcher.ts +97 -0
  304. package/src/core/parser/typography.ts +83 -0
  305. package/src/core/style-builder/build-style.ts +500 -0
  306. package/src/core/style-builder/index.ts +3 -0
  307. package/src/core/style-builder/union-builder.ts +328 -0
  308. package/src/core/types.ts +15 -0
  309. package/src/metro/dts.ts +128 -0
  310. package/src/metro/index.ts +9 -0
  311. package/src/metro/resolver.ts +42 -0
  312. package/src/metro/state.ts +305 -0
  313. package/src/metro/transform-ast.ts +1729 -0
  314. package/src/metro/transformer.ts +372 -0
  315. package/src/metro/warn-unknown-classes.ts +79 -0
  316. package/src/metro/with-config.ts +251 -0
  317. package/src/runtime/chain-handlers.ts +47 -0
  318. package/src/runtime/components/rnwind-provider.tsx +144 -0
  319. package/src/runtime/gradient-types.ts +60 -0
  320. package/src/runtime/haptics.ts +120 -0
  321. package/src/runtime/hooks/use-css.ts +16 -0
  322. package/src/runtime/hooks/use-interact.ts +65 -0
  323. package/src/runtime/hooks/use-scheme.ts +63 -0
  324. package/src/runtime/index.ts +54 -0
  325. package/src/runtime/interactive-box.tsx +57 -0
  326. package/src/runtime/lookup-css.ts +628 -0
  327. package/src/runtime/types.ts +32 -0
  328. package/src/testing/index.ts +507 -0
  329. package/src/types/tailwindcss-node.d.ts +33 -0
  330. package/src/index.ts +0 -1
@@ -0,0 +1,191 @@
1
+ import type { CssColor, LABColor } from 'lightningcss'
2
+ import { formatHex, rgb as culoriRgb } from 'culori'
3
+
4
+ /**
5
+ * Clamp a 0-255 float to the integer byte range RN color strings accept.
6
+ * @param value Unclamped float (may be negative or above 255).
7
+ * @returns Integer in `[0, 255]`.
8
+ */
9
+ function clampByte(value: number): number {
10
+ if (value < 0) return 0
11
+ if (value > 255) return 255
12
+ return Math.round(value)
13
+ }
14
+
15
+ /**
16
+ * Two-digit hex encoding of a 0-255 byte.
17
+ * @param byte Byte value (may be out-of-range — clamped).
18
+ * @returns Zero-padded two-char hex string.
19
+ */
20
+ function byteToHex(byte: number): string {
21
+ const hex = clampByte(byte).toString(16)
22
+ return hex.length === 1 ? `0${hex}` : hex
23
+ }
24
+
25
+ /**
26
+ * Format an integer-RGB triple + alpha as `#rrggbb` or `rgba(r, g, b, a)`.
27
+ * @param r 0-255 red.
28
+ * @param g 0-255 green.
29
+ * @param b 0-255 blue.
30
+ * @param alpha 0-1 alpha.
31
+ * @returns Color string.
32
+ */
33
+ function rgbIntsToString(r: number, g: number, b: number, alpha: number): string {
34
+ if (alpha >= 1) return `#${byteToHex(r)}${byteToHex(g)}${byteToHex(b)}`
35
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`
36
+ }
37
+
38
+ /**
39
+ * Format a float-RGB triple + alpha (CSS `color(srgb …)` forms) as
40
+ * hex/rgba.
41
+ * @param r 0-1 red.
42
+ * @param g 0-1 green.
43
+ * @param b 0-1 blue.
44
+ * @param alpha 0-1 alpha.
45
+ * @returns Color string.
46
+ */
47
+ function floatRgbToString(r: number, g: number, b: number, alpha: number): string {
48
+ return rgbIntsToString(clampByte(r * 255), clampByte(g * 255), clampByte(b * 255), alpha)
49
+ }
50
+
51
+ /**
52
+ * Dispatch a LAB-family color through culori's hex formatter.
53
+ * @param color Typed LAB-family color.
54
+ * @returns `#rrggbb` string, or `null` when culori couldn't convert.
55
+ */
56
+ function culoriHexFor(color: LABColor): string | null {
57
+ switch (color.type) {
58
+ case 'oklch': {
59
+ return formatHex({ mode: 'oklch', l: color.l, c: color.c, h: color.h }) ?? null
60
+ }
61
+ case 'oklab': {
62
+ return formatHex({ mode: 'oklab', l: color.l, a: color.a, b: color.b }) ?? null
63
+ }
64
+ case 'lab': {
65
+ return formatHex({ mode: 'lab', l: color.l, a: color.a, b: color.b }) ?? null
66
+ }
67
+ case 'lch': {
68
+ return formatHex({ mode: 'lch', l: color.l, c: color.c, h: color.h }) ?? null
69
+ }
70
+ default: {
71
+ return null
72
+ }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Composite a culori-produced sRGB hex with the source alpha into the RN
78
+ * color string. Shared tail for every culori-backed conversion (lab
79
+ * family, XYZ, wide-gamut RGB): opaque → the hex as-is; translucent →
80
+ * `rgba(...)` rebuilt from the hex channels.
81
+ * @param hex sRGB hex from culori, or `null` when culori rejected the color.
82
+ * @param alpha Source alpha (0–1).
83
+ * @returns RN color string.
84
+ */
85
+ function withAlpha(hex: string | null, alpha: number): string {
86
+ if (!hex) return alpha < 1 ? 'rgba(0, 0, 0, 0)' : 'transparent'
87
+ if (alpha >= 1) return hex
88
+ const back = culoriRgb(hex)
89
+ if (!back) return hex
90
+ return rgbIntsToString(clampByte(back.r * 255), clampByte(back.g * 255), clampByte(back.b * 255), alpha)
91
+ }
92
+
93
+ /**
94
+ * Convert a LAB / LCH / OKLAB / OKLCH color to sRGB hex via culori. RN
95
+ * can't evaluate these modern color spaces at paint time; compile-time
96
+ * lowering to sRGB is the only portable path.
97
+ * @param color Typed lab-family color.
98
+ * @returns Hex or rgba string in sRGB.
99
+ */
100
+ function labFamilyToHex(color: LABColor): string {
101
+ return withAlpha(culoriHexFor(color), color.alpha)
102
+ }
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: 'lrgb' | 'p3' | 'a98' | 'prophoto' | 'rec2020', r: number, g: number, b: number, alpha: number): string {
118
+ return withAlpha(formatHex({ mode, r, g, b }) ?? null, alpha)
119
+ }
120
+
121
+ /**
122
+ * Convert a CSS `color(xyz-d50 …)` / `color(xyz-d65 …)` value to sRGB hex
123
+ * via culori.
124
+ * @param color Typed XYZ color (discriminated by `type`).
125
+ * @param color.type Whether the color is in the D50 or D65 XYZ space.
126
+ * @param color.x CIE X component (0–1).
127
+ * @param color.y CIE Y component (0–1).
128
+ * @param color.z CIE Z component (0–1).
129
+ * @param color.alpha Alpha channel (0–1).
130
+ * @returns `#rrggbb` string, or `'transparent'` when culori rejects it.
131
+ */
132
+ function xyzToHex(color: { type: 'xyz-d50' | 'xyz-d65'; x: number; y: number; z: number; alpha: number }): string {
133
+ const mode = color.type === 'xyz-d50' ? 'xyz50' : 'xyz65'
134
+ return withAlpha(formatHex({ mode, x: color.x, y: color.y, z: color.z }) ?? null, color.alpha)
135
+ }
136
+
137
+ /**
138
+ * Convert a lightningcss `CssColor` to an RN-safe color string. RGB
139
+ * passes through unchanged. LAB / LCH / OKLAB / OKLCH / `color(xyz-…)`
140
+ * forms go through culori to reach sRGB — RN's native view manager only
141
+ * understands sRGB-family strings. SystemColor keywords (`'background'`,
142
+ * `'canvas'`, …) pass through untouched; they have no RN analog and the
143
+ * runtime ignores unknown color strings gracefully.
144
+ * @param color Typed color value.
145
+ * @returns Color string RN accepts.
146
+ */
147
+ export function cssColorToString(color: CssColor): string {
148
+ if (typeof color === 'string') return color
149
+ switch (color.type) {
150
+ case 'rgb': {
151
+ return rgbIntsToString(color.r, color.g, color.b, color.alpha)
152
+ }
153
+ case 'lab':
154
+ case 'lch':
155
+ case 'oklab':
156
+ case 'oklch': {
157
+ return labFamilyToHex(color)
158
+ }
159
+ case 'srgb': {
160
+ return floatRgbToString(color.r, color.g, color.b, color.alpha)
161
+ }
162
+ case 'srgb-linear': {
163
+ return wideGamutToHex('lrgb', color.r, color.g, color.b, color.alpha)
164
+ }
165
+ case 'display-p3': {
166
+ return wideGamutToHex('p3', color.r, color.g, color.b, color.alpha)
167
+ }
168
+ case 'a98-rgb': {
169
+ return wideGamutToHex('a98', color.r, color.g, color.b, color.alpha)
170
+ }
171
+ case 'prophoto-rgb': {
172
+ return wideGamutToHex('prophoto', color.r, color.g, color.b, color.alpha)
173
+ }
174
+ case 'rec2020': {
175
+ return wideGamutToHex('rec2020', color.r, color.g, color.b, color.alpha)
176
+ }
177
+ case 'xyz-d50':
178
+ case 'xyz-d65': {
179
+ return xyzToHex(color)
180
+ }
181
+ case 'currentcolor': {
182
+ return 'currentColor'
183
+ }
184
+ case 'light-dark': {
185
+ return cssColorToString(color.light)
186
+ }
187
+ default: {
188
+ return 'transparent'
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,11 @@
1
+ /** 1 rem = 16 px — matches the CSS default so unit-bare Tailwind spacing stays predictable. */
2
+ export const REM_TO_PX = 16
3
+
4
+ // Hot-path regexes hoisted to module scope so they're compiled once per
5
+ // process; an inline literal would recompile on every declaration.
6
+ export const BARE_NUMBER_REGEX = /^-?\d+(?:\.\d+)?$/
7
+ export const LENGTH_PX_REGEX = /^(-?\d+(?:\.\d+)?)px$/
8
+ export const LENGTH_REM_REGEX = /^(-?\d+(?:\.\d+)?)rem$/
9
+ export const CALC_RATIO_REGEX = /^calc\(\s*(-?\d+(?:\.\d+)?)\s*\/\s*(-?\d+(?:\.\d+)?)\s*\)$/
10
+ export const CALC_MUL_REGEX = /^calc\(\s*(-?\d+(?:\.\d+)?)(?:px|rem)?\s*\*\s*(-?\d+(?:\.\d+)?)\s*\)$/
11
+ export const KEBAB_BOUNDARY_REGEX = /-([a-z])/g
@@ -0,0 +1,340 @@
1
+ /* eslint-disable sonarjs/cognitive-complexity -- the main Declaration → RN-entries dispatcher is intentionally a flat switch so each branch keeps its narrowed value type */
2
+ import type { Declaration as LcDeclaration, TokenOrValue } from 'lightningcss'
3
+ import { kebabToCamel } from './case-convert'
4
+ import { cssColorToString } from './color'
5
+ import { dimensionPercentageToNumber, gapValueToValue, lengthPercentageOrAutoToValue, sizeLikeToValue } from './length'
6
+ import {
7
+ expandBorderColor,
8
+ expandBorderRadius,
9
+ expandFourSided,
10
+ expandGap,
11
+ expandLogicalBlock,
12
+ expandLogicalInline,
13
+ flexToEntries,
14
+ } from './shorthand'
15
+ import { coerceUnparsedValue, serializeTokens, substituteThemeVars } from './tokens'
16
+ import { displayToEntries, fontSizeToPx, fontWeightToValue, zIndexToNumber } from './typography'
17
+ import { dispatchMotionDeclaration } from './motion-dispatcher'
18
+ import { dispatchTypographyDeclaration } from './typography-dispatcher'
19
+ import { dispatchLayoutDeclaration } from './layout-dispatcher'
20
+ import { dispatchColorPropertyDeclaration } from './color-properties-dispatcher'
21
+ import { dispatchBorderDeclaration } from './border-dispatcher'
22
+ import { detectSafeAreaMarker } from './safe-area'
23
+ import type { RNEntry } from './types'
24
+
25
+ /** CSS timing-function properties that need `cubic-bezier(...)` snapping. */
26
+ const TIMING_FUNCTION_PROPS = new Set(['transition-timing-function', 'animation-timing-function'])
27
+
28
+ /** CSS easing keywords Reanimated v4's CSS engine accepts as strings. */
29
+ const CSS_EASING_KEYWORDS = new Set(['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end'])
30
+
31
+ /** CSS properties with no useful RN equivalent — silently dropped. */
32
+ const UNSUPPORTED_LOGICAL_PROPS = new Set([
33
+ 'border-inline-style',
34
+ 'border-block-style',
35
+ 'border-inline-start-style',
36
+ 'border-inline-end-style',
37
+ 'border-block-start-style',
38
+ 'border-block-end-style',
39
+ ])
40
+
41
+ /**
42
+ * Pick the closest predefined CSS easing keyword for a `cubic-bezier`
43
+ * control-point set. Mirrors {@link snapCubicBezierToKeyword} in
44
+ * `animation.ts` — kept here so the unparsed-string path doesn't need
45
+ * to import a typed-only helper.
46
+ * @param x1 First control-point x (0–1).
47
+ * @param y1 First control-point y (0–1).
48
+ * @param x2 Second control-point x (0–1).
49
+ * @param y2 Second control-point y (0–1).
50
+ * @returns CSS easing keyword.
51
+ */
52
+ function snapBezier(x1: number, y1: number, x2: number, y2: number): string {
53
+ const tol = 0.01
54
+ const eq = (a: number, b: number): boolean => Math.abs(a - b) < tol
55
+ if (eq(x1, 0) && eq(y1, 0) && eq(x2, 1) && eq(y2, 1)) return 'linear'
56
+ if (eq(x1, 0.25) && eq(y1, 0.1) && eq(x2, 0.25) && eq(y2, 1)) return 'ease'
57
+ if (eq(x1, 0.4) && eq(y1, 0) && eq(x2, 1) && eq(y2, 1)) return 'ease-in'
58
+ if (eq(x1, 0) && eq(y1, 0) && eq(x2, 0.2) && eq(y2, 1)) return 'ease-out'
59
+ if (eq(x1, 0.4) && eq(y1, 0) && eq(x2, 0.2) && eq(y2, 1)) return 'ease-in-out'
60
+ const startsFlat = x1 < 0.1
61
+ const endsFlat = x2 > 0.9
62
+ if (startsFlat && !endsFlat) return 'ease-out'
63
+ if (!startsFlat && endsFlat) return 'ease-in'
64
+ return 'ease-in-out'
65
+ }
66
+
67
+ /**
68
+ * Snap a cubic-bezier expression string to the closest CSS keyword
69
+ * Reanimated v4's CSS engine accepts. Strings that already are keywords
70
+ * pass through unchanged.
71
+ * @param value Resolved value text from an unparsed timing-function declaration.
72
+ * @returns CSS easing keyword.
73
+ */
74
+ function coerceCubicBezierString(value: string): string {
75
+ const text = value.trim()
76
+ if (CSS_EASING_KEYWORDS.has(text)) return text
77
+ const match = /^cubic-bezier\(\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*\)$/.exec(text)
78
+ if (!match) return 'ease-in-out'
79
+ const [, x1, y1, x2, y2] = match
80
+ return snapBezier(Number(x1), Number(y1), Number(x2), Number(y2))
81
+ }
82
+
83
+ /**
84
+ * Fast-path check for the handful of color property names Tailwind emits.
85
+ * @param property Kebab-case CSS property name.
86
+ * @returns Whether the property's value should be treated as a color.
87
+ */
88
+ function isColorProperty(property: string): boolean {
89
+ return (
90
+ property === 'color' ||
91
+ property === 'background-color' ||
92
+ (property.startsWith('border-') && property.endsWith('-color')) ||
93
+ property.endsWith('-color')
94
+ )
95
+ }
96
+
97
+ /**
98
+ * Convert an unparsed declaration (typical Tailwind v4 output containing
99
+ * `var()` / `calc()`) into RN entries. Serializes the token list, then
100
+ * coerces the flat string into a number / keyword / length via a tight
101
+ * set of shapes Tailwind actually emits.
102
+ * @param property Real property name (kebab-case).
103
+ * @param tokens Token list from lightningcss.
104
+ * @param themeVars Optional lookup table for resolving `var(--x)` references.
105
+ * @returns RN entries — usually one, empty when unusable.
106
+ */
107
+ function unparsedToEntries(
108
+ property: string,
109
+ tokens: readonly TokenOrValue[],
110
+ themeVars: ReadonlyMap<string, string> | undefined,
111
+ ): readonly RNEntry[] {
112
+ if (property.length === 0) return []
113
+ // Safe-area detection runs BEFORE token serialization because
114
+ // `env()` serializes to an empty string, which would strip the side
115
+ // info we need. If the tokens encode a recognised `env(safe-area-inset-*)`
116
+ // pattern (pure / `max(..., n)` / `calc(...+n)` / `h-screen-safe`),
117
+ // emit a runtime-resolved marker instead.
118
+ if (UNSUPPORTED_LOGICAL_PROPS.has(property)) return []
119
+ const safe = detectSafeAreaMarker(tokens, themeVars)
120
+ if (safe !== null) return [[kebabToCamel(property), safe]]
121
+ let text = serializeTokens(tokens)
122
+ if (themeVars && themeVars.size > 0) text = substituteThemeVars(text, themeVars)
123
+ const coerced = coerceUnparsedValue(text)
124
+ if (coerced === null) return []
125
+ // Skip values that didn't resolve past their `var()` wrapper — they
126
+ // came from a `@property --tw-*` token without a real fallback.
127
+ // Tailwind v4's `border-N` emits `border-style: var(--tw-border-style)`
128
+ // expecting the cascade to fill it in; in RN we drop them and rely on
129
+ // RN's default (solid).
130
+ if (typeof coerced === 'string' && coerced.startsWith('var(')) return []
131
+ // Logical-direction CSS properties RN doesn't have direct equivalents
132
+ // for. Keep the dropped names in one place so it's easy to audit.
133
+ if (UNSUPPORTED_LOGICAL_PROPS.has(property)) return []
134
+ if (TIMING_FUNCTION_PROPS.has(property) && typeof coerced === 'string') {
135
+ // `transition-colors` and similar emit `var(--tw-ease, cubic-bezier(...))`
136
+ // which serializes to a cubic-bezier STRING after substitution.
137
+ // Reanimated v4's CSS engine rejects those — snap to the closest
138
+ // predefined keyword (same logic as the typed `EasingFunction` path).
139
+ return [[kebabToCamel(property), coerceCubicBezierString(coerced)]]
140
+ }
141
+ if (isColorProperty(property) && typeof coerced === 'string') {
142
+ // Resolved user-theme color strings (e.g. `#ff0099`) go straight to
143
+ // the RN style — no further conversion needed.
144
+ return [[kebabToCamel(property), coerced]]
145
+ }
146
+ return [[kebabToCamel(property), coerced]]
147
+ }
148
+
149
+ /**
150
+ * Convert one lightningcss `Declaration` into zero-or-more RN style
151
+ * entries. Shorthand declarations (padding/margin/border-radius/flex) can
152
+ * emit multiple entries; skipped or unsupported properties emit none.
153
+ *
154
+ * The switch branches on `decl.property` so TypeScript narrows
155
+ * `decl.value` to the exact typed shape for each branch — no casts
156
+ * required. Unknown properties fall through to `[]`.
157
+ * @param decl One declaration from a lightningcss style rule.
158
+ * @param themeVars Optional lookup table for resolving `var(--x)` references inside unparsed values.
159
+ * @returns Array of `[key, value]` entries.
160
+ */
161
+ export function declarationToRnEntries(decl: LcDeclaration, themeVars?: ReadonlyMap<string, string>): readonly RNEntry[] {
162
+ switch (decl.property) {
163
+ case 'custom': {
164
+ // Lightningcss routes two shapes through `custom`:
165
+ // - Actual CSS custom properties (`--my-var`): no RN meaning, drop.
166
+ // - Real properties it doesn't have a dedicated typed entry for
167
+ // (e.g. `object-fit`, `pointer-events`, future CSS keyword-only
168
+ // props): treat like an unparsed declaration so the keyword
169
+ // surfaces in the RN style.
170
+ const customName = decl.value.name
171
+ if (customName.startsWith('--')) return []
172
+ return unparsedToEntries(customName, decl.value.value ?? [], themeVars)
173
+ }
174
+ case 'unparsed': {
175
+ return unparsedToEntries(decl.value.propertyId.property, decl.value.value, themeVars)
176
+ }
177
+ case 'color':
178
+ case 'background-color':
179
+ case 'border-top-color':
180
+ case 'border-right-color':
181
+ case 'border-bottom-color':
182
+ case 'border-left-color': {
183
+ // `background-color` narrows to `CssColor | 'background'` — the
184
+ // literal keyword means UA default. Skip the keyword.
185
+ if (typeof decl.value === 'string') return []
186
+ return [[kebabToCamel(decl.property), cssColorToString(decl.value)]]
187
+ }
188
+ case 'border-color': {
189
+ return expandBorderColor(decl.value)
190
+ }
191
+ case 'opacity': {
192
+ // Lightningcss hands us an `f32` for opacity, so values like `0.8`
193
+ // round-trip as `0.800000011920929`. Snap to 4 decimals to match the
194
+ // rest of the parser's numeric convention.
195
+ return [[decl.property, Math.round(decl.value * 10_000) / 10_000]]
196
+ }
197
+ case 'z-index': {
198
+ return [['zIndex', zIndexToNumber(decl.value)]]
199
+ }
200
+ case 'top':
201
+ case 'right':
202
+ case 'bottom':
203
+ case 'left': {
204
+ const v = lengthPercentageOrAutoToValue(decl.value)
205
+ if (v === null) return []
206
+ return [[decl.property, v]]
207
+ }
208
+ case 'inset': {
209
+ const top = lengthPercentageOrAutoToValue(decl.value.top)
210
+ const right = lengthPercentageOrAutoToValue(decl.value.right)
211
+ const bottom = lengthPercentageOrAutoToValue(decl.value.bottom)
212
+ const left = lengthPercentageOrAutoToValue(decl.value.left)
213
+ if (top === null || right === null || bottom === null || left === null) return []
214
+ return [
215
+ ['top', top],
216
+ ['right', right],
217
+ ['bottom', bottom],
218
+ ['left', left],
219
+ ]
220
+ }
221
+ case 'inset-inline': {
222
+ const start = lengthPercentageOrAutoToValue(decl.value.inlineStart)
223
+ const end = lengthPercentageOrAutoToValue(decl.value.inlineEnd)
224
+ if (start === null || end === null) return []
225
+ return [
226
+ ['left', start],
227
+ ['right', end],
228
+ ]
229
+ }
230
+ case 'inset-block': {
231
+ const start = lengthPercentageOrAutoToValue(decl.value.blockStart)
232
+ const end = lengthPercentageOrAutoToValue(decl.value.blockEnd)
233
+ if (start === null || end === null) return []
234
+ return [
235
+ ['top', start],
236
+ ['bottom', end],
237
+ ]
238
+ }
239
+ case 'width':
240
+ case 'height':
241
+ case 'min-width':
242
+ case 'min-height':
243
+ case 'max-width':
244
+ case 'max-height': {
245
+ const v = sizeLikeToValue(decl.value)
246
+ if (v === null) return []
247
+ return [[kebabToCamel(decl.property), v]]
248
+ }
249
+ case 'gap': {
250
+ return expandGap(decl.value)
251
+ }
252
+ case 'row-gap':
253
+ case 'column-gap': {
254
+ const v = gapValueToValue(decl.value)
255
+ if (v === null) return []
256
+ return [[kebabToCamel(decl.property), v]]
257
+ }
258
+ case 'font-style': {
259
+ return [['fontStyle', decl.value.type]]
260
+ }
261
+ case 'display': {
262
+ return displayToEntries(decl.value)
263
+ }
264
+ case 'position': {
265
+ return [['position', decl.value.type]]
266
+ }
267
+ case 'font-size': {
268
+ const px = fontSizeToPx(decl.value)
269
+ if (px === null) return []
270
+ return [['fontSize', px]]
271
+ }
272
+ case 'font-weight': {
273
+ return [['fontWeight', fontWeightToValue(decl.value)]]
274
+ }
275
+ case 'padding': {
276
+ return expandFourSided('padding', decl.value)
277
+ }
278
+ case 'margin': {
279
+ return expandFourSided('margin', decl.value)
280
+ }
281
+ case 'padding-inline': {
282
+ return expandLogicalInline('padding', decl.value)
283
+ }
284
+ case 'padding-block': {
285
+ return expandLogicalBlock('padding', decl.value)
286
+ }
287
+ case 'margin-inline': {
288
+ return expandLogicalInline('margin', decl.value)
289
+ }
290
+ case 'margin-block': {
291
+ return expandLogicalBlock('margin', decl.value)
292
+ }
293
+ case 'padding-top':
294
+ case 'padding-right':
295
+ case 'padding-bottom':
296
+ case 'padding-left':
297
+ case 'margin-top':
298
+ case 'margin-right':
299
+ case 'margin-bottom':
300
+ case 'margin-left': {
301
+ const v = lengthPercentageOrAutoToValue(decl.value)
302
+ if (v === null) return []
303
+ return [[kebabToCamel(decl.property), v]]
304
+ }
305
+ case 'border-radius': {
306
+ return expandBorderRadius(decl.value)
307
+ }
308
+ case 'border-top-left-radius':
309
+ case 'border-top-right-radius':
310
+ case 'border-bottom-left-radius':
311
+ case 'border-bottom-right-radius': {
312
+ const [xAxis] = decl.value
313
+ const v = dimensionPercentageToNumber(xAxis)
314
+ if (v === null) return []
315
+ return [[kebabToCamel(decl.property), v]]
316
+ }
317
+ case 'flex': {
318
+ return flexToEntries(decl.value)
319
+ }
320
+ case 'flex-grow':
321
+ case 'flex-shrink': {
322
+ return [[kebabToCamel(decl.property), decl.value]]
323
+ }
324
+ case 'flex-basis': {
325
+ const v = lengthPercentageOrAutoToValue(decl.value)
326
+ if (v === null) return []
327
+ return [['flexBasis', v]]
328
+ }
329
+ default: {
330
+ return (
331
+ dispatchLayoutDeclaration(decl) ??
332
+ dispatchTypographyDeclaration(decl) ??
333
+ dispatchColorPropertyDeclaration(decl) ??
334
+ dispatchBorderDeclaration(decl) ??
335
+ dispatchMotionDeclaration(decl) ??
336
+ []
337
+ )
338
+ }
339
+ }
340
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Gradient-atom extractor.
3
+ *
4
+ * Tailwind v4's gradient system is encoded as a set of CSS custom
5
+ * properties Tailwind writes onto specific utility class rules:
6
+ *
7
+ * .from-red-500 → `--tw-gradient-from: #ef4444`
8
+ * .via-green-500 → `--tw-gradient-via: #22c55e`
9
+ * .to-blue-500 → `--tw-gradient-to: #3b82f6`
10
+ * .bg-gradient-to-r → `--tw-gradient-position: to right`
11
+ * .bg-linear-to-br → `--tw-gradient-position: to bottom right`
12
+ *
13
+ * None of these set properties React Native can render (`backgroundImage:
14
+ * linear-gradient(...)` is web-only). Instead, rnwind treats them as
15
+ * **metadata**: the transformer strips gradient atoms from the JSX
16
+ * site's className and emits `colors={[...]} start={...} end={...}`
17
+ * props on the original component (user supplies `<LinearGradient>`
18
+ * from `expo-linear-gradient` or similar).
19
+ *
20
+ * This module walks a lightningcss declaration list, notices the
21
+ * `--tw-gradient-*` writes, and surfaces them as a compact
22
+ * {@link GradientAtomInfo} record the transformer can read per atom.
23
+ */
24
+
25
+ import type { Declaration as LcDeclaration, TokenOrValue } from 'lightningcss'
26
+ import { cssColorToString } from './color'
27
+
28
+ /**
29
+ * The four roles an atom can play in a Tailwind v4 gradient. `from`,
30
+ * `via`, `to` carry a color. `direction` carries one of the 8 stock
31
+ * points (or a bare angle when Tailwind v4's `bg-linear-[angle]`
32
+ * syntax is used).
33
+ */
34
+ export type GradientAtomInfo =
35
+ | { readonly role: 'from'; readonly color: string }
36
+ | { readonly role: 'via'; readonly color: string }
37
+ | { readonly role: 'to'; readonly color: string }
38
+ | { readonly role: 'direction'; readonly dir: GradientDirection }
39
+
40
+ /**
41
+ * Eight stock corner directions Tailwind v4 ships. The transformer
42
+ * maps each to a `(start, end)` pair of unit-square points the
43
+ * expo-linear-gradient API expects. Unknown directions are surfaced as
44
+ * `'unknown'` so the transformer can skip them gracefully.
45
+ */
46
+ export type GradientDirection =
47
+ | 'to-r'
48
+ | 'to-l'
49
+ | 'to-t'
50
+ | 'to-b'
51
+ | 'to-tr'
52
+ | 'to-tl'
53
+ | 'to-br'
54
+ | 'to-bl'
55
+ | 'unknown'
56
+
57
+ /**
58
+ * Inspect a rule's declaration list for `--tw-gradient-*` writes and
59
+ * return the atom's role + data. Returns `null` for rules that don't
60
+ * belong to a gradient utility.
61
+ * @param declarations Declarations from one lightningcss style rule.
62
+ * @returns Gradient info, or null.
63
+ */
64
+ function detectGradientAtom(declarations: readonly LcDeclaration[]): GradientAtomInfo | null {
65
+ for (const decl of declarations) {
66
+ if (decl.property !== 'custom') continue
67
+ const custom = decl.value as { name: { name: string } | string; value?: readonly TokenOrValue[] }
68
+ const name = typeof custom.name === 'string' ? custom.name : custom.name.name
69
+ if (!name.startsWith('--tw-gradient-')) continue
70
+ if (name === '--tw-gradient-from') return fromColor('from', custom.value)
71
+ if (name === '--tw-gradient-via') return fromColor('via', custom.value)
72
+ if (name === '--tw-gradient-to') return fromColor('to', custom.value)
73
+ if (name === '--tw-gradient-position') return fromDirection(custom.value)
74
+ }
75
+ return null
76
+ }
77
+
78
+ /**
79
+ * Extract a single color token from a custom-property value list and
80
+ * return the `{role, color}` record. Returns `null` when no color token
81
+ * is present (defensive — Tailwind always emits one, but future output
82
+ * shapes may not).
83
+ * @param role Target role (`from` / `via` / `to`).
84
+ * @param tokens Value tokens from the `--tw-gradient-*` declaration.
85
+ * @returns Gradient info, or null.
86
+ */
87
+ function fromColor(role: 'from' | 'via' | 'to', tokens: readonly TokenOrValue[] | undefined): GradientAtomInfo | null {
88
+ if (!tokens) return null
89
+ for (const token of tokens) {
90
+ if (token.type !== 'color') continue
91
+ const color = cssColorToString(token.value as never)
92
+ if (!color) return null
93
+ return { role, color } as GradientAtomInfo
94
+ }
95
+ return null
96
+ }
97
+
98
+ /**
99
+ * Interpret a `--tw-gradient-position` value list as a direction tag.
100
+ * Tailwind emits plain idents (`to`, `right`, `bottom`, …) for the
101
+ * 8 stock corners.
102
+ * @param tokens Value tokens from `--tw-gradient-position`.
103
+ * @returns Direction record, or null when unrecognised.
104
+ */
105
+ function fromDirection(tokens: readonly TokenOrValue[] | undefined): GradientAtomInfo | null {
106
+ if (!tokens) return null
107
+ const idents: string[] = []
108
+ for (const token of tokens) {
109
+ if (token.type !== 'token') continue
110
+ if (token.value.type === 'ident') idents.push(token.value.value as string)
111
+ }
112
+ const dir = directionFromIdents(idents)
113
+ if (!dir) return null
114
+ return { role: 'direction', dir }
115
+ }
116
+
117
+ /** Table of Tailwind-space direction idents → compact rnwind tag. */
118
+ const DIRECTION_TABLE: Record<string, GradientDirection> = {
119
+ right: 'to-r',
120
+ left: 'to-l',
121
+ top: 'to-t',
122
+ bottom: 'to-b',
123
+ 'top-right': 'to-tr',
124
+ 'top-left': 'to-tl',
125
+ 'bottom-right': 'to-br',
126
+ 'bottom-left': 'to-bl',
127
+ }
128
+
129
+ /**
130
+ * Collapse the ident list Tailwind emits (`['to', 'right']`,
131
+ * `['to', 'bottom', 'right']`, etc.) into the compact tag the
132
+ * transformer hoists.
133
+ * @param idents Ident tokens from the declaration value.
134
+ * @returns One of the eight stock directions, `'unknown'`, or null.
135
+ */
136
+ function directionFromIdents(idents: readonly string[]): GradientDirection | null {
137
+ if (idents.length === 0) return null
138
+ if (idents[0] !== 'to') return 'unknown'
139
+ // Tailwind v4 appends `in <colourspace>` for colour interpolation
140
+ // (`to right in oklab`). Strip everything from the `in` keyword on
141
+ // — it's irrelevant for the point mapping.
142
+ const inIndex = idents.indexOf('in')
143
+ const rest = idents.slice(1, inIndex === -1 ? idents.length : inIndex)
144
+ if (rest.length === 0 || rest.length > 2) return 'unknown'
145
+ return DIRECTION_TABLE[rest.join('-')] ?? 'unknown'
146
+ }
147
+
148
+ export { detectGradientAtom }