meno-core 1.0.53 → 1.0.54

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 (387) hide show
  1. package/.claude/settings.local.json +1 -3
  2. package/bin/cli.ts +48 -57
  3. package/build-astro.ts +131 -113
  4. package/build-next.ts +109 -96
  5. package/build-static.test.ts +39 -10
  6. package/build-static.ts +120 -122
  7. package/dist/bin/cli.js +34 -38
  8. package/dist/bin/cli.js.map +2 -2
  9. package/dist/build-static.js +12 -11
  10. package/dist/chunks/chunk-2AR55GYH.js +42 -0
  11. package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
  12. package/dist/chunks/{chunk-IGYR22T6.js → chunk-2FN4UOVO.js} +512 -239
  13. package/dist/chunks/chunk-2FN4UOVO.js.map +7 -0
  14. package/dist/chunks/chunk-3XER4E5W.js +168 -0
  15. package/dist/chunks/chunk-3XER4E5W.js.map +7 -0
  16. package/dist/chunks/{chunk-X754AHS5.js → chunk-5ETZFREW.js} +9 -12
  17. package/dist/chunks/chunk-5ETZFREW.js.map +7 -0
  18. package/dist/chunks/{chunk-2MHDV5BF.js → chunk-7E4IF5L7.js} +15 -21
  19. package/dist/chunks/chunk-7E4IF5L7.js.map +7 -0
  20. package/dist/chunks/{chunk-JGWFTO6P.js → chunk-7HWQUVTU.js} +1578 -1349
  21. package/dist/chunks/chunk-7HWQUVTU.js.map +7 -0
  22. package/dist/chunks/{chunk-O3NAGJP4.js → chunk-AE3QK5QW.js} +110 -21
  23. package/dist/chunks/chunk-AE3QK5QW.js.map +7 -0
  24. package/dist/chunks/{chunk-GZHGVVW3.js → chunk-F6KTJYGV.js} +7 -7
  25. package/dist/chunks/chunk-F6KTJYGV.js.map +7 -0
  26. package/dist/chunks/{chunk-WQFG7PAH.js → chunk-FZITJSSS.js} +2 -6
  27. package/dist/chunks/chunk-FZITJSSS.js.map +7 -0
  28. package/dist/chunks/{chunk-QB2LNO4W.js → chunk-GSYYA5GX.js} +2 -2
  29. package/dist/chunks/chunk-GSYYA5GX.js.map +7 -0
  30. package/dist/chunks/{chunk-YBLHKYFF.js → chunk-HIZMY3EP.js} +1 -1
  31. package/dist/chunks/chunk-HIZMY3EP.js.map +7 -0
  32. package/dist/chunks/{chunk-AZQYF6KE.js → chunk-I2WEGYA7.js} +41 -176
  33. package/dist/chunks/chunk-I2WEGYA7.js.map +7 -0
  34. package/dist/chunks/{chunk-I7YIGZXT.js → chunk-JNO3CNLJ.js} +6 -9
  35. package/dist/chunks/chunk-JNO3CNLJ.js.map +7 -0
  36. package/dist/chunks/{chunk-UB44F4Z2.js → chunk-NVRBTSQG.js} +2 -4
  37. package/dist/chunks/chunk-NVRBTSQG.js.map +7 -0
  38. package/dist/chunks/{chunk-JGP5A3Y5.js → chunk-Q4OBWKXG.js} +40 -33
  39. package/dist/chunks/chunk-Q4OBWKXG.js.map +7 -0
  40. package/dist/chunks/{chunk-R6XHAFBF.js → chunk-QTE32Y53.js} +250 -242
  41. package/dist/chunks/chunk-QTE32Y53.js.map +7 -0
  42. package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-STDY3OVM.js} +214 -85
  43. package/dist/chunks/chunk-STDY3OVM.js.map +7 -0
  44. package/dist/chunks/configService-PRJZF7Y6.js +14 -0
  45. package/dist/chunks/{constants-STK2YBIW.js → constants-KIQEYMAM.js} +2 -2
  46. package/dist/chunks/{fs-JGINUXGL.js → fs-ZI5JEU7V.js} +2 -2
  47. package/dist/entries/server-router.js +14 -19
  48. package/dist/entries/server-router.js.map +2 -2
  49. package/dist/lib/client/index.js +638 -332
  50. package/dist/lib/client/index.js.map +2 -2
  51. package/dist/lib/server/index.js +177 -235
  52. package/dist/lib/server/index.js.map +2 -2
  53. package/dist/lib/shared/index.js +80 -44
  54. package/dist/lib/shared/index.js.map +2 -2
  55. package/dist/lib/shared/richtext/index.js +1 -1
  56. package/dist/lib/test-utils/index.js +38 -60
  57. package/dist/lib/test-utils/index.js.map +2 -2
  58. package/entries/client-router.tsx +10 -8
  59. package/entries/server-router.tsx +1 -7
  60. package/lib/client/ClientInitializer.ts +8 -8
  61. package/lib/client/ErrorBoundary.test.tsx +146 -143
  62. package/lib/client/ErrorBoundary.tsx +175 -127
  63. package/lib/client/componentRegistry.test.ts +96 -108
  64. package/lib/client/componentRegistry.ts +1 -2
  65. package/lib/client/contexts/ThemeContext.tsx +3 -2
  66. package/lib/client/core/ComponentBuilder.test.ts +513 -560
  67. package/lib/client/core/ComponentBuilder.ts +318 -146
  68. package/lib/client/core/ComponentRenderer.test.tsx +1 -2
  69. package/lib/client/core/ComponentRenderer.tsx +46 -33
  70. package/lib/client/core/builders/embedBuilder.ts +241 -53
  71. package/lib/client/core/builders/linkBuilder.ts +71 -44
  72. package/lib/client/core/builders/linkNodeBuilder.ts +78 -53
  73. package/lib/client/core/builders/listBuilder.ts +115 -86
  74. package/lib/client/core/builders/localeListBuilder.ts +95 -60
  75. package/lib/client/core/builders/types.ts +5 -5
  76. package/lib/client/core/cmsTemplateProcessor.ts +7 -7
  77. package/lib/client/elementRegistry.ts +3 -3
  78. package/lib/client/fontFamiliesService.test.ts +2 -10
  79. package/lib/client/fontFamiliesService.ts +3 -3
  80. package/lib/client/hmr/HMRManager.tsx +8 -0
  81. package/lib/client/hmrCssReload.ts +14 -8
  82. package/lib/client/hmrWebSocket.ts +9 -14
  83. package/lib/client/hooks/useColorVariables.test.ts +21 -21
  84. package/lib/client/hooks/useColorVariables.ts +12 -10
  85. package/lib/client/hooks/usePropertyAutocomplete.ts +3 -5
  86. package/lib/client/hooks/useVariables.ts +4 -4
  87. package/lib/client/hydration/HydrationUtils.test.ts +24 -25
  88. package/lib/client/hydration/HydrationUtils.ts +3 -4
  89. package/lib/client/i18nConfigService.test.ts +2 -7
  90. package/lib/client/i18nConfigService.ts +2 -2
  91. package/lib/client/meno-filter/MenoFilter.test.ts +19 -21
  92. package/lib/client/meno-filter/MenoFilter.ts +5 -9
  93. package/lib/client/meno-filter/bindings.ts +15 -40
  94. package/lib/client/meno-filter/init.ts +1 -1
  95. package/lib/client/meno-filter/renderer.ts +23 -29
  96. package/lib/client/meno-filter/script.generated.ts +1 -3
  97. package/lib/client/meno-filter/ui.ts +3 -5
  98. package/lib/client/meno-filter/updates.ts +15 -21
  99. package/lib/client/navigation.test.ts +159 -159
  100. package/lib/client/navigation.ts +0 -1
  101. package/lib/client/responsiveStyleResolver.test.ts +230 -228
  102. package/lib/client/responsiveStyleResolver.ts +13 -16
  103. package/lib/client/routing/RouteLoader.test.ts +23 -24
  104. package/lib/client/routing/RouteLoader.ts +22 -35
  105. package/lib/client/routing/Router.tsx +31 -3
  106. package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
  107. package/lib/client/scripts/ScriptExecutor.ts +15 -33
  108. package/lib/client/services/PrefetchService.test.ts +2 -2
  109. package/lib/client/services/PrefetchService.ts +10 -24
  110. package/lib/client/styleProcessor.test.ts +9 -9
  111. package/lib/client/styleProcessor.ts +18 -15
  112. package/lib/client/styles/StyleInjector.test.ts +122 -115
  113. package/lib/client/styles/StyleInjector.ts +9 -6
  114. package/lib/client/styles/UtilityClassCollector.ts +19 -26
  115. package/lib/client/styles/cspNonce.test.ts +2 -5
  116. package/lib/client/templateEngine.test.ts +554 -482
  117. package/lib/client/templateEngine.ts +200 -64
  118. package/lib/client/theme.ts +0 -1
  119. package/lib/client/utils/toast.ts +0 -1
  120. package/lib/server/__integration__/api-routes.test.ts +8 -4
  121. package/lib/server/__integration__/cms-integration.test.ts +1 -4
  122. package/lib/server/__integration__/server-lifecycle.test.ts +2 -5
  123. package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
  124. package/lib/server/__integration__/static-assets.test.ts +1 -1
  125. package/lib/server/__integration__/test-helpers.ts +84 -70
  126. package/lib/server/ab/generateFunctions.ts +12 -10
  127. package/lib/server/astro/cmsPageEmitter.ts +23 -38
  128. package/lib/server/astro/componentEmitter.ts +25 -36
  129. package/lib/server/astro/cssCollector.ts +10 -26
  130. package/lib/server/astro/nodeToAstro.test.ts +1750 -30
  131. package/lib/server/astro/nodeToAstro.ts +198 -187
  132. package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +105 -109
  133. package/lib/server/astro/normalizeOrphanTemplateProps.ts +4 -12
  134. package/lib/server/astro/pageEmitter.ts +9 -13
  135. package/lib/server/astro/tailwindMapper.test.ts +10 -37
  136. package/lib/server/astro/tailwindMapper.ts +33 -40
  137. package/lib/server/astro/templateTransformer.ts +14 -17
  138. package/lib/server/createServer.ts +5 -17
  139. package/lib/server/cssGenerator.test.ts +35 -44
  140. package/lib/server/cssGenerator.ts +6 -17
  141. package/lib/server/fileWatcher.test.ts +124 -10
  142. package/lib/server/fileWatcher.ts +124 -118
  143. package/lib/server/index.ts +7 -1
  144. package/lib/server/jsonLoader.test.ts +39 -2
  145. package/lib/server/jsonLoader.ts +33 -31
  146. package/lib/server/middleware/cors.test.ts +20 -20
  147. package/lib/server/middleware/cors.ts +28 -4
  148. package/lib/server/middleware/errorHandler.test.ts +5 -3
  149. package/lib/server/middleware/errorHandler.ts +3 -8
  150. package/lib/server/middleware/index.ts +0 -1
  151. package/lib/server/middleware/logger.test.ts +7 -5
  152. package/lib/server/middleware/logger.ts +10 -22
  153. package/lib/server/pageCache.test.ts +76 -77
  154. package/lib/server/pageCache.ts +0 -1
  155. package/lib/server/projectContext.ts +4 -3
  156. package/lib/server/providers/fileSystemCMSProvider.test.ts +124 -95
  157. package/lib/server/providers/fileSystemCMSProvider.ts +35 -20
  158. package/lib/server/providers/fileSystemPageProvider.test.ts +84 -0
  159. package/lib/server/providers/fileSystemPageProvider.ts +31 -12
  160. package/lib/server/routes/api/cms.test.ts +26 -14
  161. package/lib/server/routes/api/cms.ts +9 -14
  162. package/lib/server/routes/api/components.ts +30 -34
  163. package/lib/server/routes/api/config.ts +0 -1
  164. package/lib/server/routes/api/core-routes.ts +47 -74
  165. package/lib/server/routes/api/functions.ts +8 -16
  166. package/lib/server/routes/api/index.ts +3 -6
  167. package/lib/server/routes/api/pages.ts +8 -11
  168. package/lib/server/routes/api/shared.test.ts +1 -1
  169. package/lib/server/routes/api/shared.ts +12 -9
  170. package/lib/server/routes/api/variables.test.ts +1 -3
  171. package/lib/server/routes/api/variables.ts +1 -1
  172. package/lib/server/routes/index.ts +23 -26
  173. package/lib/server/routes/pages.ts +34 -29
  174. package/lib/server/routes/static.ts +16 -4
  175. package/lib/server/runtime/bundler.ts +47 -32
  176. package/lib/server/runtime/fs.ts +3 -13
  177. package/lib/server/runtime/httpServer.ts +5 -9
  178. package/lib/server/services/ColorService.ts +32 -27
  179. package/lib/server/services/EnumService.test.ts +2 -6
  180. package/lib/server/services/EnumService.ts +7 -2
  181. package/lib/server/services/VariableService.test.ts +1 -5
  182. package/lib/server/services/VariableService.ts +6 -1
  183. package/lib/server/services/cmsService.test.ts +116 -78
  184. package/lib/server/services/cmsService.ts +24 -54
  185. package/lib/server/services/componentService.test.ts +122 -36
  186. package/lib/server/services/componentService.ts +54 -38
  187. package/lib/server/services/configService.test.ts +9 -31
  188. package/lib/server/services/configService.ts +20 -27
  189. package/lib/server/services/fileWatcherService.ts +40 -6
  190. package/lib/server/services/index.ts +0 -1
  191. package/lib/server/services/pageService.test.ts +1 -3
  192. package/lib/server/services/pageService.ts +14 -13
  193. package/lib/server/ssr/attributeBuilder.ts +16 -6
  194. package/lib/server/ssr/buildErrorOverlay.ts +11 -12
  195. package/lib/server/ssr/clientDataInjector.ts +7 -21
  196. package/lib/server/ssr/cmsSSRProcessor.ts +3 -6
  197. package/lib/server/ssr/cssCollector.ts +1 -1
  198. package/lib/server/ssr/errorOverlay.test.ts +1 -1
  199. package/lib/server/ssr/errorOverlay.ts +3 -9
  200. package/lib/server/ssr/htmlGenerator.nonce.test.ts +3 -9
  201. package/lib/server/ssr/htmlGenerator.test.ts +120 -43
  202. package/lib/server/ssr/htmlGenerator.ts +120 -85
  203. package/lib/server/ssr/imageMetadata.test.ts +3 -1
  204. package/lib/server/ssr/imageMetadata.ts +25 -19
  205. package/lib/server/ssr/jsCollector.test.ts +3 -13
  206. package/lib/server/ssr/jsCollector.ts +3 -8
  207. package/lib/server/ssr/liveReloadIntegration.test.ts +69 -28
  208. package/lib/server/ssr/metaTagGenerator.ts +2 -2
  209. package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
  210. package/lib/server/ssr/ssrRenderer.test.ts +196 -246
  211. package/lib/server/ssr/ssrRenderer.ts +609 -225
  212. package/lib/server/ssrRenderer.test.ts +1044 -950
  213. package/lib/server/utils/jsonLineMapper.test.ts +28 -28
  214. package/lib/server/utils/jsonLineMapper.ts +1 -1
  215. package/lib/server/validateStyleCoverage.ts +18 -20
  216. package/lib/server/webflow/buildWebflow.ts +41 -53
  217. package/lib/server/webflow/nodeToWebflow.test.ts +150 -218
  218. package/lib/server/webflow/nodeToWebflow.ts +195 -258
  219. package/lib/server/webflow/styleMapper.test.ts +15 -56
  220. package/lib/server/webflow/styleMapper.ts +33 -41
  221. package/lib/server/webflow/types.ts +1 -8
  222. package/lib/server/websocketManager.ts +16 -19
  223. package/lib/shared/attributeNodeUtils.test.ts +15 -15
  224. package/lib/shared/attributeNodeUtils.ts +5 -12
  225. package/lib/shared/breakpoints.ts +4 -11
  226. package/lib/shared/cmsQueryParser.test.ts +50 -42
  227. package/lib/shared/cmsQueryParser.ts +49 -31
  228. package/lib/shared/colorVariableUtils.test.ts +5 -5
  229. package/lib/shared/colorVariableUtils.ts +3 -8
  230. package/lib/shared/componentRefs.ts +1 -5
  231. package/lib/shared/constants.test.ts +3 -3
  232. package/lib/shared/constants.ts +4 -8
  233. package/lib/shared/cssGeneration.test.ts +262 -144
  234. package/lib/shared/cssGeneration.ts +189 -516
  235. package/lib/shared/cssNamedColors.ts +152 -30
  236. package/lib/shared/cssProperties.test.ts +5 -6
  237. package/lib/shared/cssProperties.ts +296 -112
  238. package/lib/shared/elementClassName.test.ts +109 -109
  239. package/lib/shared/elementClassName.ts +1 -1
  240. package/lib/shared/elementUtils.ts +12 -16
  241. package/lib/shared/errorLogger.ts +2 -10
  242. package/lib/shared/errors.test.ts +2 -13
  243. package/lib/shared/errors.ts +2 -8
  244. package/lib/shared/expressionEvaluator.test.ts +119 -0
  245. package/lib/shared/expressionEvaluator.ts +43 -24
  246. package/lib/shared/fontLoader.test.ts +19 -5
  247. package/lib/shared/friendlyError.ts +2 -3
  248. package/lib/shared/gradientUtils.test.ts +1 -5
  249. package/lib/shared/gradientUtils.ts +2 -6
  250. package/lib/shared/hrefRefs.ts +2 -10
  251. package/lib/shared/i18n.test.ts +1 -1
  252. package/lib/shared/i18n.ts +13 -34
  253. package/lib/shared/index.ts +4 -0
  254. package/lib/shared/inlineSvgStyleRules.ts +2 -4
  255. package/lib/shared/interactiveStyleMappings.test.ts +11 -33
  256. package/lib/shared/interactiveStyleMappings.ts +9 -16
  257. package/lib/shared/interactiveStyles.test.ts +165 -188
  258. package/lib/shared/interfaces/contentProvider.ts +1 -1
  259. package/lib/shared/itemTemplateUtils.test.ts +6 -12
  260. package/lib/shared/itemTemplateUtils.ts +19 -35
  261. package/lib/shared/jsonRepair.ts +8 -2
  262. package/lib/shared/libraryLoader.test.ts +15 -49
  263. package/lib/shared/libraryLoader.ts +7 -22
  264. package/lib/shared/netlifyLocale404.test.ts +179 -0
  265. package/lib/shared/netlifyLocale404.ts +110 -0
  266. package/lib/shared/nodeUtils.test.ts +24 -16
  267. package/lib/shared/nodeUtils.ts +49 -19
  268. package/lib/shared/pathArrayUtils.test.ts +1 -2
  269. package/lib/shared/pathArrayUtils.ts +1 -1
  270. package/lib/shared/pathSecurity.ts +1 -1
  271. package/lib/shared/pathUtils.test.ts +4 -6
  272. package/lib/shared/pathUtils.ts +42 -48
  273. package/lib/shared/paths/Path.test.ts +2 -2
  274. package/lib/shared/paths/Path.ts +0 -1
  275. package/lib/shared/paths/PathConverter.test.ts +1 -1
  276. package/lib/shared/paths/PathConverter.ts +14 -17
  277. package/lib/shared/paths/PathUtils.ts +9 -10
  278. package/lib/shared/paths/PathValidator.test.ts +2 -15
  279. package/lib/shared/paths/PathValidator.ts +11 -9
  280. package/lib/shared/paths/index.ts +1 -2
  281. package/lib/shared/propResolver.test.ts +240 -244
  282. package/lib/shared/propResolver.ts +14 -25
  283. package/lib/shared/pxToRem.test.ts +7 -6
  284. package/lib/shared/pxToRem.ts +2 -5
  285. package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +9 -5
  286. package/lib/shared/registry/ClientRegistry.ts +0 -1
  287. package/lib/shared/registry/ComponentRegistry.test.ts +43 -29
  288. package/lib/shared/registry/ComponentRegistry.ts +9 -11
  289. package/lib/shared/registry/NodeTypeDefinition.ts +15 -8
  290. package/lib/shared/registry/RegistryManager.ts +1 -2
  291. package/lib/shared/registry/SSRRegistry.ts +0 -1
  292. package/lib/shared/registry/createNodeType.ts +7 -9
  293. package/lib/shared/registry/defineNodeType.ts +2 -6
  294. package/lib/shared/registry/index.ts +0 -1
  295. package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
  296. package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
  297. package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +47 -18
  298. package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
  299. package/lib/shared/registry/nodeTypes/ListNodeType.ts +78 -74
  300. package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
  301. package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +6 -7
  302. package/lib/shared/registry/nodeTypes/index.ts +10 -2
  303. package/lib/shared/responsiveScaling.test.ts +15 -31
  304. package/lib/shared/responsiveScaling.ts +55 -37
  305. package/lib/shared/responsiveStyleUtils.ts +11 -13
  306. package/lib/shared/richtext/htmlToTiptap.test.ts +23 -14
  307. package/lib/shared/richtext/htmlToTiptap.ts +1 -3
  308. package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
  309. package/lib/shared/richtext/types.ts +1 -8
  310. package/lib/shared/slugTranslator.test.ts +13 -13
  311. package/lib/shared/slugTranslator.ts +12 -16
  312. package/lib/shared/slugify.ts +9 -15
  313. package/lib/shared/styleNodeUtils.test.ts +8 -8
  314. package/lib/shared/styleNodeUtils.ts +5 -10
  315. package/lib/shared/styleUtils.test.ts +87 -61
  316. package/lib/shared/styleUtils.ts +5 -6
  317. package/lib/shared/themeDefaults.test.ts +11 -11
  318. package/lib/shared/themeDefaults.ts +3 -4
  319. package/lib/shared/tree/PathBuilder.test.ts +62 -138
  320. package/lib/shared/tree/PathBuilder.ts +49 -39
  321. package/lib/shared/treePathUtils.test.ts +2 -10
  322. package/lib/shared/treePathUtils.ts +54 -59
  323. package/lib/shared/types/api.ts +1 -2
  324. package/lib/shared/types/cms.ts +25 -21
  325. package/lib/shared/types/comment.ts +49 -16
  326. package/lib/shared/types/components.ts +27 -25
  327. package/lib/shared/types/errors.test.ts +1 -6
  328. package/lib/shared/types/errors.ts +3 -7
  329. package/lib/shared/types/experiments.ts +28 -28
  330. package/lib/shared/types/index.ts +2 -2
  331. package/lib/shared/types/styles.ts +0 -1
  332. package/lib/shared/types/variables.test.ts +4 -13
  333. package/lib/shared/types/variables.ts +48 -27
  334. package/lib/shared/types.ts +1 -2
  335. package/lib/shared/utilityClassConfig.ts +648 -321
  336. package/lib/shared/utilityClassMapper.test.ts +203 -92
  337. package/lib/shared/utilityClassMapper.ts +188 -246
  338. package/lib/shared/utilityClassNames.ts +326 -0
  339. package/lib/shared/utils.test.ts +2 -10
  340. package/lib/shared/utils.ts +19 -10
  341. package/lib/shared/validation/cmsValidators.ts +2 -1
  342. package/lib/shared/validation/commentValidators.test.ts +53 -0
  343. package/lib/shared/validation/commentValidators.ts +12 -1
  344. package/lib/shared/validation/propValidator.test.ts +18 -20
  345. package/lib/shared/validation/propValidator.ts +12 -17
  346. package/lib/shared/validation/schemas.test.ts +24 -33
  347. package/lib/shared/validation/schemas.ts +469 -344
  348. package/lib/shared/validation/validators.test.ts +1 -6
  349. package/lib/shared/validation/validators.ts +89 -68
  350. package/lib/shared/viewportUnits.integration.test.ts +14 -10
  351. package/lib/shared/viewportUnits.test.ts +11 -23
  352. package/lib/test-utils/dom-setup.ts +1 -1
  353. package/lib/test-utils/factories/ConsoleMockFactory.ts +3 -7
  354. package/lib/test-utils/factories/DomMockFactory.ts +7 -19
  355. package/lib/test-utils/factories/EventMockFactory.ts +7 -13
  356. package/lib/test-utils/factories/FetchMockFactory.ts +39 -57
  357. package/lib/test-utils/factories/ServerMockFactory.ts +5 -9
  358. package/lib/test-utils/factories/StoreMockFactory.ts +14 -25
  359. package/lib/test-utils/fixtures.ts +45 -45
  360. package/lib/test-utils/helpers/asyncHelpers.test.ts +15 -18
  361. package/lib/test-utils/helpers/asyncHelpers.ts +11 -20
  362. package/lib/test-utils/helpers.ts +1 -5
  363. package/lib/test-utils/index.ts +0 -4
  364. package/lib/test-utils/mockFactories.ts +12 -18
  365. package/lib/test-utils/mocks.ts +4 -2
  366. package/package.json +1 -1
  367. package/scripts/build-meno-filter.ts +1 -4
  368. package/vite.config.ts +4 -4
  369. package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
  370. package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
  371. package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
  372. package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
  373. package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
  374. package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
  375. package/dist/chunks/chunk-JGP5A3Y5.js.map +0 -7
  376. package/dist/chunks/chunk-JGWFTO6P.js.map +0 -7
  377. package/dist/chunks/chunk-O3NAGJP4.js.map +0 -7
  378. package/dist/chunks/chunk-QB2LNO4W.js.map +0 -7
  379. package/dist/chunks/chunk-R6XHAFBF.js.map +0 -7
  380. package/dist/chunks/chunk-UB44F4Z2.js.map +0 -7
  381. package/dist/chunks/chunk-WQFG7PAH.js.map +0 -7
  382. package/dist/chunks/chunk-X754AHS5.js.map +0 -7
  383. package/dist/chunks/chunk-YBLHKYFF.js.map +0 -7
  384. package/dist/chunks/configService-R3OGU2UD.js +0 -13
  385. /package/dist/chunks/{configService-R3OGU2UD.js.map → configService-PRJZF7Y6.js.map} +0 -0
  386. /package/dist/chunks/{constants-STK2YBIW.js.map → constants-KIQEYMAM.js.map} +0 -0
  387. /package/dist/chunks/{fs-JGINUXGL.js.map → fs-ZI5JEU7V.js.map} +0 -0
@@ -1,19 +1,36 @@
1
1
  /**
2
- * Maps CSS property values to utility class names
3
- * Example: { padding: "10px", display: "flex" } → ["p-10px", "flex"]
2
+ * Maps CSS property values to Tailwind-style utility class names
3
+ * Example: { padding: "10px", display: "flex" } → ["p-[10px]", "flex"]
4
4
  * Uses centralized configuration from utilityClassConfig.ts
5
5
  */
6
6
 
7
7
  import type { ResponsiveStyleObject, StyleObject, StyleMapping } from './types';
8
8
  import {
9
9
  propertyMap,
10
- specialValueMappings,
11
- classToStyleSpecialCases,
10
+ propertyOrder,
11
+ staticUtilities,
12
+ staticUtilityReverse,
13
+ presetClassReverse,
14
+ keywordValues,
15
+ genericKeywordRoots,
16
+ integerValueRoots,
17
+ colorTokenProps,
12
18
  shadowPresets,
13
19
  gradientPresets,
14
20
  borderPresets,
15
21
  } from './utilityClassConfig';
16
- import { registerStyleValue, registerDynamicStyle, getStyleValue } from './styleValueRegistry';
22
+ import {
23
+ camelToKebab,
24
+ cssPropertyToCamel,
25
+ encodeArbitraryValue,
26
+ parseUtilityClass,
27
+ splitVariantPrefix,
28
+ breakpointClassPrefix,
29
+ resolveRootProperty,
30
+ ROOT_CAMEL_OVERRIDES,
31
+ isColorValue,
32
+ } from './utilityClassNames';
33
+ import { registerStyleValue, registerDynamicStyle, getStyleValue, getDynamicStyle } from './styleValueRegistry';
17
34
  import {
18
35
  SCALABLE_CSS_PROPERTIES,
19
36
  buildFluidClampWithExplicitMin,
@@ -22,43 +39,13 @@ import {
22
39
  type ResponsiveScales,
23
40
  } from './responsiveScaling';
24
41
 
25
- // Function name abbreviations for shorter class names
26
- const functionAbbreviations: Record<string, string> = {
27
- blur: 'bl',
28
- translateY: 'ty',
29
- translateX: 'tx',
30
- translate: 't',
31
- scale: 'sc',
32
- scaleX: 'scx',
33
- scaleY: 'scy',
34
- rotate: 'ro',
35
- skew: 'sk',
36
- skewX: 'skx',
37
- skewY: 'sky',
38
- repeat: 're',
39
- minmax: 'mm',
40
- calc: 'ca',
41
- };
42
-
43
42
  /**
44
43
  * Check if a value is a StyleMapping object
45
44
  */
46
45
  function isStyleMapping(value: unknown): value is StyleMapping {
47
- return typeof value === 'object' && value !== null && '_mapping' in value && (value as StyleMapping)._mapping === true;
48
- }
49
-
50
- /**
51
- * Sanitize a value for use in a CSS class name
52
- * Replaces special characters with safe alternatives
53
- */
54
- function sanitizeClassValue(value: string): string {
55
- return value
56
- .replace(/\s+/g, '-') // spaces to hyphens
57
- .replace(/[()]/g, '') // remove parentheses
58
- .replace(/,/g, '_') // commas to underscores
59
- .replace(/%/g, 'p') // percent to 'p'
60
- .replace(/\./g, 'd') // dots to 'd' (for decimals)
61
- .replace(/[^\w-]/g, ''); // remove other special chars
46
+ return (
47
+ typeof value === 'object' && value !== null && '_mapping' in value && (value as StyleMapping)._mapping === true
48
+ );
62
49
  }
63
50
 
64
51
  /**
@@ -76,166 +63,145 @@ function shortHash(input: string): string {
76
63
  }
77
64
 
78
65
  /**
79
- * True if a class name contains characters illegal in CSS selectors. We
80
- * generate dynamic class names from CSS values; complex values (especially
81
- * Webflow `clamp(2*1rem, ((..2)/(90 - 20)*20)*1rem + ..*100vw, 4*1rem)`)
82
- * end up with `*`, `(`, `)`, `+`, `,` baked into the class name, none of
83
- * which are valid in CSS selectors. Browser silently drops the rule and
84
- * the styled property reverts to UA defaults. Hash-fallback gives us a
85
- * short, valid-CSS class name, with the actual value resolved via the
86
- * style-value registry at CSS generation time.
66
+ * True for a bare Meno color token (e.g. `primary`, `background-light`) that
67
+ * should resolve to `var(--token)`. Named CSS colors, hex/function colors and
68
+ * values starting with digits are NOT tokens.
87
69
  */
88
- function hasIllegalClassChars(className: string): boolean {
89
- return /[^\w-]/.test(className);
70
+ function isBareColorToken(value: string): boolean {
71
+ if (!/^[a-zA-Z][\w-]*$/.test(value)) return false;
72
+ if (isColorValue(value)) return false; // named colors / transparent / currentColor
73
+ return true;
90
74
  }
91
75
 
92
76
  /**
93
- * Converts a CSS property value to a utility class name
94
- * Example: { prop: "padding", value: "10px" } → "p-10px"
77
+ * Converts a CSS property value to a Tailwind-style utility class name.
78
+ * Examples:
79
+ * { prop: "padding", value: "10px" } → "p-[10px]"
80
+ * { prop: "display", value: "flex" } → "flex"
81
+ * { prop: "color", value: "primary" } → "text-(--primary)"
82
+ * { prop: "width", value: "100%" } → "w-full"
83
+ * { prop: "textOverflow", value: "ellipsis" } → "[text-overflow:ellipsis]"
95
84
  *
96
- * For properties not in propertyMap, generates a dynamic class using
97
- * a shortened property name prefix (e.g., "textOverflow" → "txov-ellipsis")
85
+ * Values that can't be encoded reversibly fall back to a hash class
86
+ * (`pt-h1y9pr6i`) resolved through the style-value registry.
98
87
  */
99
88
  function propertyValueToClass(prop: string, value: string | number): string | null {
100
- const prefix = propertyMap[prop];
101
-
102
- // For properties not in propertyMap, generate a dynamic fallback class
103
- if (!prefix) {
104
- const stringValue = String(value);
105
- // Generate a short prefix from the property name (first 2-4 chars of each word)
106
- // e.g., "textOverflow" → "txov", "WebkitBackgroundClip" → "wbbg"
107
- const dynamicPrefix = prop
108
- .replace(/([A-Z])/g, '-$1') // camelCase to kebab
109
- .toLowerCase()
110
- .split('-')
111
- .filter(Boolean)
112
- .map(word => word.slice(0, 2)) // first 2 chars of each word
113
- .join('');
114
-
115
- // Sanitize value for class name
116
- const sanitizedValue = sanitizeClassValue(stringValue);
117
- const className = `${dynamicPrefix}-${sanitizedValue}`;
118
-
119
- // Convert camelCase to kebab-case for CSS property
120
- const cssProperty = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
121
-
122
- // Register in dynamic registry with property info
123
- registerDynamicStyle(className, cssProperty, value);
124
- return className;
125
- }
126
-
127
89
  let stringValue = String(value);
128
90
 
129
91
  // Strip outer quotes from fontFamily values (e.g. "Space Grotesk" → Space Grotesk)
130
92
  if (prop === 'fontFamily') {
131
- stringValue = stringValue.split(',').map(font => {
132
- const trimmed = font.trim();
133
- if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
134
- (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
135
- return trimmed.slice(1, -1);
136
- }
137
- return trimmed;
138
- }).join(', ');
93
+ stringValue = stringValue
94
+ .split(',')
95
+ .map((font) => {
96
+ const trimmed = font.trim();
97
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
98
+ return trimmed.slice(1, -1);
99
+ }
100
+ return trimmed;
101
+ })
102
+ .join(', ');
139
103
  }
140
104
 
141
- let className: string | null = null;
142
-
143
- // Check for special value mappings first
144
- if (specialValueMappings[prop]) {
145
- const mapping = specialValueMappings[prop];
146
- if (mapping[stringValue]) {
147
- className = mapping[stringValue];
148
- }
105
+ // Static utilities (Tailwind names): display: flex "flex", textAlign: center → "text-center"
106
+ const staticClass = staticUtilities[prop]?.[stringValue];
107
+ if (staticClass) {
108
+ registerStyleValue(staticClass, stringValue);
109
+ return staticClass;
149
110
  }
150
111
 
151
- // Check for preset values (e.g., boxShadow: "2" → sh-2)
152
- if (!className && prop === 'boxShadow' && shadowPresets[stringValue]) {
153
- className = `sh-${stringValue}`;
112
+ // Preset values (e.g., boxShadow: "2" → shadow-2)
113
+ if (prop === 'boxShadow' && shadowPresets[stringValue]) {
114
+ const className = `shadow-${stringValue}`;
115
+ registerStyleValue(className, shadowPresets[stringValue]);
116
+ return className;
154
117
  }
155
- if (!className && (prop === 'background' || prop === 'backgroundImage') && gradientPresets[stringValue]) {
156
- className = `gr-${stringValue}`;
118
+ if ((prop === 'background' || prop === 'backgroundImage') && gradientPresets[stringValue]) {
119
+ const className = `bg-gradient-${stringValue}`;
120
+ registerStyleValue(className, gradientPresets[stringValue]);
121
+ return className;
157
122
  }
158
- if (!className && prop === 'border' && borderPresets[stringValue]) {
159
- className = `bd-${stringValue}`;
123
+ if (prop === 'border' && borderPresets[stringValue]) {
124
+ const className = `border-preset-${stringValue}`;
125
+ registerStyleValue(className, borderPresets[stringValue]);
126
+ return className;
160
127
  }
161
128
 
162
- // Handle CSS function values: blur(), translateY(), rotate(), scale(), repeat(), etc.
163
- // Uses abbreviations for shorter class names: blur(8px) → bl8, translateY(-15px) → tyn15
164
- if (!className && stringValue.includes('(') && stringValue.includes(')')) {
165
- const funcMatch = stringValue.match(/^([a-zA-Z]+)\((.+)\)$/);
166
- if (funcMatch) {
167
- const [, funcName, args] = funcMatch;
168
- const abbrev = functionAbbreviations[funcName] || funcName.slice(0, 2);
169
- const argNormalized = args
170
- .replace(/,\s*/g, '-') // "3, 1fr" → "3-1fr"
171
- .replace(/\s+/g, '') // Remove spaces
172
- .replace(/-(\d)/g, 'n$1') // "-15px" → "n15px"
173
- .replace(/px$/i, '') // Remove trailing px for brevity
174
- .replace(/%$/, 'p'); // "50%" → "50p"
175
- className = `${prefix}-${abbrev}${argNormalized}`;
176
- }
177
- }
129
+ const root = propertyMap[prop];
178
130
 
179
- // CSS variables (e.g., var(--background))
180
- if (!className && stringValue.includes('var(')) {
181
- const varMatch = stringValue.match(/var\((--[\w-]+)\)/);
182
- if (varMatch) {
183
- const varName = varMatch[1].replace('--', '');
184
- className = `${prefix}-${varName}`;
185
- }
131
+ // Properties without a Tailwind root use arbitrary-property syntax:
132
+ // textOverflow: ellipsis "[text-overflow:ellipsis]"
133
+ if (!root) {
134
+ const cssProperty = camelToKebab(prop);
135
+ const encoded = encodeArbitraryValue(stringValue);
136
+ const className = encoded != null ? `[${cssProperty}:${encoded}]` : `${cssProperty}-h${shortHash(stringValue)}`;
137
+ registerDynamicStyle(className, cssProperty, value);
138
+ return className;
186
139
  }
187
140
 
188
- // Detect variable names for color/background properties (e.g., color: "text" → c-text)
189
- // These are stored as plain names without var() wrapper and should be treated as variables
190
- if (!className &&
191
- (prop === 'color' || prop === 'backgroundColor' || prop === 'borderColor') &&
192
- !stringValue.match(/^\d/) && !stringValue.includes('#') && !stringValue.includes('rgb')) {
193
- className = `${prefix}-${stringValue}`;
194
- }
141
+ let className: string | null = null;
195
142
 
196
- // Direct values (with units)
197
- if (!className && (stringValue.match(/^\d+px$/) || stringValue.match(/^\d+em$/))) {
198
- className = `${prefix}-${stringValue}`;
143
+ // Named keyword values: width: 100% → w-full, margin: auto → m-auto
144
+ const kwTable = keywordValues[root];
145
+ if (kwTable) {
146
+ for (const [keyword, cssValue] of Object.entries(kwTable)) {
147
+ if (cssValue === stringValue) {
148
+ className = `${root}-${keyword}`;
149
+ break;
150
+ }
151
+ }
199
152
  }
200
153
 
201
- // Percentage values - convert % to p (e.g., "50%""50p")
202
- if (!className && stringValue.match(/^\d+%$/)) {
203
- const percentValue = stringValue.replace('%', 'p');
204
- className = `${prefix}-${percentValue}`;
154
+ // Generic keyword roots: cursor: pointer cursor-pointer, transformOrigin: centerorigin-center
155
+ if (!className && genericKeywordRoots.has(root) && /^[a-z][a-z-]*$/.test(stringValue)) {
156
+ className = `${root}-${stringValue}`;
205
157
  }
206
158
 
207
- // Handle values with spaces by replacing spaces with hyphens (e.g., "92px 0" "p-92px-0")
208
- if (!className && stringValue.includes(' ')) {
209
- const spacedValue = stringValue.replace(/\s+/g, '-');
210
- className = `${prefix}-${spacedValue}`;
159
+ // Bare numeric values Tailwind-style: zIndex: 10 z-10, opacity: 0.5opacity-50
160
+ if (!className && integerValueRoots.has(root) && /^-?\d+$/.test(stringValue)) {
161
+ className = `${root}-${stringValue}`;
162
+ }
163
+ if (!className && prop === 'opacity') {
164
+ const num = Number(stringValue);
165
+ if (Number.isFinite(num) && num >= 0 && num <= 1) {
166
+ const scaled = Math.round(num * 100);
167
+ if (Math.abs(num * 100 - scaled) < 1e-9) {
168
+ className = `opacity-${scaled}`;
169
+ }
170
+ }
211
171
  }
212
172
 
213
- // Special handling for margin/padding "auto"
214
- if (!className && stringValue === 'auto') {
215
- className = `${prefix}-auto`;
173
+ // CSS variables: var(--x) root-(--x). The `font-*` root is shared by font-weight and
174
+ // font-family and is disambiguated by value type — but a `var(--x)` value is ambiguous, so
175
+ // weight/size vars carry a Tailwind data-type hint (`number:` / `length:`) the parser reads
176
+ // back. Without it a `fontWeight: var(--h-fw)` binding would resolve to `font-family` (an
177
+ // invalid `font-family: 450`), dropping the weight entirely.
178
+ if (!className) {
179
+ const varMatch = stringValue.match(/^var\((--[\w-]+)\)$/);
180
+ if (varMatch) {
181
+ if (prop === 'fontSize') className = `text-(length:${varMatch[1]})`;
182
+ else if (prop === 'fontWeight') className = `font-(number:${varMatch[1]})`;
183
+ else className = `${root}-(${varMatch[1]})`;
184
+ }
216
185
  }
217
186
 
218
- // Handle values with slash (e.g., aspectRatio "1/1" → "ar-1s1")
219
- if (!className && stringValue.includes('/')) {
220
- const slashValue = stringValue.replace(/\//g, 's');
221
- className = `${prefix}-${slashValue}`;
187
+ // Bare color tokens (Meno convention): color: "primary" → text-(--primary)
188
+ if (!className && colorTokenProps.has(prop) && isBareColorToken(stringValue)) {
189
+ className = `${root}-(--${stringValue})`;
222
190
  }
223
191
 
224
- // For other cases, append the value
192
+ // Arbitrary value (Tailwind bracket syntax): padding: "92px 0" → p-[92px_0]
225
193
  if (!className) {
226
- className = `${prefix}-${stringValue}`;
194
+ const encoded = encodeArbitraryValue(stringValue);
195
+ if (encoded != null) {
196
+ className = `${root}-[${encoded}]`;
197
+ }
227
198
  }
228
199
 
229
- // Hash-fallback for class names with illegal CSS selector chars. Webflow's
230
- // fluid `clamp(2*1rem, ((2 - .5)/70*20)*1rem + ..*100vw, 4*1rem)` would
231
- // otherwise produce `pt-cl2*1rem-((2-.5)/70*20)*1rem+..*100vw-4*1rem`,
232
- // with `*`, `(`, `)`, `+`, `,` — none of which are valid in CSS selectors.
233
- // Browser drops the rule, the styled element reverts to defaults
234
- // (paddings collapse to 0, etc.). Short FNV-1a hash + style-value
235
- // registry preserves the original value for the CSS generator without
236
- // baking it into the selector.
237
- if (hasIllegalClassChars(className)) {
238
- className = `${prefix}-h${shortHash(stringValue)}`;
200
+ // Hash-fallback for values that can't be encoded reversibly (quotes,
201
+ // brackets, literal underscores). The original value is preserved in the
202
+ // style-value registry for the CSS generator.
203
+ if (!className) {
204
+ className = `${root}-h${shortHash(stringValue)}`;
239
205
  }
240
206
 
241
207
  // Register original value for CSS generation (single registration point)
@@ -243,24 +209,19 @@ function propertyValueToClass(prop: string, value: string | number): string | nu
243
209
  return className;
244
210
  }
245
211
 
246
- // Get property order from propertyMap for consistent class ordering
247
- const propertyOrder = Object.keys(propertyMap);
248
-
249
212
  /**
250
213
  * Convert a style object to an array of utility classes
251
- * Example: { padding: "10px", display: "flex" } → ["p-10px", "flex"]
214
+ * Example: { padding: "10px", display: "flex" } → ["p-[10px]", "flex"]
252
215
  *
253
- * Properties are sorted according to propertyMap order to ensure
216
+ * Properties are sorted according to propertyOrder to ensure
254
217
  * dependent properties (like borderColor) come after their base (border)
255
218
  *
256
219
  * StyleMapping values are filtered out - they are handled at render time
257
220
  */
258
- export function stylesToClasses(
259
- styles: StyleObject | Record<string, string | number> | null | undefined
260
- ): string[] {
221
+ export function stylesToClasses(styles: StyleObject | Record<string, string | number> | null | undefined): string[] {
261
222
  if (!styles) return [];
262
223
 
263
- // Sort properties by their order in propertyMap
224
+ // Sort properties by their canonical order
264
225
  // This ensures border comes before borderColor, etc.
265
226
  const sortedProps = Object.keys(styles).sort((a, b) => {
266
227
  const indexA = propertyOrder.indexOf(a);
@@ -287,10 +248,10 @@ export function stylesToClasses(
287
248
  /**
288
249
  * Convert a responsive style object to utility classes with responsive prefixes
289
250
  * Example: { base: { padding: "10px" }, tablet: { padding: "24px" } }
290
- * → ["p-10px", "t-p-24px"]
251
+ * → ["p-[10px]", "tablet:p-[24px]"]
291
252
  *
292
253
  * Also handles flat merged style objects:
293
- * Example: { padding: "10px", display: "flex" } → ["p-10px", "flex"]
254
+ * Example: { padding: "10px", display: "flex" } → ["p-[10px]", "flex"]
294
255
  */
295
256
  /** Values that don't make sense in a fluid container pattern. Mirrors cssGeneration.ts. */
296
257
  const CONTAINER_RESERVED_VALUES = new Set(['auto', 'inherit', 'initial', 'unset', '']);
@@ -322,7 +283,7 @@ function transformContainerIntent(style: StyleObject): StyleObject {
322
283
  * In fluid mode, consume per-node mobile overrides for scalable properties
323
284
  * into the base's `clamp()` value. The mobile value becomes the clamp's MIN
324
285
  * (small-viewport endpoint), the base stays as MAX. The mobile property is
325
- * then stripped from the mobile slice so no `.mob-*` @media rule is emitted
286
+ * then stripped from the mobile slice so no `mobile:*` @media rule is emitted
326
287
  * for it — avoiding the jump at the mobile breakpoint that a hard override
327
288
  * would create.
328
289
  *
@@ -332,12 +293,12 @@ function transformContainerIntent(style: StyleObject): StyleObject {
332
293
  *
333
294
  * Bails out when units mismatch, parsing fails, or the registered values
334
295
  * aren't plain numeric+unit strings — those cases fall back to today's
335
- * behavior (mobile emits its own .mob-* rule).
296
+ * behavior (mobile emits its own `mobile:*` rule).
336
297
  */
337
298
  function consumeMobileIntoBaseClamp(
338
299
  base: StyleObject,
339
300
  mobile: StyleObject,
340
- responsiveScales: ResponsiveScales
301
+ responsiveScales: ResponsiveScales,
341
302
  ): { patchedBase: StyleObject; patchedMobile: StyleObject } {
342
303
  const fluidRange = responsiveScales.fluidRange ?? DEFAULT_FLUID_RANGE;
343
304
  const patchedBase: StyleObject = { ...base };
@@ -364,7 +325,7 @@ function consumeMobileIntoBaseClamp(
364
325
  baseParsed.value,
365
326
  baseParsed.unit,
366
327
  fluidRange.min,
367
- fluidRange.max
328
+ fluidRange.max,
368
329
  );
369
330
 
370
331
  patchedBase[prop] = clampValue;
@@ -376,7 +337,7 @@ function consumeMobileIntoBaseClamp(
376
337
 
377
338
  export function responsiveStylesToClasses(
378
339
  styles: StyleObject | ResponsiveStyleObject | null | undefined,
379
- options?: { fluidActive?: boolean; responsiveScales?: ResponsiveScales }
340
+ options?: { fluidActive?: boolean; responsiveScales?: ResponsiveScales },
380
341
  ): string[] {
381
342
  if (!styles) return [];
382
343
  const fluidActive = options?.fluidActive === true;
@@ -395,45 +356,35 @@ export function responsiveStylesToClasses(
395
356
  let baseSource = responsiveStyles.base;
396
357
  let mobileSource = responsiveStyles.mobile;
397
358
  if (fluidActive && responsiveScales?.enabled === true && baseSource && mobileSource) {
398
- const { patchedBase, patchedMobile } = consumeMobileIntoBaseClamp(
399
- baseSource,
400
- mobileSource,
401
- responsiveScales
402
- );
359
+ const { patchedBase, patchedMobile } = consumeMobileIntoBaseClamp(baseSource, mobileSource, responsiveScales);
403
360
  baseSource = patchedBase;
404
361
  mobileSource = patchedMobile;
405
362
  }
406
363
 
407
364
  // Base styles (no prefix)
408
365
  if (baseSource) {
409
- const base = fluidActive
410
- ? transformContainerIntent(baseSource)
411
- : baseSource;
366
+ const base = fluidActive ? transformContainerIntent(baseSource) : baseSource;
412
367
  classes.push(...stylesToClasses(base));
413
368
  }
414
369
 
415
- // Tablet styles (t- prefix)
370
+ // Tablet styles (tablet: variant prefix)
416
371
  if (responsiveStyles.tablet) {
417
- const tablet = fluidActive
418
- ? transformContainerIntent(responsiveStyles.tablet)
419
- : responsiveStyles.tablet;
372
+ const tablet = fluidActive ? transformContainerIntent(responsiveStyles.tablet) : responsiveStyles.tablet;
420
373
  const tabletClasses = stylesToClasses(tablet);
421
- classes.push(...tabletClasses.map((cls) => `t-${cls}`));
374
+ const prefix = breakpointClassPrefix('tablet');
375
+ classes.push(...tabletClasses.map((cls) => `${prefix}${cls}`));
422
376
  }
423
377
 
424
- // Mobile styles (mob- prefix to avoid conflict with margin)
378
+ // Mobile styles (mobile: variant prefix)
425
379
  if (mobileSource && Object.keys(mobileSource).length > 0) {
426
- const mobile = fluidActive
427
- ? transformContainerIntent(mobileSource)
428
- : mobileSource;
380
+ const mobile = fluidActive ? transformContainerIntent(mobileSource) : mobileSource;
429
381
  const mobileClasses = stylesToClasses(mobile);
430
- classes.push(...mobileClasses.map((cls) => `mob-${cls}`));
382
+ const prefix = breakpointClassPrefix('mobile');
383
+ classes.push(...mobileClasses.map((cls) => `${prefix}${cls}`));
431
384
  }
432
385
  } else {
433
386
  // Flat style object - treat all as base classes
434
- const flat = fluidActive
435
- ? transformContainerIntent(styles as StyleObject)
436
- : (styles as StyleObject);
387
+ const flat = fluidActive ? transformContainerIntent(styles as StyleObject) : (styles as StyleObject);
437
388
  classes.push(...stylesToClasses(flat));
438
389
  }
439
390
 
@@ -442,57 +393,55 @@ export function responsiveStylesToClasses(
442
393
 
443
394
  /**
444
395
  * Parse a utility class back to CSS property and value
445
- * Example: "p-10px" → { padding: "10px" }
396
+ * Example: "p-[10px]" → { prop: "padding", value: "10px" }
446
397
  */
447
398
  export function classToStyle(className: string): { prop: string; value: string } | null {
448
- // Remove responsive prefixes for parsing
449
- let cleanClass = className;
450
- let responsivePrefix = '';
451
-
452
- if (className.startsWith('t-')) {
453
- responsivePrefix = 't';
454
- cleanClass = className.slice(2);
455
- } else if (className.startsWith('mob-')) {
456
- responsivePrefix = 'mob';
457
- cleanClass = className.slice(4);
458
- }
399
+ // Remove responsive variant prefix for parsing (tablet:p-[10px] → p-[10px])
400
+ const { base: cleanClass } = splitVariantPrefix(className);
459
401
 
460
- // Check special cases from config
461
- if (classToStyleSpecialCases[cleanClass]) {
462
- return classToStyleSpecialCases[cleanClass];
402
+ // Static utilities and presets have fixed reverse mappings
403
+ if (staticUtilityReverse[cleanClass]) {
404
+ return staticUtilityReverse[cleanClass];
405
+ }
406
+ if (presetClassReverse[cleanClass]) {
407
+ return presetClassReverse[cleanClass];
463
408
  }
464
409
 
465
- // Parse prefix-value pattern (e.g., "p-10px", "m-20px")
466
- const match = cleanClass.match(/^([a-z-]+)-(.*?)$/);
467
- if (!match) return null;
468
-
469
- const [, prefix, value] = match;
410
+ const parsed = parseUtilityClass(cleanClass);
470
411
 
471
- // Find the property from the prefix
472
- let prop = '';
473
- for (const [key, val] of Object.entries(propertyMap)) {
474
- if (val === prefix) {
475
- prop = key;
476
- break;
412
+ if (!parsed) {
413
+ // Dynamic (arbitrary-property hash) classes registered for unknown roots
414
+ const dynamic = getDynamicStyle(cleanClass);
415
+ if (dynamic) {
416
+ return { prop: cssPropertyToCamel(dynamic.property), value: String(dynamic.value) };
477
417
  }
418
+ return null;
478
419
  }
479
420
 
480
- if (!prop) return null;
421
+ if (parsed.kind === 'arbitrary-property') {
422
+ return { prop: cssPropertyToCamel(parsed.property), value: parsed.value! };
423
+ }
481
424
 
482
425
  // Hash-fallback class — value is `h<base36>`, the real CSS value lives in
483
426
  // the style-value registry (set when the class was minted from a complex
484
- // `clamp()` / `calc()` value that wouldn't fit in a CSS selector). Pull
485
- // the real value back out so editor reads see the original expression.
486
- if (/^h[0-9a-z]+$/.test(value)) {
427
+ // value that wouldn't fit in a class name). Pull the real value back out
428
+ // so editor reads see the original expression.
429
+ if (parsed.kind === 'hash') {
487
430
  const registered = getStyleValue(cleanClass);
488
- if (registered != null) {
489
- return { prop, value: String(registered) };
490
- }
431
+ if (registered == null) return null;
432
+ const value = String(registered);
433
+ const root = parsed.root!;
434
+ const prop = ROOT_CAMEL_OVERRIDES[root] ?? cssPropertyToCamel(resolveRootProperty(root, value));
435
+ return { prop, value };
491
436
  }
492
437
 
493
- // Handle CSS variables
494
- if (value.includes('background') || value.includes('text') || value.includes('border')) {
495
- return { prop, value: `var(--${value})` };
438
+ const root = parsed.root;
439
+ const prop = root && ROOT_CAMEL_OVERRIDES[root] ? ROOT_CAMEL_OVERRIDES[root] : cssPropertyToCamel(parsed.property);
440
+ let value = parsed.value!;
441
+
442
+ // Color token props store bare tokens (var(--primary) → "primary")
443
+ if (parsed.kind === 'var' && colorTokenProps.has(prop)) {
444
+ value = value.replace(/^var\(--([\w-]+)\)$/, '$1');
496
445
  }
497
446
 
498
447
  return { prop, value };
@@ -500,22 +449,15 @@ export function classToStyle(className: string): { prop: string; value: string }
500
449
 
501
450
  /**
502
451
  * Convert utility classes back to a style object
503
- * Example: ["p-10px", "t-p-24px"] → { base: { padding: "10px" }, tablet: { padding: "24px" } }
452
+ * Example: ["p-[10px]", "tablet:p-[24px]"] → { base: { padding: "10px" }, tablet: { padding: "24px" } }
504
453
  */
505
454
  export function classesToStyles(classes: string[]): ResponsiveStyleObject | null {
506
455
  const styles: ResponsiveStyleObject = { base: {}, tablet: {}, mobile: {} };
507
456
 
508
457
  for (const className of classes) {
509
- let breakpoint: 'base' | 'tablet' | 'mobile' = 'base';
510
- let cleanClass = className;
511
-
512
- if (className.startsWith('t-')) {
513
- breakpoint = 'tablet';
514
- cleanClass = className.slice(2);
515
- } else if (className.startsWith('mob-')) {
516
- breakpoint = 'mobile';
517
- cleanClass = className.slice(4);
518
- }
458
+ const { breakpoint: variant, base: cleanClass } = splitVariantPrefix(className);
459
+ const breakpoint: 'base' | 'tablet' | 'mobile' =
460
+ variant === 'tablet' ? 'tablet' : variant === 'mobile' ? 'mobile' : 'base';
519
461
 
520
462
  const styleEntry = classToStyle(cleanClass);
521
463
  if (styleEntry) {