@tenphi/tasty 0.0.0-snapshot.ef8cc29 → 0.0.0-snapshot.efb24cc

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 (298) hide show
  1. package/README.md +65 -215
  2. package/dist/_virtual/_rolldown/runtime.js +1 -2
  3. package/dist/chunks/cacheKey.d.ts +1 -0
  4. package/dist/chunks/cacheKey.js +1 -2
  5. package/dist/chunks/cacheKey.js.map +1 -1
  6. package/dist/chunks/definitions.d.ts +1 -1
  7. package/dist/chunks/definitions.js +2 -3
  8. package/dist/chunks/definitions.js.map +1 -1
  9. package/dist/chunks/index.d.ts +1 -0
  10. package/dist/chunks/renderChunk.d.ts +1 -0
  11. package/dist/chunks/renderChunk.js +1 -2
  12. package/dist/chunks/renderChunk.js.map +1 -1
  13. package/dist/compute-styles.d.ts +31 -0
  14. package/dist/compute-styles.js +322 -0
  15. package/dist/compute-styles.js.map +1 -0
  16. package/dist/config.d.ts +105 -21
  17. package/dist/config.js +175 -49
  18. package/dist/config.js.map +1 -1
  19. package/dist/core/index.d.ts +7 -7
  20. package/dist/core/index.js +8 -8
  21. package/dist/counter-style/index.js +51 -0
  22. package/dist/counter-style/index.js.map +1 -0
  23. package/dist/debug.js +5 -6
  24. package/dist/debug.js.map +1 -1
  25. package/dist/font-face/index.js +63 -0
  26. package/dist/font-face/index.js.map +1 -0
  27. package/dist/hooks/index.d.ts +7 -0
  28. package/dist/hooks/useCounterStyle.d.ts +36 -0
  29. package/dist/hooks/useCounterStyle.js +65 -0
  30. package/dist/hooks/useCounterStyle.js.map +1 -0
  31. package/dist/hooks/useFontFace.d.ts +45 -0
  32. package/dist/hooks/useFontFace.js +66 -0
  33. package/dist/hooks/useFontFace.js.map +1 -0
  34. package/dist/hooks/useGlobalStyles.d.ts +18 -2
  35. package/dist/hooks/useGlobalStyles.js +52 -47
  36. package/dist/hooks/useGlobalStyles.js.map +1 -1
  37. package/dist/hooks/useKeyframes.d.ts +4 -2
  38. package/dist/hooks/useKeyframes.js +43 -57
  39. package/dist/hooks/useKeyframes.js.map +1 -1
  40. package/dist/hooks/useProperty.d.ts +4 -2
  41. package/dist/hooks/useProperty.js +30 -48
  42. package/dist/hooks/useProperty.js.map +1 -1
  43. package/dist/hooks/useRawCSS.d.ts +13 -44
  44. package/dist/hooks/useRawCSS.js +91 -28
  45. package/dist/hooks/useRawCSS.js.map +1 -1
  46. package/dist/hooks/useStyles.d.ts +3 -8
  47. package/dist/hooks/useStyles.js +7 -225
  48. package/dist/hooks/useStyles.js.map +1 -1
  49. package/dist/index.d.ts +10 -8
  50. package/dist/index.js +11 -9
  51. package/dist/injector/index.d.ts +36 -19
  52. package/dist/injector/index.js +43 -20
  53. package/dist/injector/index.js.map +1 -1
  54. package/dist/injector/injector.d.ts +53 -3
  55. package/dist/injector/injector.js +235 -14
  56. package/dist/injector/injector.js.map +1 -1
  57. package/dist/injector/sheet-manager.d.ts +9 -13
  58. package/dist/injector/sheet-manager.js +41 -71
  59. package/dist/injector/sheet-manager.js.map +1 -1
  60. package/dist/injector/types.d.ts +112 -21
  61. package/dist/keyframes/index.js +1 -1
  62. package/dist/parser/classify.js +4 -5
  63. package/dist/parser/classify.js.map +1 -1
  64. package/dist/parser/const.js +30 -3
  65. package/dist/parser/const.js.map +1 -1
  66. package/dist/parser/lru.js +1 -1
  67. package/dist/parser/lru.js.map +1 -1
  68. package/dist/parser/parser.js +1 -2
  69. package/dist/parser/parser.js.map +1 -1
  70. package/dist/parser/tokenizer.js +1 -1
  71. package/dist/parser/tokenizer.js.map +1 -1
  72. package/dist/parser/types.js +1 -1
  73. package/dist/parser/types.js.map +1 -1
  74. package/dist/pipeline/conditions.js +1 -1
  75. package/dist/pipeline/conditions.js.map +1 -1
  76. package/dist/pipeline/exclusive.js +58 -4
  77. package/dist/pipeline/exclusive.js.map +1 -1
  78. package/dist/pipeline/index.d.ts +2 -0
  79. package/dist/pipeline/index.js +39 -12
  80. package/dist/pipeline/index.js.map +1 -1
  81. package/dist/pipeline/materialize.js +57 -5
  82. package/dist/pipeline/materialize.js.map +1 -1
  83. package/dist/pipeline/parseStateKey.js +5 -6
  84. package/dist/pipeline/parseStateKey.js.map +1 -1
  85. package/dist/pipeline/simplify.js +181 -7
  86. package/dist/pipeline/simplify.js.map +1 -1
  87. package/dist/pipeline/warnings.js +1 -1
  88. package/dist/plugins/index.d.ts +2 -0
  89. package/dist/plugins/okhsl-plugin.js +1 -2
  90. package/dist/plugins/okhsl-plugin.js.map +1 -1
  91. package/dist/plugins/types.d.ts +12 -1
  92. package/dist/properties/index.js +2 -3
  93. package/dist/properties/index.js.map +1 -1
  94. package/dist/properties/property-type-resolver.js +1 -2
  95. package/dist/properties/property-type-resolver.js.map +1 -1
  96. package/dist/rsc-cache.js +79 -0
  97. package/dist/rsc-cache.js.map +1 -0
  98. package/dist/ssr/astro-client.d.ts +1 -0
  99. package/dist/ssr/astro-client.js +19 -0
  100. package/dist/ssr/astro-client.js.map +1 -0
  101. package/dist/ssr/astro-middleware.d.ts +15 -0
  102. package/dist/ssr/astro-middleware.js +19 -0
  103. package/dist/ssr/astro-middleware.js.map +1 -0
  104. package/dist/ssr/astro.d.ts +89 -10
  105. package/dist/ssr/astro.js +113 -29
  106. package/dist/ssr/astro.js.map +1 -1
  107. package/dist/ssr/async-storage.js +15 -6
  108. package/dist/ssr/async-storage.js.map +1 -1
  109. package/dist/ssr/collect-auto-properties.js +29 -11
  110. package/dist/ssr/collect-auto-properties.js.map +1 -1
  111. package/dist/ssr/collector.d.ts +26 -13
  112. package/dist/ssr/collector.js +84 -32
  113. package/dist/ssr/collector.js.map +1 -1
  114. package/dist/ssr/context.js +4 -2
  115. package/dist/ssr/context.js.map +1 -1
  116. package/dist/ssr/format-global-rules.js +1 -1
  117. package/dist/ssr/format-keyframes.js +1 -2
  118. package/dist/ssr/format-keyframes.js.map +1 -1
  119. package/dist/ssr/format-property.js +1 -2
  120. package/dist/ssr/format-property.js.map +1 -1
  121. package/dist/ssr/format-rules.js +8 -5
  122. package/dist/ssr/format-rules.js.map +1 -1
  123. package/dist/ssr/hydrate.d.ts +20 -13
  124. package/dist/ssr/hydrate.js +25 -30
  125. package/dist/ssr/hydrate.js.map +1 -1
  126. package/dist/ssr/index.d.ts +3 -4
  127. package/dist/ssr/index.js +5 -7
  128. package/dist/ssr/index.js.map +1 -1
  129. package/dist/ssr/next.d.ts +6 -5
  130. package/dist/ssr/next.js +14 -10
  131. package/dist/ssr/next.js.map +1 -1
  132. package/dist/ssr/ssr-collector-ref.js +20 -3
  133. package/dist/ssr/ssr-collector-ref.js.map +1 -1
  134. package/dist/states/index.js +3 -2
  135. package/dist/states/index.js.map +1 -1
  136. package/dist/static/index.js +1 -2
  137. package/dist/static/inject.d.ts +5 -0
  138. package/dist/static/inject.js +17 -0
  139. package/dist/static/inject.js.map +1 -0
  140. package/dist/static/tastyStatic.js +1 -2
  141. package/dist/static/tastyStatic.js.map +1 -1
  142. package/dist/static/types.js +1 -1
  143. package/dist/styles/border.d.ts +1 -1
  144. package/dist/styles/border.js +30 -24
  145. package/dist/styles/border.js.map +1 -1
  146. package/dist/styles/color.d.ts +1 -1
  147. package/dist/styles/color.js +2 -3
  148. package/dist/styles/color.js.map +1 -1
  149. package/dist/styles/const.js +17 -0
  150. package/dist/styles/const.js.map +1 -0
  151. package/dist/styles/createStyle.js +4 -5
  152. package/dist/styles/createStyle.js.map +1 -1
  153. package/dist/styles/dimension.js +15 -3
  154. package/dist/styles/dimension.js.map +1 -1
  155. package/dist/styles/directional.js +133 -0
  156. package/dist/styles/directional.js.map +1 -0
  157. package/dist/styles/display.d.ts +3 -10
  158. package/dist/styles/display.js +45 -39
  159. package/dist/styles/display.js.map +1 -1
  160. package/dist/styles/fade.d.ts +1 -1
  161. package/dist/styles/fade.js +9 -5
  162. package/dist/styles/fade.js.map +1 -1
  163. package/dist/styles/fill.d.ts +2 -2
  164. package/dist/styles/fill.js +3 -4
  165. package/dist/styles/fill.js.map +1 -1
  166. package/dist/styles/flow.js +1 -1
  167. package/dist/styles/gap.d.ts +1 -1
  168. package/dist/styles/gap.js +4 -3
  169. package/dist/styles/gap.js.map +1 -1
  170. package/dist/styles/height.d.ts +1 -1
  171. package/dist/styles/height.js +1 -2
  172. package/dist/styles/height.js.map +1 -1
  173. package/dist/styles/index.d.ts +0 -1
  174. package/dist/styles/index.js +3 -4
  175. package/dist/styles/index.js.map +1 -1
  176. package/dist/styles/inset.d.ts +1 -29
  177. package/dist/styles/inset.js +19 -135
  178. package/dist/styles/inset.js.map +1 -1
  179. package/dist/styles/list.d.ts +5 -5
  180. package/dist/styles/list.js +4 -2
  181. package/dist/styles/list.js.map +1 -1
  182. package/dist/styles/margin.d.ts +1 -1
  183. package/dist/styles/margin.js +17 -89
  184. package/dist/styles/margin.js.map +1 -1
  185. package/dist/styles/outline.d.ts +1 -1
  186. package/dist/styles/outline.js +6 -16
  187. package/dist/styles/outline.js.map +1 -1
  188. package/dist/styles/padding.d.ts +1 -1
  189. package/dist/styles/padding.js +17 -89
  190. package/dist/styles/padding.js.map +1 -1
  191. package/dist/styles/placement.d.ts +37 -0
  192. package/dist/styles/placement.js +74 -0
  193. package/dist/styles/placement.js.map +1 -0
  194. package/dist/styles/predefined.d.ts +4 -4
  195. package/dist/styles/predefined.js +8 -9
  196. package/dist/styles/predefined.js.map +1 -1
  197. package/dist/styles/preset.d.ts +6 -1
  198. package/dist/styles/preset.js +54 -53
  199. package/dist/styles/preset.js.map +1 -1
  200. package/dist/styles/radius.d.ts +1 -3
  201. package/dist/styles/radius.js +38 -6
  202. package/dist/styles/radius.js.map +1 -1
  203. package/dist/styles/scrollMargin.d.ts +24 -0
  204. package/dist/styles/scrollMargin.js +32 -0
  205. package/dist/styles/scrollMargin.js.map +1 -0
  206. package/dist/styles/scrollbar.d.ts +1 -1
  207. package/dist/styles/scrollbar.js +8 -5
  208. package/dist/styles/scrollbar.js.map +1 -1
  209. package/dist/styles/shadow.d.ts +1 -1
  210. package/dist/styles/shadow.js +4 -3
  211. package/dist/styles/shadow.js.map +1 -1
  212. package/dist/styles/shared.js +17 -0
  213. package/dist/styles/shared.js.map +1 -0
  214. package/dist/styles/transition.d.ts +1 -1
  215. package/dist/styles/transition.js +5 -4
  216. package/dist/styles/transition.js.map +1 -1
  217. package/dist/styles/types.d.ts +52 -11
  218. package/dist/styles/width.d.ts +1 -1
  219. package/dist/styles/width.js +1 -2
  220. package/dist/styles/width.js.map +1 -1
  221. package/dist/tasty.d.ts +46 -893
  222. package/dist/tasty.js +96 -67
  223. package/dist/tasty.js.map +1 -1
  224. package/dist/utils/cache-wrapper.js +1 -2
  225. package/dist/utils/cache-wrapper.js.map +1 -1
  226. package/dist/utils/case-converter.js +1 -1
  227. package/dist/utils/color-math.js +1 -1
  228. package/dist/utils/color-math.js.map +1 -1
  229. package/dist/utils/color-space.js +1 -2
  230. package/dist/utils/color-space.js.map +1 -1
  231. package/dist/utils/colors.js +1 -2
  232. package/dist/utils/colors.js.map +1 -1
  233. package/dist/utils/deps-equal.js +15 -0
  234. package/dist/utils/deps-equal.js.map +1 -0
  235. package/dist/utils/dotize.js +1 -1
  236. package/dist/utils/dotize.js.map +1 -1
  237. package/dist/utils/filter-base-props.d.ts +1 -1
  238. package/dist/utils/filter-base-props.js +1 -1
  239. package/dist/utils/filter-base-props.js.map +1 -1
  240. package/dist/utils/get-display-name.js +1 -1
  241. package/dist/utils/has-keys.js +1 -1
  242. package/dist/utils/hash.js +14 -0
  243. package/dist/utils/hash.js.map +1 -0
  244. package/dist/utils/is-dev-env.js +1 -1
  245. package/dist/utils/is-dev-env.js.map +1 -1
  246. package/dist/utils/is-valid-element-type.js +1 -1
  247. package/dist/utils/merge-styles.js +1 -2
  248. package/dist/utils/merge-styles.js.map +1 -1
  249. package/dist/utils/mod-attrs.d.ts +0 -2
  250. package/dist/utils/mod-attrs.js +1 -2
  251. package/dist/utils/mod-attrs.js.map +1 -1
  252. package/dist/utils/process-tokens.d.ts +1 -5
  253. package/dist/utils/process-tokens.js +3 -11
  254. package/dist/utils/process-tokens.js.map +1 -1
  255. package/dist/utils/resolve-recipes.js +1 -2
  256. package/dist/utils/resolve-recipes.js.map +1 -1
  257. package/dist/utils/selector-transform.js +1 -1
  258. package/dist/utils/string.js +1 -1
  259. package/dist/utils/styles.d.ts +1 -3
  260. package/dist/utils/styles.js +2 -5
  261. package/dist/utils/styles.js.map +1 -1
  262. package/dist/utils/typography.d.ts +21 -10
  263. package/dist/utils/typography.js +10 -2
  264. package/dist/utils/typography.js.map +1 -1
  265. package/dist/utils/warnings.js +1 -1
  266. package/dist/zero/babel.d.ts +27 -101
  267. package/dist/zero/babel.js +164 -48
  268. package/dist/zero/babel.js.map +1 -1
  269. package/dist/zero/css-writer.js +1 -2
  270. package/dist/zero/css-writer.js.map +1 -1
  271. package/dist/zero/extractor.js +54 -3
  272. package/dist/zero/extractor.js.map +1 -1
  273. package/dist/zero/index.js +1 -2
  274. package/dist/zero/next.d.ts +12 -0
  275. package/dist/zero/next.js +5 -4
  276. package/dist/zero/next.js.map +1 -1
  277. package/docs/README.md +2 -2
  278. package/docs/adoption.md +5 -3
  279. package/docs/comparison.md +24 -25
  280. package/docs/configuration.md +130 -1
  281. package/docs/design-system.md +47 -12
  282. package/docs/dsl.md +115 -9
  283. package/docs/getting-started.md +10 -10
  284. package/docs/injector.md +41 -25
  285. package/docs/methodology.md +119 -6
  286. package/docs/react-api.md +557 -0
  287. package/docs/ssr.md +141 -81
  288. package/docs/styles.md +45 -23
  289. package/docs/tasty-static.md +103 -2
  290. package/package.json +20 -9
  291. package/dist/ssr/context.d.ts +0 -8
  292. package/dist/styles/align.d.ts +0 -15
  293. package/dist/styles/align.js +0 -14
  294. package/dist/styles/align.js.map +0 -1
  295. package/dist/styles/justify.d.ts +0 -15
  296. package/dist/styles/justify.js +0 -14
  297. package/dist/styles/justify.js.map +0 -1
  298. package/docs/runtime.md +0 -317
package/README.md CHANGED
@@ -41,8 +41,8 @@ On top of that foundation, Tasty gives teams a governed styling model: a CSS-lik
41
41
 
42
42
  ### Supporting capabilities
43
43
 
44
- - **Typed style props** — `styleProps` lets you expose selected styles as typed React props. Use `<Button placeSelf="end">` or `<Space flow="row" gap="2x">` without extra wrappers, utility classes, or `styles` overrides. The same props also accept state maps, so responsive values work with the same API. See [CSS properties as props](#css-properties-as-props).
45
- - **Runtime, SSR, and zero-runtime options** — Use `tasty()` for runtime React components, add SSR integrations when your framework renders that runtime on the server, or use `tastyStatic()` when you specifically want build-time extraction instead of runtime styling.
44
+ - **Typed style props and mod props** — `styleProps` exposes selected CSS properties as typed React props (`<Space flow="row" gap="2x">`); `modProps` does the same for modifier keys (`<Button isLoading size="large">`). Both support state maps and full TypeScript autocomplete. See [Style Props](#style-props) and [Mod Props](#mod-props).
45
+ - **Server-compatible by default, zero client JS in server-only contexts** — All `tasty()` components and style functions are hook-free. In server-only rendering (Next.js RSC, Astro without islands, SSG), they produce zero client JavaScript with the full feature set. Add SSR integration only when your app also has client-side hydration. Use `tastyStatic()` only when you need build-time extraction without React.
46
46
  - **Broad modern CSS coverage** — Media queries, container queries, `@supports`, `:has()`, `@starting-style`, `@property`, `@keyframes`, and more. Features that do not fit the component model (such as `@layer` and `!important`) are intentionally left out.
47
47
  - **Performance and caching** — Runtime mode injects CSS on demand, reuses chunks aggressively, and relies on multi-level caching so large component systems stay practical.
48
48
  - **TypeScript-first and AI-friendly** — Style definitions are declarative, structurally consistent, and fully typed, which helps both humans and tooling understand advanced stateful styles without hidden cascade logic.
@@ -178,58 +178,17 @@ configure({
178
178
 
179
179
  Use `configure()` once when your app or design system needs shared aliases, tokens, recipes, or parser extensions. Predefined states turn complex selector logic into single tokens, so teams can write `@mobile` instead of repeating media query expressions in every component.
180
180
 
181
- ### CSS properties as props
181
+ ### Props as the public API
182
182
 
183
- Beyond state resolution, Tasty can also expose selected style controls as typed component props. That lets design systems keep layout and composition inside governed component APIs instead of pushing teams toward wrapper elements or ad hoc styling escapes.
184
-
185
- With `styleProps`, a component can expose the styles you choose as normal typed props. You can adjust layout, spacing, alignment, or positioning where the component is used while staying inside a typed, design-system-aware API.
186
-
187
- ```tsx
188
- import { tasty, FLOW_STYLES, POSITION_STYLES } from '@tenphi/tasty';
189
-
190
- const Space = tasty({
191
- styles: {
192
- display: 'flex',
193
- flow: 'column',
194
- gap: '1x',
195
- },
196
- styleProps: FLOW_STYLES,
197
- });
198
-
199
- const Button = tasty({
200
- as: 'button',
201
- styles: {
202
- padding: '1.5x 3x',
203
- fill: '#primary',
204
- color: '#primary-text',
205
- radius: true,
206
- },
207
- styleProps: POSITION_STYLES,
208
- });
209
- ```
210
-
211
- Now you can compose layout and tweak component positioning directly in JSX:
183
+ `styleProps` exposes selected CSS properties as typed React props, and `modProps` does the same for modifier keys. Together they let design systems define a governed, typed component API without wrapper elements or `styles` overrides:
212
184
 
213
185
  ```tsx
214
186
  <Space flow="row" gap="2x" placeItems="center">
215
- <Title>Dashboard</Title>
216
- <Button placeSelf="end">Add Item</Button>
187
+ <Button isLoading size="large" placeSelf="end">Submit</Button>
217
188
  </Space>
218
189
  ```
219
190
 
220
- The same props also support state maps, so responsive values use the exact same API:
221
-
222
- ```tsx
223
- <Space
224
- flow={{ '': 'column', '@tablet': 'row' }}
225
- gap={{ '': '2x', '@tablet': '4x' }}
226
- >
227
- <Sidebar />
228
- <Content />
229
- </Space>
230
- ```
231
-
232
- Layout components can expose flow props. Buttons can expose positioning props. Each component can offer only the style props that make sense for its role while keeping tokens, custom units, and state maps fully typed. This works in runtime `tasty()` components, not in `tastyStatic()`.
191
+ See [Style Props](#style-props) and [Mod Props](#mod-props) below, or the full reference in [React API](docs/react-api.md#style-props).
233
192
 
234
193
  ## Choose a Styling Approach
235
194
 
@@ -237,10 +196,11 @@ Once you understand the component model, pick the rendering mode that matches yo
237
196
 
238
197
  | Approach | Entry point | Best for | Trade-off |
239
198
  |----------|-------------|----------|-----------|
240
- | **Runtime** | `@tenphi/tasty` | Interactive apps with reusable stateful components and design systems | Full feature set; CSS is generated on demand at runtime |
241
- | **Zero-runtime** | `@tenphi/tasty/static` | Static sites, SSG, landing pages | Requires the Babel plugin; no component-level `styleProps` or runtime-only APIs |
199
+ | **Runtime (default)** | `tasty()` from `@tenphi/tasty` | All React apps server-rendered by default, zero client JS until you need interactivity | Full feature set; CSS computed during React rendering (server or client) |
200
+ | **Runtime + SSR integration** | Add `@tenphi/tasty/ssr/*` | Apps with client-side hydration (Next.js client components, Astro islands) | Adds CSS deduplication, FOUC prevention, and client cache hydration |
201
+ | **Zero-runtime** | `tastyStatic()` from `@tenphi/tasty/static` | Non-React frameworks or when you need build-time extraction without React | Requires the Babel plugin; no component-level `styleProps` or runtime-only APIs |
242
202
 
243
- If your framework can execute runtime React code on the server, you can also add **SSR on top of runtime** with `@tenphi/tasty/ssr/*`. This uses the same `tasty()` pipeline, but collects CSS during server rendering and hydrates the cache on the client. That is the model for Next.js, generic React SSR, and Astro islands. See [Getting Started](docs/getting-started.md#choosing-a-rendering-mode), [Zero Runtime](docs/tasty-static.md), and [Server-Side Rendering](docs/ssr.md).
203
+ All `tasty()` components are hook-free and work as React Server Components. In server-only contexts Next.js RSC without `'use client'`, Astro without `client:*` directives, and other SSG setups — they produce the same end result as `tastyStatic()` (static HTML + CSS, zero client JavaScript) but with the full feature set including `styleProps`, sub-elements, and variants. SSR integration is only needed when your app also has client-side rendering. See [Getting Started](docs/getting-started.md#choosing-a-rendering-mode), [Zero Runtime](docs/tasty-static.md), and [Server-Side Rendering](docs/ssr.md).
244
204
 
245
205
  ## How It Actually Works
246
206
 
@@ -342,7 +302,7 @@ Every rule is guarded by the negation of higher-priority rules. No two rules can
342
302
 
343
303
  By absorbing selector complexity, Tasty makes advanced CSS patterns practical again — nested container queries, multi-condition `@supports` gates, and combined root-state/media branches. You stay in pure CSS instead of relying on JavaScript workarounds, so the browser can optimize layout, painting, and transitions natively. Tasty keeps the solution in CSS while removing much of the selector bookkeeping that is hard to maintain by hand.
344
304
 
345
- [Try it in the Cube UI Kit Storybook playground →](https://cube-ui-kit.vercel.app/?path=/story/getting-started-tasty-playground--playground)
305
+ [Try it in the playground →](https://tasty.style/playground)
346
306
 
347
307
  ## Capabilities
348
308
 
@@ -388,7 +348,7 @@ Every style property accepts a state mapping object. Keys can be combined with b
388
348
  | Feature query | `@supports(display: grid)` | `@supports (display: grid)` |
389
349
  | Entry animation | `@starting` | `@starting-style` |
390
350
 
391
- Combine with `&` (AND), `|` (OR), `!` (NOT):
351
+ Combine with `&` (AND), `|` (OR), `!` (NOT), `^` (XOR):
392
352
 
393
353
  ```tsx
394
354
  fill: {
@@ -400,220 +360,109 @@ fill: {
400
360
 
401
361
  ### Sub-Element Styling
402
362
 
403
- Style inner elements from the parent component definition. No extra components, no CSS leakage:
404
-
405
- ```tsx
406
- const Card = tasty({
407
- styles: {
408
- padding: '4x',
409
- Title: { preset: 'h3', color: '#primary' },
410
- Content: { color: '#text', preset: 't2' },
411
- },
412
- elements: { Title: 'h2', Content: 'div' },
413
- });
414
-
415
- <Card>
416
- <Card.Title>Heading</Card.Title>
417
- <Card.Content>Body text</Card.Content>
418
- </Card>
419
- ```
363
+ Compound components can style inner parts from the parent definition with capitalized keys in `styles` and optional `elements` declarations, producing typed sub-components like `<Card.Title />` instead of separate wrapper components or ad hoc class naming.
420
364
 
421
- Sub-elements use `data-element` attributes no extra class names, no naming conventions.
365
+ Sub-elements share the root state context by default, so keys like `:hover`, modifiers, root states, and media queries resolve as one coordinated styling block. Use `@own(...)` when a sub-element should react to its own state, and use the `$` selector affix when you need precise descendant targeting.
422
366
 
423
- By default, sub-elements participate in the same state context as the root component. That means mappings like `:hover`, `theme=danger`, `[role="button"]`, and other keys are evaluated as one unified block, which keeps styling logic predictable across the whole markup tree.
367
+ See [React API - Sub-element Styling](docs/react-api.md#sub-element-styling), [Style DSL - Advanced States](docs/dsl.md#advanced-states--prefix), and [Methodology](docs/methodology.md#component-architecture-root--sub-elements).
424
368
 
425
- Use `@own(...)` when a sub-element should react to its own state instead of the root state context.
369
+ ### Style Props
426
370
 
427
- Class selectors are also supported, but modifiers/pseudo-classes are usually the better default in design-system code.
428
-
429
- Use the sub-element selector `$` when you need precise descendant targeting to avoid leakage in deeply nested component trees.
430
-
431
- ### Variants
432
-
433
- Variants are designed to keep single-component CSS lean. Instead of generating dozens of static button classes up front, define all versions once and let runtime usage decide what CSS is actually emitted.
371
+ `styleProps` exposes selected CSS properties as typed React props. Components control which properties to open up; consumers get layout and composition knobs without `styles` overrides. Supports state maps for responsive values.
434
372
 
435
373
  ```tsx
436
- const Button = tasty({
437
- styles: { padding: '2x 4x', radius: '1r' },
438
- variants: {
439
- default: { fill: '#primary', color: '#on-primary' },
440
- danger: { fill: '#danger', color: '#on-danger' },
441
- outline: { fill: 'transparent', border: '1bw solid #primary' },
442
- },
374
+ const Space = tasty({
375
+ styles: { display: 'flex', flow: 'column', gap: '1x' },
376
+ styleProps: FLOW_STYLES,
443
377
  });
444
378
 
445
- <Button variant="danger">Delete</Button>
379
+ <Space flow="row" gap={{ '': '2x', '@tablet': '4x' }}>
446
380
  ```
447
381
 
448
- ### Recipes
382
+ See [React API - Style Props](docs/react-api.md#style-props) and [Methodology - styleProps](docs/methodology.md#styleprops-as-the-public-api).
449
383
 
450
- Recipes are predefined style sets that work like composable styling classes for Tasty. They can be pre-applied or post-applied to current styles, which lets you add reusable state logic while still allowing local style overrides.
384
+ ### Mod Props
451
385
 
452
- ```tsx
453
- configure({
454
- recipes: {
455
- card: { padding: '4x', fill: '#surface', radius: '1r', border: true },
456
- elevated: { shadow: '0 2x 4x #shadow' },
457
- },
458
- });
386
+ `modProps` exposes modifier keys as typed React props — the modifier equivalent of `styleProps`. Accepts an array of key names or an object with type descriptors (`Boolean`, `String`, `Number`, or enum arrays) for full TypeScript autocomplete.
459
387
 
460
- const ProfileCard = tasty({
388
+ ```tsx
389
+ const Button = tasty({
390
+ as: 'button',
391
+ modProps: { isLoading: Boolean, size: ['sm', 'md', 'lg'] as const },
461
392
  styles: {
462
- recipe: 'card elevated',
463
- color: '#text',
393
+ fill: { '': '#primary', isLoading: '#primary.5' },
394
+ padding: { '': '2x 4x', 'size=sm': '1x 2x' },
464
395
  },
465
396
  });
466
- ```
467
397
 
468
- Use `/` to post-apply recipes after local styles when you need recipe states/styles to win the final merge order. Use `none` to skip base recipes: `recipe: 'none / disabled'`.
398
+ <Button isLoading size="lg">Submit</Button>
399
+ ```
469
400
 
470
- ### Auto-Inferred `@property`
401
+ See [React API - Mod Props](docs/react-api.md#mod-props) and [Methodology - modProps](docs/methodology.md#modprops-and-mods).
471
402
 
472
- CSS custom properties do not animate smoothly by default because the browser does not know how to interpolate their values. The [`@property`](https://developer.mozilla.org/en-US/docs/Web/CSS/@property) at-rule fixes that by declaring a property's syntax, such as `<number>` or `<color>`.
403
+ ### Variants
473
404
 
474
- In Tasty, you usually do not need to declare `@property` manually. When a custom property is assigned a concrete value, Tasty infers the syntax and registers the matching `@property` for you:
405
+ Variants let one component expose named visual versions without pre-generating a separate class for every possible combination. In runtime mode, Tasty emits only the variant CSS that is actually used.
475
406
 
476
- ```tsx
477
- const Pulse = tasty({
478
- styles: {
479
- animation: 'pulse 2s infinite',
480
- transform: 'scale($pulse-scale)',
481
- '@keyframes': {
482
- pulse: {
483
- '0%, 100%': { '$pulse-scale': 1 },
484
- '50%': { '$pulse-scale': 1.05 },
485
- },
486
- },
487
- },
488
- });
489
- ```
407
+ See [React API - Variants](docs/react-api.md#variants).
490
408
 
491
- Here, `$pulse-scale: 1` is inferred as `<number>`, so Tasty injects `@property --pulse-scale` automatically before using it in the animation. Numeric types (`<number>`, `<length>`, `<percentage>`, `<angle>`, `<time>`) are inferred from values; `<color>` is inferred from the `#name` token convention.
409
+ ### Recipes
492
410
 
493
- If you prefer full manual control, disable auto-inference globally with `configure({ autoPropertyTypes: false })`.
411
+ Recipes are reusable style bundles defined in `configure({ recipes })` and applied with the `recipe` style property. They are useful when your design system wants shared state logic or visual presets without forcing every component to repeat the same style map.
494
412
 
495
- ### Explicit `@properties`
413
+ Use `/` to post-apply recipes after local styles when recipe states should win the final merge order, and use `none` to skip base recipes entirely.
496
414
 
497
- Declare `@properties` yourself only when you need to override the defaults, for example to set `inherits: false` or provide a custom `initialValue`:
415
+ See [Style DSL - Recipes](docs/dsl.md#recipes) and [Configuration - recipes](docs/configuration.md#recipes).
498
416
 
499
- ```tsx
500
- '@properties': {
501
- '$pulse-scale': { syntax: '<number>', inherits: false, initialValue: 1 },
502
- },
503
- ```
417
+ ### Auto-Inferred `@property`
504
418
 
505
- ### React Hooks
419
+ Tasty usually removes the need to hand-author CSS [`@property`](https://developer.mozilla.org/en-US/docs/Web/CSS/@property) rules. When a custom property receives a concrete value, Tasty infers its syntax and registers the matching `@property` automatically, which makes transitions and animations on custom properties work without extra boilerplate.
506
420
 
507
- For cases where you don't need a full component:
421
+ If you prefer explicit control, disable inference with `configure({ autoPropertyTypes: false })` or declare the properties yourself.
508
422
 
509
- ```tsx
510
- import { useStyles, useGlobalStyles, useRawCSS } from '@tenphi/tasty';
423
+ See [Style DSL - Properties (`@property`)](docs/dsl.md#properties-property).
511
424
 
512
- function App() {
513
- const { className } = useStyles({ padding: '2x', fill: '#surface' });
514
- useGlobalStyles('body', { margin: '0' });
515
- useRawCSS('@font-face { font-family: "Custom"; src: url(...); }');
425
+ ### Explicit `@properties`
516
426
 
517
- return <main className={className}>...</main>;
518
- }
519
- ```
427
+ Use explicit `@properties` only when you need to override defaults such as `inherits: false` or a custom `initialValue`.
520
428
 
521
- ### Zero-Runtime Mode
429
+ See [Style DSL - Properties (`@property`)](docs/dsl.md#properties-property).
522
430
 
523
- Extract all CSS at build time. Zero JavaScript overhead in production:
431
+ ### Style Functions
524
432
 
525
- ```tsx
526
- import { tastyStatic } from '@tenphi/tasty/static';
433
+ When you do not need a full component wrapper, use the style functions directly: `useStyles` for local class names, `useGlobalStyles` for selector-scoped global CSS, `useRawCSS` for raw rules, plus `useKeyframes`, `useProperty`, `useFontFace`, and `useCounterStyle` for animation, custom-property, font, and counter-style primitives. All style functions are hook-free and work in React Server Components.
527
434
 
528
- const card = tastyStatic({
529
- padding: '4x',
530
- fill: '#surface',
531
- radius: '1r',
532
- color: { '': '#text', '@dark': '#text-on-dark' },
533
- });
435
+ See [React API - Style Functions](docs/react-api.md#style-functions).
534
436
 
535
- // card is a CSS class name string
536
- <div className={card}>Static styles, zero runtime</div>
537
- ```
437
+ ### Zero-Runtime Mode
538
438
 
539
- Configure the Babel plugin:
439
+ Use `tastyStatic` when you want the same DSL and state model, but with CSS extracted at build time and no styling runtime in the client bundle. It is a strong fit for static sites, landing pages, and other build-time-first setups.
540
440
 
541
- ```js
542
- module.exports = {
543
- plugins: [
544
- ['@tenphi/tasty/babel-plugin', {
545
- output: 'public/tasty.css',
546
- config: {
547
- states: { '@dark': '@root(schema=dark)' },
548
- },
549
- }],
550
- ],
551
- };
552
- ```
441
+ See [Zero Runtime (tastyStatic)](docs/tasty-static.md) and [Getting Started - Choosing a rendering mode](docs/getting-started.md#choosing-a-rendering-mode).
553
442
 
554
443
  ### `tasty` vs `tastyStatic`
555
444
 
556
- | | `tasty` (runtime) | `tastyStatic` (zero-runtime) |
557
- |---|---|---|
558
- | **Output** | React component | CSS class name |
559
- | **CSS injection** | Runtime `<style>` tags | Build-time extraction |
560
- | **Runtime cost** | Style generation on mount | None |
561
- | **Generated CSS scope** | Only styles/variants used at runtime | All extracted static styles at build time |
562
- | **Dynamic values** | Fully supported | Via CSS custom properties |
563
- | **Sub-elements** | Built-in (`<C.Title>`) | Manual (`data-element`) |
564
- | **Variants** | Built-in (`variants` option) | Separate static styles |
565
- | **Framework** | React | Any (requires Babel) |
566
- | **Best for** | Interactive apps with reusable stateful components, design systems | Static sites, SSG, landing pages |
445
+ `tasty()` returns React components that compute CSS during rendering. In server-only contexts, this produces static HTML + CSS with zero client JavaScript — the same end result as `tastyStatic()` but with the full feature set. `tastyStatic()` returns class names and extracts CSS during the build via a Babel plugin, with no React dependency at runtime. Both share the same DSL, tokens, units, state mappings, and recipes. Use `tasty()` as the default for any React-based setup; use `tastyStatic()` when you need build-time extraction without React.
567
446
 
568
- Both share the same DSL, tokens, units, state mappings, and recipes.
447
+ See [Zero Runtime (tastyStatic)](docs/tasty-static.md), [React API](docs/react-api.md), and [Comparison - Build-time vs runtime](docs/comparison.md#build-time-vs-runtime).
569
448
 
570
449
  ### Server-Side Rendering
571
450
 
572
- SSR with zero-cost client hydration. Existing `tasty()` components work unchanged SSR is opt-in and requires no per-component modifications. Supports Next.js (App Router with streaming), Astro (middleware + islands), and any React-based framework via the core API. Requires React 18+.
573
-
574
- **Next.js setup:**
575
-
576
- ```tsx
577
- // app/tasty-registry.tsx
578
- 'use client';
579
-
580
- import { TastyRegistry } from '@tenphi/tasty/ssr/next';
451
+ `tasty()` components already work on the server without any SSR integration — they are hook-free and render as React Server Components by default. In server-only contexts (Next.js RSC, Astro without islands), they produce zero client JavaScript with the full feature set.
581
452
 
582
- export default function TastyStyleRegistry({
583
- children,
584
- }: {
585
- children: React.ReactNode;
586
- }) {
587
- return <TastyRegistry>{children}</TastyRegistry>;
588
- }
589
- ```
453
+ SSR integration (`TastyRegistry`, `tastyIntegration`) adds CSS batching, deduplication across component trees, FOUC prevention, and client cache hydration. Use it when your app also has client-side rendering:
590
454
 
591
- ```tsx
592
- // app/layout.tsx
593
- import TastyStyleRegistry from './tasty-registry';
594
-
595
- export default function RootLayout({
596
- children,
597
- }: {
598
- children: React.ReactNode;
599
- }) {
600
- return (
601
- <html>
602
- <body>
603
- <TastyStyleRegistry>{children}</TastyStyleRegistry>
604
- </body>
605
- </html>
606
- );
607
- }
608
- ```
455
+ - `@tenphi/tasty/ssr/next` for Next.js App Router (mixed server + client components)
456
+ - `@tenphi/tasty/ssr/astro` for Astro (with or without islands)
457
+ - The core SSR API for other React SSR setups
609
458
 
610
- See the [full SSR guide](docs/ssr.md) for Astro integration, streaming SSR, generic framework usage, troubleshooting, and the current requirements.
459
+ See the [full SSR guide](docs/ssr.md).
611
460
 
612
461
  ## Entry Points
613
462
 
614
463
  | Import | Description | Platform |
615
464
  |--------|-------------|----------|
616
- | `@tenphi/tasty` | Runtime style engine (`tasty`, hooks, `configure`) | Browser |
465
+ | `@tenphi/tasty` | Runtime style engine (`tasty`, style functions, `configure`) | Browser |
617
466
  | `@tenphi/tasty/static` | Zero-runtime static styles (`tastyStatic`) | Browser |
618
467
  | `@tenphi/tasty/core` | Lower-level internals (config, parser, pipeline, injector, style handlers) for tooling and advanced use | Browser / Node |
619
468
  | `@tenphi/tasty/babel-plugin` | Babel plugin for zero-runtime CSS extraction | Node |
@@ -621,7 +470,8 @@ See the [full SSR guide](docs/ssr.md) for Astro integration, streaming SSR, gene
621
470
  | `@tenphi/tasty/next` | Next.js integration wrapper | Node |
622
471
  | `@tenphi/tasty/ssr` | Core SSR API (collector, context, hydration) | Node |
623
472
  | `@tenphi/tasty/ssr/next` | Next.js App Router SSR integration | Node |
624
- | `@tenphi/tasty/ssr/astro` | Astro middleware + auto-hydration | Node / Browser |
473
+ | `@tenphi/tasty/ssr/astro` | Astro integration + middleware | Node |
474
+ | `@tenphi/tasty/ssr/astro-client` | Astro client-side cache hydration | Browser |
625
475
 
626
476
  ## Browser Requirements
627
477
 
@@ -761,7 +611,7 @@ Start from the docs hub if you want the shortest path to the right guide for you
761
611
  ### Reference
762
612
 
763
613
  - **[Style DSL](docs/dsl.md)** — The Tasty style language: state maps, tokens, units, color syntax, extending semantics, recipes, keyframes, and @property
764
- - **[Runtime API](docs/runtime.md)** — React-specific API: `tasty()` factory, component props, variants, sub-elements, and hooks
614
+ - **[React API](docs/react-api.md)** — React-specific API: `tasty()` factory, component props, variants, sub-elements, and style functions
765
615
  - **[Configuration](docs/configuration.md)** — Global configuration: tokens, recipes, custom units, style handlers, and TypeScript extensions
766
616
  - **[Style Properties](docs/styles.md)** — Complete reference for all enhanced style properties: syntax, values, modifiers, and recommendations
767
617
 
@@ -3,6 +3,5 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
3
3
  if (typeof require !== "undefined") return require.apply(this, arguments);
4
4
  throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
5
5
  });
6
-
7
6
  //#endregion
8
- export { __require };
7
+ export { __require };
@@ -0,0 +1 @@
1
+ export { };
@@ -1,5 +1,4 @@
1
1
  import { extractLocalPredefinedStates, extractPredefinedStateRefs } from "../states/index.js";
2
-
3
2
  //#region src/chunks/cacheKey.ts
4
3
  /**
5
4
  * Chunk-specific cache key generation.
@@ -72,7 +71,7 @@ function generateChunkCacheKey(styles, chunkName, styleKeys) {
72
71
  }
73
72
  return parts.join("\0");
74
73
  }
75
-
76
74
  //#endregion
77
75
  export { generateChunkCacheKey };
76
+
78
77
  //# sourceMappingURL=cacheKey.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cacheKey.js","names":[],"sources":["../../src/chunks/cacheKey.ts"],"sourcesContent":["/**\n * Chunk-specific cache key generation.\n *\n * Generates cache keys that only include styles relevant to a specific chunk,\n * enabling more granular caching and reuse.\n *\n * Enhanced to support predefined states:\n * - Global predefined states don't affect cache keys (constant across app)\n * - Local predefined states only affect cache keys if referenced in the chunk\n */\n\nimport {\n extractLocalPredefinedStates,\n extractPredefinedStateRefs,\n} from '../states';\nimport type { Styles } from '../styles/types';\n\nconst _stableStringifyCache = new WeakMap<object, string>();\n\n/**\n * Recursively serialize a value with sorted keys for stable output.\n * This ensures that {a: 1, b: 2} and {b: 2, a: 1} produce the same string.\n * Uses a WeakMap cache for object values to avoid re-serializing the same references.\n */\nfunction stableStringify(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return 'undefined';\n }\n if (typeof value !== 'object') {\n return JSON.stringify(value);\n }\n\n const cached = _stableStringifyCache.get(value as object);\n if (cached !== undefined) return cached;\n\n let result: string;\n if (Array.isArray(value)) {\n result = '[' + value.map(stableStringify).join(',') + ']';\n } else {\n const obj = value as Record<string, unknown>;\n const sortedKeys = Object.keys(obj).sort();\n const parts: string[] = [];\n for (const key of sortedKeys) {\n if (obj[key] !== undefined) {\n parts.push(`${JSON.stringify(key)}:${stableStringify(obj[key])}`);\n }\n }\n result = '{' + parts.join(',') + '}';\n }\n\n _stableStringifyCache.set(value as object, result);\n return result;\n}\n\n/**\n * Generate a cache key for a specific chunk.\n *\n * Only includes the styles that belong to this chunk, allowing\n * chunks to be cached independently.\n *\n * Also includes relevant local predefined states that are referenced\n * by this chunk's styles.\n *\n * @param styles - The full styles object\n * @param chunkName - Name of the chunk\n * @param styleKeys - Keys of styles belonging to this chunk\n * @returns A stable cache key string\n */\nexport function generateChunkCacheKey(\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): string {\n // Start with chunk name for namespace separation\n const parts: string[] = [chunkName];\n\n // styleKeys are already sorted by categorizeStyleKeys\n let chunkStylesStr = '';\n\n for (const key of styleKeys) {\n const value = styles[key];\n if (value !== undefined) {\n // Use stable stringify for consistent serialization regardless of key order\n const serialized = stableStringify(value);\n parts.push(`${key}:${serialized}`);\n chunkStylesStr += serialized;\n }\n }\n\n // Extract local predefined states from the full styles object\n const localStates = extractLocalPredefinedStates(styles);\n\n // Only include local predefined states that are actually referenced in this chunk\n if (Object.keys(localStates).length > 0) {\n const referencedStates = extractPredefinedStateRefs(chunkStylesStr);\n const relevantLocalStates: string[] = [];\n\n for (const stateName of referencedStates) {\n if (localStates[stateName]) {\n relevantLocalStates.push(`${stateName}=${localStates[stateName]}`);\n }\n }\n\n // Add relevant local states to the cache key (sorted for stability)\n if (relevantLocalStates.length > 0) {\n relevantLocalStates.sort();\n parts.unshift(`[states:${relevantLocalStates.join('|')}]`);\n }\n }\n\n // Use null character as separator (safe, not in JSON output)\n return parts.join('\\0');\n}\n"],"mappings":";;;;;;;;;;;;;AAiBA,MAAM,wCAAwB,IAAI,SAAyB;;;;;;AAO3D,SAAS,gBAAgB,OAAwB;AAC/C,KAAI,UAAU,KACZ,QAAO;AAET,KAAI,UAAU,OACZ,QAAO;AAET,KAAI,OAAO,UAAU,SACnB,QAAO,KAAK,UAAU,MAAM;CAG9B,MAAM,SAAS,sBAAsB,IAAI,MAAgB;AACzD,KAAI,WAAW,OAAW,QAAO;CAEjC,IAAI;AACJ,KAAI,MAAM,QAAQ,MAAM,CACtB,UAAS,MAAM,MAAM,IAAI,gBAAgB,CAAC,KAAK,IAAI,GAAG;MACjD;EACL,MAAM,MAAM;EACZ,MAAM,aAAa,OAAO,KAAK,IAAI,CAAC,MAAM;EAC1C,MAAM,QAAkB,EAAE;AAC1B,OAAK,MAAM,OAAO,WAChB,KAAI,IAAI,SAAS,OACf,OAAM,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,GAAG,gBAAgB,IAAI,KAAK,GAAG;AAGrE,WAAS,MAAM,MAAM,KAAK,IAAI,GAAG;;AAGnC,uBAAsB,IAAI,OAAiB,OAAO;AAClD,QAAO;;;;;;;;;;;;;;;;AAiBT,SAAgB,sBACd,QACA,WACA,WACQ;CAER,MAAM,QAAkB,CAAC,UAAU;CAGnC,IAAI,iBAAiB;AAErB,MAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,QAAW;GAEvB,MAAM,aAAa,gBAAgB,MAAM;AACzC,SAAM,KAAK,GAAG,IAAI,GAAG,aAAa;AAClC,qBAAkB;;;CAKtB,MAAM,cAAc,6BAA6B,OAAO;AAGxD,KAAI,OAAO,KAAK,YAAY,CAAC,SAAS,GAAG;EACvC,MAAM,mBAAmB,2BAA2B,eAAe;EACnE,MAAM,sBAAgC,EAAE;AAExC,OAAK,MAAM,aAAa,iBACtB,KAAI,YAAY,WACd,qBAAoB,KAAK,GAAG,UAAU,GAAG,YAAY,aAAa;AAKtE,MAAI,oBAAoB,SAAS,GAAG;AAClC,uBAAoB,MAAM;AAC1B,SAAM,QAAQ,WAAW,oBAAoB,KAAK,IAAI,CAAC,GAAG;;;AAK9D,QAAO,MAAM,KAAK,KAAK"}
1
+ {"version":3,"file":"cacheKey.js","names":[],"sources":["../../src/chunks/cacheKey.ts"],"sourcesContent":["/**\n * Chunk-specific cache key generation.\n *\n * Generates cache keys that only include styles relevant to a specific chunk,\n * enabling more granular caching and reuse.\n *\n * Enhanced to support predefined states:\n * - Global predefined states don't affect cache keys (constant across app)\n * - Local predefined states only affect cache keys if referenced in the chunk\n */\n\nimport {\n extractLocalPredefinedStates,\n extractPredefinedStateRefs,\n} from '../states';\nimport type { Styles } from '../styles/types';\n\nconst _stableStringifyCache = new WeakMap<object, string>();\n\n/**\n * Recursively serialize a value with sorted keys for stable output.\n * This ensures that {a: 1, b: 2} and {b: 2, a: 1} produce the same string.\n * Uses a WeakMap cache for object values to avoid re-serializing the same references.\n */\nfunction stableStringify(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return 'undefined';\n }\n if (typeof value !== 'object') {\n return JSON.stringify(value);\n }\n\n const cached = _stableStringifyCache.get(value as object);\n if (cached !== undefined) return cached;\n\n let result: string;\n if (Array.isArray(value)) {\n result = '[' + value.map(stableStringify).join(',') + ']';\n } else {\n const obj = value as Record<string, unknown>;\n const sortedKeys = Object.keys(obj).sort();\n const parts: string[] = [];\n for (const key of sortedKeys) {\n if (obj[key] !== undefined) {\n parts.push(`${JSON.stringify(key)}:${stableStringify(obj[key])}`);\n }\n }\n result = '{' + parts.join(',') + '}';\n }\n\n _stableStringifyCache.set(value as object, result);\n return result;\n}\n\n/**\n * Generate a cache key for a specific chunk.\n *\n * Only includes the styles that belong to this chunk, allowing\n * chunks to be cached independently.\n *\n * Also includes relevant local predefined states that are referenced\n * by this chunk's styles.\n *\n * @param styles - The full styles object\n * @param chunkName - Name of the chunk\n * @param styleKeys - Keys of styles belonging to this chunk\n * @returns A stable cache key string\n */\nexport function generateChunkCacheKey(\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): string {\n // Start with chunk name for namespace separation\n const parts: string[] = [chunkName];\n\n // styleKeys are already sorted by categorizeStyleKeys\n let chunkStylesStr = '';\n\n for (const key of styleKeys) {\n const value = styles[key];\n if (value !== undefined) {\n // Use stable stringify for consistent serialization regardless of key order\n const serialized = stableStringify(value);\n parts.push(`${key}:${serialized}`);\n chunkStylesStr += serialized;\n }\n }\n\n // Extract local predefined states from the full styles object\n const localStates = extractLocalPredefinedStates(styles);\n\n // Only include local predefined states that are actually referenced in this chunk\n if (Object.keys(localStates).length > 0) {\n const referencedStates = extractPredefinedStateRefs(chunkStylesStr);\n const relevantLocalStates: string[] = [];\n\n for (const stateName of referencedStates) {\n if (localStates[stateName]) {\n relevantLocalStates.push(`${stateName}=${localStates[stateName]}`);\n }\n }\n\n // Add relevant local states to the cache key (sorted for stability)\n if (relevantLocalStates.length > 0) {\n relevantLocalStates.sort();\n parts.unshift(`[states:${relevantLocalStates.join('|')}]`);\n }\n }\n\n // Use null character as separator (safe, not in JSON output)\n return parts.join('\\0');\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAM,wCAAwB,IAAI,SAAyB;;;;;;AAO3D,SAAS,gBAAgB,OAAwB;AAC/C,KAAI,UAAU,KACZ,QAAO;AAET,KAAI,UAAU,KAAA,EACZ,QAAO;AAET,KAAI,OAAO,UAAU,SACnB,QAAO,KAAK,UAAU,MAAM;CAG9B,MAAM,SAAS,sBAAsB,IAAI,MAAgB;AACzD,KAAI,WAAW,KAAA,EAAW,QAAO;CAEjC,IAAI;AACJ,KAAI,MAAM,QAAQ,MAAM,CACtB,UAAS,MAAM,MAAM,IAAI,gBAAgB,CAAC,KAAK,IAAI,GAAG;MACjD;EACL,MAAM,MAAM;EACZ,MAAM,aAAa,OAAO,KAAK,IAAI,CAAC,MAAM;EAC1C,MAAM,QAAkB,EAAE;AAC1B,OAAK,MAAM,OAAO,WAChB,KAAI,IAAI,SAAS,KAAA,EACf,OAAM,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,GAAG,gBAAgB,IAAI,KAAK,GAAG;AAGrE,WAAS,MAAM,MAAM,KAAK,IAAI,GAAG;;AAGnC,uBAAsB,IAAI,OAAiB,OAAO;AAClD,QAAO;;;;;;;;;;;;;;;;AAiBT,SAAgB,sBACd,QACA,WACA,WACQ;CAER,MAAM,QAAkB,CAAC,UAAU;CAGnC,IAAI,iBAAiB;AAErB,MAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,KAAA,GAAW;GAEvB,MAAM,aAAa,gBAAgB,MAAM;AACzC,SAAM,KAAK,GAAG,IAAI,GAAG,aAAa;AAClC,qBAAkB;;;CAKtB,MAAM,cAAc,6BAA6B,OAAO;AAGxD,KAAI,OAAO,KAAK,YAAY,CAAC,SAAS,GAAG;EACvC,MAAM,mBAAmB,2BAA2B,eAAe;EACnE,MAAM,sBAAgC,EAAE;AAExC,OAAK,MAAM,aAAa,iBACtB,KAAI,YAAY,WACd,qBAAoB,KAAK,GAAG,UAAU,GAAG,YAAY,aAAa;AAKtE,MAAI,oBAAoB,SAAS,GAAG;AAClC,uBAAoB,MAAM;AAC1B,SAAM,QAAQ,WAAW,oBAAoB,KAAK,IAAI,CAAC,GAAG;;;AAK9D,QAAO,MAAM,KAAK,KAAK"}
@@ -1,6 +1,6 @@
1
1
  //#region src/chunks/definitions.d.ts
2
2
  declare const CHUNK_NAMES: {
3
- /** Special chunk for styles that cannot be split (e.g., @starting-style) */readonly COMBINED: "combined";
3
+ /** Special chunk for styles that cannot be split */readonly COMBINED: "combined";
4
4
  readonly SUBCOMPONENTS: "subcomponents";
5
5
  readonly APPEARANCE: "appearance";
6
6
  readonly FONT: "font";
@@ -1,5 +1,4 @@
1
1
  import { isSelector } from "../pipeline/index.js";
2
-
3
2
  //#region src/chunks/definitions.ts
4
3
  /**
5
4
  * Style chunk definitions for CSS chunking optimization.
@@ -238,7 +237,7 @@ function categorizeStyleKeys(styles) {
238
237
  const chunkData = {};
239
238
  const keys = Object.keys(styles);
240
239
  for (const key of keys) {
241
- if (key === "$" || key === "@keyframes" || key === "@properties" || key === "recipe") continue;
240
+ if (key === "$" || key === "@keyframes" || key === "@properties" || key === "@fontFace" || key === "@counterStyle" || key === "recipe") continue;
242
241
  if (isSelector(key)) {
243
242
  if (!chunkData[CHUNK_NAMES.SUBCOMPONENTS]) chunkData[CHUNK_NAMES.SUBCOMPONENTS] = [];
244
243
  chunkData[CHUNK_NAMES.SUBCOMPONENTS].push(key);
@@ -253,7 +252,7 @@ function categorizeStyleKeys(styles) {
253
252
  for (const chunkName of Object.keys(chunkData)) if (!orderedChunks.has(chunkName)) orderedChunks.set(chunkName, chunkData[chunkName].sort());
254
253
  return orderedChunks;
255
254
  }
256
-
257
255
  //#endregion
258
256
  export { CHUNK_NAMES, STYLE_TO_CHUNK, categorizeStyleKeys };
257
+
259
258
  //# sourceMappingURL=definitions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","names":[],"sources":["../../src/chunks/definitions.ts"],"sourcesContent":["/**\n * Style chunk definitions for CSS chunking optimization.\n *\n * Styles are grouped into chunks based on:\n * 1. Handler dependencies - styles that share a handler MUST be in the same chunk\n * 2. Logical grouping - related styles grouped for better cache reuse\n *\n * See STYLE_CHUNKING_SPEC.md for detailed rationale.\n *\n * ============================================================================\n * ⚠️ CRITICAL ARCHITECTURAL CONSTRAINT: NO CROSS-CHUNK HANDLER DEPENDENCIES\n * ============================================================================\n *\n * Style handlers declare their dependencies via `__lookupStyles` array.\n * This creates a dependency graph where handlers read multiple style props.\n *\n * **ALL styles in a handler's `__lookupStyles` MUST be in the SAME chunk.**\n *\n * Why this matters:\n * 1. Each chunk computes a cache key from ONLY its own style values\n * 2. If a handler reads a style from another chunk, that value isn't in the cache key\n * 3. Changing the cross-chunk style won't invalidate this chunk's cache\n * 4. Result: stale CSS output or incorrect cache hits\n *\n * Example of a violation:\n * ```\n * // flowStyle.__lookupStyles = ['display', 'flow']\n * // If 'display' is in DISPLAY chunk and 'flow' is in LAYOUT chunk:\n * // - User sets { display: 'grid', flow: 'column' }\n * // - LAYOUT chunk caches CSS with flow=column, display=grid\n * // - User changes to { display: 'flex', flow: 'column' }\n * // - LAYOUT chunk cache key unchanged (only has 'flow')\n * // - Returns stale CSS computed with display=grid!\n * ```\n *\n * Before adding/moving styles, verify:\n * 1. Find all handlers that use this style (grep for the style name in __lookupStyles)\n * 2. Ensure ALL styles from each handler's __lookupStyles are in the same chunk\n * ============================================================================\n */\n\nimport { isSelector } from '../pipeline';\n\n// ============================================================================\n// Chunk Style Lists\n// ============================================================================\n\n/**\n * Appearance chunk - visual styling with independent handlers\n */\nexport const APPEARANCE_CHUNK_STYLES = [\n 'fill', // fillStyle (independent)\n 'color', // colorStyle (independent)\n 'opacity', // independent\n 'border', // borderStyle (independent)\n 'radius', // radiusStyle (independent)\n 'outline', // outlineStyle: outline ↔ outlineOffset\n 'outlineOffset', // outlineStyle: outline ↔ outlineOffset\n 'shadow', // shadowStyle (independent)\n 'fade', // fadeStyle (independent)\n] as const;\n\n/**\n * Font chunk - typography styles\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ presetStyle: preset, fontSize, lineHeight, letterSpacing, textTransform,\n * fontWeight, fontStyle, font\n */\nexport const FONT_CHUNK_STYLES = [\n // All from presetStyle handler - MUST stay together\n 'preset',\n 'font',\n 'fontWeight',\n 'fontStyle',\n 'fontSize',\n 'lineHeight',\n 'letterSpacing',\n 'textTransform',\n // Independent text styles grouped for cohesion\n 'fontFamily', // independent alias (logical grouping with font styles)\n 'textAlign',\n 'textDecoration',\n 'wordBreak',\n 'wordWrap',\n 'boldFontWeight',\n] as const;\n\n/**\n * Dimension chunk - sizing and spacing\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ paddingStyle: padding, paddingTop/Right/Bottom/Left, paddingBlock/Inline\n * ⚠️ marginStyle: margin, marginTop/Right/Bottom/Left, marginBlock/Inline\n * ⚠️ widthStyle: width, minWidth, maxWidth\n * ⚠️ heightStyle: height, minHeight, maxHeight\n */\nexport const DIMENSION_CHUNK_STYLES = [\n // All from paddingStyle handler - MUST stay together\n 'padding',\n 'paddingTop',\n 'paddingRight',\n 'paddingBottom',\n 'paddingLeft',\n 'paddingBlock',\n 'paddingInline',\n // All from marginStyle handler - MUST stay together\n 'margin',\n 'marginTop',\n 'marginRight',\n 'marginBottom',\n 'marginLeft',\n 'marginBlock',\n 'marginInline',\n // widthStyle handler - MUST stay together\n 'width',\n 'minWidth',\n 'maxWidth',\n // heightStyle handler - MUST stay together\n 'height',\n 'minHeight',\n 'maxHeight',\n 'flexBasis',\n 'flexGrow',\n 'flexShrink',\n 'flex',\n] as const;\n\n/**\n * Display chunk - display mode, layout flow, text overflow, and scrollbar\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ displayStyle: display, hide, textOverflow, overflow, whiteSpace\n * ⚠️ flowStyle: display, flow\n * ⚠️ gapStyle: display, flow, gap\n * ⚠️ scrollbarStyle: scrollbar, overflow\n */\nexport const DISPLAY_CHUNK_STYLES = [\n // displayStyle handler\n 'display',\n 'hide',\n 'textOverflow',\n 'overflow', // also used by scrollbarStyle\n 'whiteSpace',\n // flowStyle handler (requires display)\n 'flow',\n // gapStyle handler (requires display, flow)\n 'gap',\n // scrollbarStyle handler (requires overflow)\n 'scrollbar',\n] as const;\n\n/**\n * Layout chunk - flex/grid alignment and grid templates\n *\n * Note: flow and gap are in DISPLAY chunk due to handler dependencies\n * (flowStyle and gapStyle both require 'display' prop).\n */\nexport const LAYOUT_CHUNK_STYLES = [\n // Alignment styles (all independent handlers)\n 'placeItems',\n 'placeContent',\n 'alignItems',\n 'alignContent',\n 'justifyItems',\n 'justifyContent',\n 'align', // alignStyle (independent)\n 'justify', // justifyStyle (independent)\n 'place', // placeStyle (independent)\n 'columnGap',\n 'rowGap',\n // Grid template styles\n 'gridColumns',\n 'gridRows',\n 'gridTemplate',\n 'gridAreas',\n 'gridAutoFlow',\n 'gridAutoColumns',\n 'gridAutoRows',\n] as const;\n\n/**\n * Position chunk - element positioning\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ insetStyle: inset, insetBlock, insetInline, top, right, bottom, left\n */\nexport const POSITION_CHUNK_STYLES = [\n 'position',\n // All from insetStyle handler - MUST stay together\n 'inset',\n 'insetBlock',\n 'insetInline',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'zIndex',\n 'gridArea',\n 'gridColumn',\n 'gridRow',\n 'order',\n 'placeSelf',\n 'alignSelf',\n 'justifySelf',\n 'transform',\n 'transition',\n 'animation',\n] as const;\n\n// ============================================================================\n// Chunk Names\n// ============================================================================\n\nexport const CHUNK_NAMES = {\n /** Special chunk for styles that cannot be split (e.g., @starting-style) */\n COMBINED: 'combined',\n SUBCOMPONENTS: 'subcomponents',\n APPEARANCE: 'appearance',\n FONT: 'font',\n DIMENSION: 'dimension',\n DISPLAY: 'display',\n LAYOUT: 'layout',\n POSITION: 'position',\n MISC: 'misc',\n} as const;\n\nexport type ChunkName = (typeof CHUNK_NAMES)[keyof typeof CHUNK_NAMES];\n\n// ============================================================================\n// Style-to-Chunk Lookup Map (O(1) categorization)\n// ============================================================================\n\n/**\n * Pre-computed map for O(1) style-to-chunk lookup.\n * Built once at module load time.\n */\nexport const STYLE_TO_CHUNK = new Map<string, ChunkName>();\n\n// Populate the lookup map\nfunction populateStyleToChunkMap() {\n for (const style of APPEARANCE_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.APPEARANCE);\n }\n for (const style of FONT_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.FONT);\n }\n for (const style of DIMENSION_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.DIMENSION);\n }\n for (const style of DISPLAY_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.DISPLAY);\n }\n for (const style of LAYOUT_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.LAYOUT);\n }\n for (const style of POSITION_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.POSITION);\n }\n}\n\n// Initialize at module load\npopulateStyleToChunkMap();\n\n// ============================================================================\n// Chunk Priority Order\n// ============================================================================\n\n/**\n * Chunk processing order. This ensures deterministic className allocation\n * regardless of style key order in the input.\n */\nconst CHUNK_ORDER: readonly string[] = [\n CHUNK_NAMES.APPEARANCE,\n CHUNK_NAMES.FONT,\n CHUNK_NAMES.DIMENSION,\n CHUNK_NAMES.DISPLAY,\n CHUNK_NAMES.LAYOUT,\n CHUNK_NAMES.POSITION,\n CHUNK_NAMES.MISC,\n CHUNK_NAMES.SUBCOMPONENTS,\n] as const;\n\n/**\n * Map from chunk name to its priority index for sorting.\n */\nconst _CHUNK_PRIORITY = new Map<string, number>(\n CHUNK_ORDER.map((name, index) => [name, index]),\n);\n\n// ============================================================================\n// Chunk Info Interface\n// ============================================================================\n\nexport interface ChunkInfo {\n /** Name of the chunk */\n name: ChunkName | string;\n /** Style keys belonging to this chunk */\n styleKeys: string[];\n}\n\n// ============================================================================\n// Style Categorization\n// ============================================================================\n\n/**\n * Categorize style keys into chunks.\n *\n * Returns chunks in a deterministic order (by CHUNK_ORDER) regardless\n * of the order of keys in the input styles object.\n *\n * @param styles - The styles object to categorize\n * @returns Map of chunk name to array of style keys in that chunk (in priority order)\n */\nexport function categorizeStyleKeys(\n styles: Record<string, unknown>,\n): Map<string, string[]> {\n // First pass: collect keys into chunks (unordered)\n const chunkData: Record<string, string[]> = {};\n const keys = Object.keys(styles);\n\n for (const key of keys) {\n // Skip the $ helper key (used for selector combinators)\n // Skip @keyframes and @properties (processed separately in useStyles)\n // Skip recipe (resolved before pipeline by resolveRecipes)\n if (\n key === '$' ||\n key === '@keyframes' ||\n key === '@properties' ||\n key === 'recipe'\n ) {\n continue;\n }\n\n if (isSelector(key)) {\n // All selectors go into the subcomponents chunk\n if (!chunkData[CHUNK_NAMES.SUBCOMPONENTS]) {\n chunkData[CHUNK_NAMES.SUBCOMPONENTS] = [];\n }\n chunkData[CHUNK_NAMES.SUBCOMPONENTS].push(key);\n } else {\n // Look up the chunk for this style, default to misc\n const chunkName = STYLE_TO_CHUNK.get(key) ?? CHUNK_NAMES.MISC;\n if (!chunkData[chunkName]) {\n chunkData[chunkName] = [];\n }\n chunkData[chunkName].push(key);\n }\n }\n\n // Second pass: build ordered Map based on CHUNK_ORDER\n const orderedChunks = new Map<string, string[]>();\n\n // Add chunks in priority order\n for (const chunkName of CHUNK_ORDER) {\n if (chunkData[chunkName] && chunkData[chunkName].length > 0) {\n // Sort keys within chunk for consistent cache key generation\n orderedChunks.set(chunkName, chunkData[chunkName].sort());\n }\n }\n\n // Handle any unknown chunks (shouldn't happen, but be defensive)\n for (const chunkName of Object.keys(chunkData)) {\n if (!orderedChunks.has(chunkName)) {\n orderedChunks.set(chunkName, chunkData[chunkName].sort());\n }\n }\n\n return orderedChunks;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,MAAa,0BAA0B;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;AASD,MAAa,oBAAoB;CAE/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;AAWD,MAAa,yBAAyB;CAEpC;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;AAWD,MAAa,uBAAuB;CAElC;CACA;CACA;CACA;CACA;CAEA;CAEA;CAEA;CACD;;;;;;;AAQD,MAAa,sBAAsB;CAEjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAQD,MAAa,wBAAwB;CACnC;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAMD,MAAa,cAAc;CAEzB,UAAU;CACV,eAAe;CACf,YAAY;CACZ,MAAM;CACN,WAAW;CACX,SAAS;CACT,QAAQ;CACR,UAAU;CACV,MAAM;CACP;;;;;AAYD,MAAa,iCAAiB,IAAI,KAAwB;AAG1D,SAAS,0BAA0B;AACjC,MAAK,MAAM,SAAS,wBAClB,gBAAe,IAAI,OAAO,YAAY,WAAW;AAEnD,MAAK,MAAM,SAAS,kBAClB,gBAAe,IAAI,OAAO,YAAY,KAAK;AAE7C,MAAK,MAAM,SAAS,uBAClB,gBAAe,IAAI,OAAO,YAAY,UAAU;AAElD,MAAK,MAAM,SAAS,qBAClB,gBAAe,IAAI,OAAO,YAAY,QAAQ;AAEhD,MAAK,MAAM,SAAS,oBAClB,gBAAe,IAAI,OAAO,YAAY,OAAO;AAE/C,MAAK,MAAM,SAAS,sBAClB,gBAAe,IAAI,OAAO,YAAY,SAAS;;AAKnD,yBAAyB;;;;;AAUzB,MAAM,cAAiC;CACrC,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACb;AAKuB,IAAI,IAC1B,YAAY,KAAK,MAAM,UAAU,CAAC,MAAM,MAAM,CAAC,CAChD;;;;;;;;;;AA0BD,SAAgB,oBACd,QACuB;CAEvB,MAAM,YAAsC,EAAE;CAC9C,MAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAK,MAAM,OAAO,MAAM;AAItB,MACE,QAAQ,OACR,QAAQ,gBACR,QAAQ,iBACR,QAAQ,SAER;AAGF,MAAI,WAAW,IAAI,EAAE;AAEnB,OAAI,CAAC,UAAU,YAAY,eACzB,WAAU,YAAY,iBAAiB,EAAE;AAE3C,aAAU,YAAY,eAAe,KAAK,IAAI;SACzC;GAEL,MAAM,YAAY,eAAe,IAAI,IAAI,IAAI,YAAY;AACzD,OAAI,CAAC,UAAU,WACb,WAAU,aAAa,EAAE;AAE3B,aAAU,WAAW,KAAK,IAAI;;;CAKlC,MAAM,gCAAgB,IAAI,KAAuB;AAGjD,MAAK,MAAM,aAAa,YACtB,KAAI,UAAU,cAAc,UAAU,WAAW,SAAS,EAExD,eAAc,IAAI,WAAW,UAAU,WAAW,MAAM,CAAC;AAK7D,MAAK,MAAM,aAAa,OAAO,KAAK,UAAU,CAC5C,KAAI,CAAC,cAAc,IAAI,UAAU,CAC/B,eAAc,IAAI,WAAW,UAAU,WAAW,MAAM,CAAC;AAI7D,QAAO"}
1
+ {"version":3,"file":"definitions.js","names":[],"sources":["../../src/chunks/definitions.ts"],"sourcesContent":["/**\n * Style chunk definitions for CSS chunking optimization.\n *\n * Styles are grouped into chunks based on:\n * 1. Handler dependencies - styles that share a handler MUST be in the same chunk\n * 2. Logical grouping - related styles grouped for better cache reuse\n *\n * See STYLE_CHUNKING_SPEC.md for detailed rationale.\n *\n * ============================================================================\n * ⚠️ CRITICAL ARCHITECTURAL CONSTRAINT: NO CROSS-CHUNK HANDLER DEPENDENCIES\n * ============================================================================\n *\n * Style handlers declare their dependencies via `__lookupStyles` array.\n * This creates a dependency graph where handlers read multiple style props.\n *\n * **ALL styles in a handler's `__lookupStyles` MUST be in the SAME chunk.**\n *\n * Why this matters:\n * 1. Each chunk computes a cache key from ONLY its own style values\n * 2. If a handler reads a style from another chunk, that value isn't in the cache key\n * 3. Changing the cross-chunk style won't invalidate this chunk's cache\n * 4. Result: stale CSS output or incorrect cache hits\n *\n * Example of a violation:\n * ```\n * // flowStyle.__lookupStyles = ['display', 'flow']\n * // If 'display' is in DISPLAY chunk and 'flow' is in LAYOUT chunk:\n * // - User sets { display: 'grid', flow: 'column' }\n * // - LAYOUT chunk caches CSS with flow=column, display=grid\n * // - User changes to { display: 'flex', flow: 'column' }\n * // - LAYOUT chunk cache key unchanged (only has 'flow')\n * // - Returns stale CSS computed with display=grid!\n * ```\n *\n * Before adding/moving styles, verify:\n * 1. Find all handlers that use this style (grep for the style name in __lookupStyles)\n * 2. Ensure ALL styles from each handler's __lookupStyles are in the same chunk\n * ============================================================================\n */\n\nimport { isSelector } from '../pipeline';\n\n// ============================================================================\n// Chunk Style Lists\n// ============================================================================\n\n/**\n * Appearance chunk - visual styling with independent handlers\n */\nexport const APPEARANCE_CHUNK_STYLES = [\n 'fill', // fillStyle (independent)\n 'color', // colorStyle (independent)\n 'opacity', // independent\n 'border', // borderStyle (independent)\n 'radius', // radiusStyle (independent)\n 'outline', // outlineStyle: outline ↔ outlineOffset\n 'outlineOffset', // outlineStyle: outline ↔ outlineOffset\n 'shadow', // shadowStyle (independent)\n 'fade', // fadeStyle (independent)\n] as const;\n\n/**\n * Font chunk - typography styles\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ presetStyle: preset, fontSize, lineHeight, letterSpacing, textTransform,\n * fontWeight, fontStyle, font\n */\nexport const FONT_CHUNK_STYLES = [\n // All from presetStyle handler - MUST stay together\n 'preset',\n 'font',\n 'fontWeight',\n 'fontStyle',\n 'fontSize',\n 'lineHeight',\n 'letterSpacing',\n 'textTransform',\n // Independent text styles grouped for cohesion\n 'fontFamily', // independent alias (logical grouping with font styles)\n 'textAlign',\n 'textDecoration',\n 'wordBreak',\n 'wordWrap',\n 'boldFontWeight',\n] as const;\n\n/**\n * Dimension chunk - sizing and spacing\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ paddingStyle: padding, paddingTop/Right/Bottom/Left, paddingBlock/Inline\n * ⚠️ marginStyle: margin, marginTop/Right/Bottom/Left, marginBlock/Inline\n * ⚠️ widthStyle: width, minWidth, maxWidth\n * ⚠️ heightStyle: height, minHeight, maxHeight\n */\nexport const DIMENSION_CHUNK_STYLES = [\n // All from paddingStyle handler - MUST stay together\n 'padding',\n 'paddingTop',\n 'paddingRight',\n 'paddingBottom',\n 'paddingLeft',\n 'paddingBlock',\n 'paddingInline',\n // All from marginStyle handler - MUST stay together\n 'margin',\n 'marginTop',\n 'marginRight',\n 'marginBottom',\n 'marginLeft',\n 'marginBlock',\n 'marginInline',\n // widthStyle handler - MUST stay together\n 'width',\n 'minWidth',\n 'maxWidth',\n // heightStyle handler - MUST stay together\n 'height',\n 'minHeight',\n 'maxHeight',\n 'flexBasis',\n 'flexGrow',\n 'flexShrink',\n 'flex',\n] as const;\n\n/**\n * Display chunk - display mode, layout flow, text overflow, and scrollbar\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ displayStyle: display, hide, textOverflow, overflow, whiteSpace\n * ⚠️ flowStyle: display, flow\n * ⚠️ gapStyle: display, flow, gap\n * ⚠️ scrollbarStyle: scrollbar, overflow\n */\nexport const DISPLAY_CHUNK_STYLES = [\n // displayStyle handler\n 'display',\n 'hide',\n 'textOverflow',\n 'overflow', // also used by scrollbarStyle\n 'whiteSpace',\n // flowStyle handler (requires display)\n 'flow',\n // gapStyle handler (requires display, flow)\n 'gap',\n // scrollbarStyle handler (requires overflow)\n 'scrollbar',\n] as const;\n\n/**\n * Layout chunk - flex/grid alignment and grid templates\n *\n * Note: flow and gap are in DISPLAY chunk due to handler dependencies\n * (flowStyle and gapStyle both require 'display' prop).\n */\nexport const LAYOUT_CHUNK_STYLES = [\n // Alignment styles (all independent handlers)\n 'placeItems',\n 'placeContent',\n 'alignItems',\n 'alignContent',\n 'justifyItems',\n 'justifyContent',\n 'align', // placementStyle\n 'justify', // placementStyle\n 'place', // placementStyle\n 'columnGap',\n 'rowGap',\n // Grid template styles\n 'gridColumns',\n 'gridRows',\n 'gridTemplate',\n 'gridAreas',\n 'gridAutoFlow',\n 'gridAutoColumns',\n 'gridAutoRows',\n] as const;\n\n/**\n * Position chunk - element positioning\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ insetStyle: inset, insetBlock, insetInline, top, right, bottom, left\n */\nexport const POSITION_CHUNK_STYLES = [\n 'position',\n // All from insetStyle handler - MUST stay together\n 'inset',\n 'insetBlock',\n 'insetInline',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'zIndex',\n 'gridArea',\n 'gridColumn',\n 'gridRow',\n 'order',\n 'placeSelf',\n 'alignSelf',\n 'justifySelf',\n 'transform',\n 'transition',\n 'animation',\n] as const;\n\n// ============================================================================\n// Chunk Names\n// ============================================================================\n\nexport const CHUNK_NAMES = {\n /** Special chunk for styles that cannot be split */\n COMBINED: 'combined',\n SUBCOMPONENTS: 'subcomponents',\n APPEARANCE: 'appearance',\n FONT: 'font',\n DIMENSION: 'dimension',\n DISPLAY: 'display',\n LAYOUT: 'layout',\n POSITION: 'position',\n MISC: 'misc',\n} as const;\n\nexport type ChunkName = (typeof CHUNK_NAMES)[keyof typeof CHUNK_NAMES];\n\n// ============================================================================\n// Style-to-Chunk Lookup Map (O(1) categorization)\n// ============================================================================\n\n/**\n * Pre-computed map for O(1) style-to-chunk lookup.\n * Built once at module load time.\n */\nexport const STYLE_TO_CHUNK = new Map<string, ChunkName>();\n\n// Populate the lookup map\nfunction populateStyleToChunkMap() {\n for (const style of APPEARANCE_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.APPEARANCE);\n }\n for (const style of FONT_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.FONT);\n }\n for (const style of DIMENSION_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.DIMENSION);\n }\n for (const style of DISPLAY_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.DISPLAY);\n }\n for (const style of LAYOUT_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.LAYOUT);\n }\n for (const style of POSITION_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.POSITION);\n }\n}\n\n// Initialize at module load\npopulateStyleToChunkMap();\n\n// ============================================================================\n// Chunk Priority Order\n// ============================================================================\n\n/**\n * Chunk processing order. This ensures deterministic className allocation\n * regardless of style key order in the input.\n */\nconst CHUNK_ORDER: readonly string[] = [\n CHUNK_NAMES.APPEARANCE,\n CHUNK_NAMES.FONT,\n CHUNK_NAMES.DIMENSION,\n CHUNK_NAMES.DISPLAY,\n CHUNK_NAMES.LAYOUT,\n CHUNK_NAMES.POSITION,\n CHUNK_NAMES.MISC,\n CHUNK_NAMES.SUBCOMPONENTS,\n] as const;\n\n/**\n * Map from chunk name to its priority index for sorting.\n */\nconst _CHUNK_PRIORITY = new Map<string, number>(\n CHUNK_ORDER.map((name, index) => [name, index]),\n);\n\n// ============================================================================\n// Chunk Info Interface\n// ============================================================================\n\nexport interface ChunkInfo {\n /** Name of the chunk */\n name: ChunkName | string;\n /** Style keys belonging to this chunk */\n styleKeys: string[];\n}\n\n// ============================================================================\n// Style Categorization\n// ============================================================================\n\n/**\n * Categorize style keys into chunks.\n *\n * Returns chunks in a deterministic order (by CHUNK_ORDER) regardless\n * of the order of keys in the input styles object.\n *\n * @param styles - The styles object to categorize\n * @returns Map of chunk name to array of style keys in that chunk (in priority order)\n */\nexport function categorizeStyleKeys(\n styles: Record<string, unknown>,\n): Map<string, string[]> {\n // First pass: collect keys into chunks (unordered)\n const chunkData: Record<string, string[]> = {};\n const keys = Object.keys(styles);\n\n for (const key of keys) {\n // Skip the $ helper key (used for selector combinators)\n // Skip @keyframes and @properties (processed separately in useStyles)\n // Skip recipe (resolved before pipeline by resolveRecipes)\n if (\n key === '$' ||\n key === '@keyframes' ||\n key === '@properties' ||\n key === '@fontFace' ||\n key === '@counterStyle' ||\n key === 'recipe'\n ) {\n continue;\n }\n\n if (isSelector(key)) {\n // All selectors go into the subcomponents chunk\n if (!chunkData[CHUNK_NAMES.SUBCOMPONENTS]) {\n chunkData[CHUNK_NAMES.SUBCOMPONENTS] = [];\n }\n chunkData[CHUNK_NAMES.SUBCOMPONENTS].push(key);\n } else {\n // Look up the chunk for this style, default to misc\n const chunkName = STYLE_TO_CHUNK.get(key) ?? CHUNK_NAMES.MISC;\n if (!chunkData[chunkName]) {\n chunkData[chunkName] = [];\n }\n chunkData[chunkName].push(key);\n }\n }\n\n // Second pass: build ordered Map based on CHUNK_ORDER\n const orderedChunks = new Map<string, string[]>();\n\n // Add chunks in priority order\n for (const chunkName of CHUNK_ORDER) {\n if (chunkData[chunkName] && chunkData[chunkName].length > 0) {\n // Sort keys within chunk for consistent cache key generation\n orderedChunks.set(chunkName, chunkData[chunkName].sort());\n }\n }\n\n // Handle any unknown chunks (shouldn't happen, but be defensive)\n for (const chunkName of Object.keys(chunkData)) {\n if (!orderedChunks.has(chunkName)) {\n orderedChunks.set(chunkName, chunkData[chunkName].sort());\n }\n }\n\n return orderedChunks;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,MAAa,0BAA0B;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;AASD,MAAa,oBAAoB;CAE/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;AAWD,MAAa,yBAAyB;CAEpC;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;AAWD,MAAa,uBAAuB;CAElC;CACA;CACA;CACA;CACA;CAEA;CAEA;CAEA;CACD;;;;;;;AAQD,MAAa,sBAAsB;CAEjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAQD,MAAa,wBAAwB;CACnC;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAMD,MAAa,cAAc;CAEzB,UAAU;CACV,eAAe;CACf,YAAY;CACZ,MAAM;CACN,WAAW;CACX,SAAS;CACT,QAAQ;CACR,UAAU;CACV,MAAM;CACP;;;;;AAYD,MAAa,iCAAiB,IAAI,KAAwB;AAG1D,SAAS,0BAA0B;AACjC,MAAK,MAAM,SAAS,wBAClB,gBAAe,IAAI,OAAO,YAAY,WAAW;AAEnD,MAAK,MAAM,SAAS,kBAClB,gBAAe,IAAI,OAAO,YAAY,KAAK;AAE7C,MAAK,MAAM,SAAS,uBAClB,gBAAe,IAAI,OAAO,YAAY,UAAU;AAElD,MAAK,MAAM,SAAS,qBAClB,gBAAe,IAAI,OAAO,YAAY,QAAQ;AAEhD,MAAK,MAAM,SAAS,oBAClB,gBAAe,IAAI,OAAO,YAAY,OAAO;AAE/C,MAAK,MAAM,SAAS,sBAClB,gBAAe,IAAI,OAAO,YAAY,SAAS;;AAKnD,yBAAyB;;;;;AAUzB,MAAM,cAAiC;CACrC,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACb;AAKuB,IAAI,IAC1B,YAAY,KAAK,MAAM,UAAU,CAAC,MAAM,MAAM,CAAC,CAChD;;;;;;;;;;AA0BD,SAAgB,oBACd,QACuB;CAEvB,MAAM,YAAsC,EAAE;CAC9C,MAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAK,MAAM,OAAO,MAAM;AAItB,MACE,QAAQ,OACR,QAAQ,gBACR,QAAQ,iBACR,QAAQ,eACR,QAAQ,mBACR,QAAQ,SAER;AAGF,MAAI,WAAW,IAAI,EAAE;AAEnB,OAAI,CAAC,UAAU,YAAY,eACzB,WAAU,YAAY,iBAAiB,EAAE;AAE3C,aAAU,YAAY,eAAe,KAAK,IAAI;SACzC;GAEL,MAAM,YAAY,eAAe,IAAI,IAAI,IAAI,YAAY;AACzD,OAAI,CAAC,UAAU,WACb,WAAU,aAAa,EAAE;AAE3B,aAAU,WAAW,KAAK,IAAI;;;CAKlC,MAAM,gCAAgB,IAAI,KAAuB;AAGjD,MAAK,MAAM,aAAa,YACtB,KAAI,UAAU,cAAc,UAAU,WAAW,SAAS,EAExD,eAAc,IAAI,WAAW,UAAU,WAAW,MAAM,CAAC;AAK7D,MAAK,MAAM,aAAa,OAAO,KAAK,UAAU,CAC5C,KAAI,CAAC,cAAc,IAAI,UAAU,CAC/B,eAAc,IAAI,WAAW,UAAU,WAAW,MAAM,CAAC;AAI7D,QAAO"}
@@ -0,0 +1 @@
1
+ import { CHUNK_NAMES, ChunkInfo, ChunkName, STYLE_TO_CHUNK, categorizeStyleKeys } from "./definitions.js";
@@ -0,0 +1 @@
1
+ export { };
@@ -1,7 +1,6 @@
1
1
  import { extractLocalPredefinedStates } from "../states/index.js";
2
2
  import { hasPipelineCacheEntry, renderStyles } from "../pipeline/index.js";
3
3
  import { CHUNK_NAMES } from "./definitions.js";
4
-
5
4
  //#region src/chunks/renderChunk.ts
6
5
  /**
7
6
  * Build a filtered styles object for a regular chunk.
@@ -54,7 +53,7 @@ function renderStylesForChunk(styles, chunkName, styleKeys, pipelineCacheKey) {
54
53
  if (pipelineCacheKey && hasPipelineCacheEntry(pipelineCacheKey)) return renderStyles(void 0, void 0, void 0, pipelineCacheKey);
55
54
  return renderStyles(chunkName === CHUNK_NAMES.SUBCOMPONENTS ? buildSubcomponentFilteredStyles(styles, styleKeys) : buildFilteredStyles(styles, styleKeys), void 0, void 0, pipelineCacheKey);
56
55
  }
57
-
58
56
  //#endregion
59
57
  export { renderStylesForChunk };
58
+
60
59
  //# sourceMappingURL=renderChunk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderChunk.js","names":[],"sources":["../../src/chunks/renderChunk.ts"],"sourcesContent":["/**\n * Chunk-specific style rendering.\n *\n * Renders styles for a specific chunk by filtering the styles object\n * to only include relevant keys before passing to renderStyles.\n */\n\nimport type { RenderResult } from '../pipeline';\nimport { hasPipelineCacheEntry, renderStyles } from '../pipeline';\nimport { extractLocalPredefinedStates } from '../states';\nimport type { Styles } from '../styles/types';\n\nimport { CHUNK_NAMES } from './definitions';\n\n/**\n * Build a filtered styles object for a regular chunk.\n */\nfunction buildFilteredStyles(styles: Styles, styleKeys: string[]): Styles {\n const localPredefinedStates = extractLocalPredefinedStates(styles);\n const filteredStyles: Styles = {};\n\n for (const [key, value] of Object.entries(localPredefinedStates)) {\n filteredStyles[key] = value;\n }\n\n for (const key of styleKeys) {\n const value = styles[key];\n if (value !== undefined) {\n filteredStyles[key] = value;\n }\n }\n\n return filteredStyles;\n}\n\n/**\n * Build a filtered styles object for the subcomponents chunk.\n */\nfunction buildSubcomponentFilteredStyles(\n styles: Styles,\n selectorKeys: string[],\n): Styles {\n const localPredefinedStates = extractLocalPredefinedStates(styles);\n const filteredStyles: Styles = {};\n\n for (const [key, value] of Object.entries(localPredefinedStates)) {\n filteredStyles[key] = value;\n }\n\n for (const key of selectorKeys) {\n const value = styles[key];\n if (value !== undefined) {\n filteredStyles[key] = value;\n }\n }\n\n if (styles.$ !== undefined) {\n filteredStyles.$ = styles.$;\n }\n\n return filteredStyles;\n}\n\n/**\n * Render styles for a specific chunk.\n *\n * On pipeline cache hit, avoids building the filtered styles object entirely.\n * Only constructs it on cache miss when the pipeline actually needs the styles.\n *\n * IMPORTANT: Local predefined states (e.g., '@mobile': '@media(w < 600px)')\n * are always included in the filtered styles, regardless of which chunk is\n * being rendered. This ensures that state references like '@mobile' in any\n * chunk can be properly resolved by the pipeline.\n *\n * @param styles - The full styles object\n * @param chunkName - Name of the chunk being rendered\n * @param styleKeys - Keys of styles belonging to this chunk\n * @returns RenderResult with rules for this chunk\n */\nexport function renderStylesForChunk(\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n pipelineCacheKey?: string,\n): RenderResult {\n if (styleKeys.length === 0) {\n return { rules: [], className: '' };\n }\n\n // Fast path: skip building filteredStyles when pipeline has a cached result\n if (pipelineCacheKey && hasPipelineCacheEntry(pipelineCacheKey)) {\n return renderStyles(undefined, undefined, undefined, pipelineCacheKey);\n }\n\n // Cache miss: build filtered styles and run pipeline\n const filteredStyles =\n chunkName === CHUNK_NAMES.SUBCOMPONENTS\n ? buildSubcomponentFilteredStyles(styles, styleKeys)\n : buildFilteredStyles(styles, styleKeys);\n\n return renderStyles(filteredStyles, undefined, undefined, pipelineCacheKey);\n}\n"],"mappings":";;;;;;;;AAiBA,SAAS,oBAAoB,QAAgB,WAA6B;CACxE,MAAM,wBAAwB,6BAA6B,OAAO;CAClE,MAAM,iBAAyB,EAAE;AAEjC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,sBAAsB,CAC9D,gBAAe,OAAO;AAGxB,MAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,OACZ,gBAAe,OAAO;;AAI1B,QAAO;;;;;AAMT,SAAS,gCACP,QACA,cACQ;CACR,MAAM,wBAAwB,6BAA6B,OAAO;CAClE,MAAM,iBAAyB,EAAE;AAEjC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,sBAAsB,CAC9D,gBAAe,OAAO;AAGxB,MAAK,MAAM,OAAO,cAAc;EAC9B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,OACZ,gBAAe,OAAO;;AAI1B,KAAI,OAAO,MAAM,OACf,gBAAe,IAAI,OAAO;AAG5B,QAAO;;;;;;;;;;;;;;;;;;AAmBT,SAAgB,qBACd,QACA,WACA,WACA,kBACc;AACd,KAAI,UAAU,WAAW,EACvB,QAAO;EAAE,OAAO,EAAE;EAAE,WAAW;EAAI;AAIrC,KAAI,oBAAoB,sBAAsB,iBAAiB,CAC7D,QAAO,aAAa,QAAW,QAAW,QAAW,iBAAiB;AASxE,QAAO,aAJL,cAAc,YAAY,gBACtB,gCAAgC,QAAQ,UAAU,GAClD,oBAAoB,QAAQ,UAAU,EAER,QAAW,QAAW,iBAAiB"}
1
+ {"version":3,"file":"renderChunk.js","names":[],"sources":["../../src/chunks/renderChunk.ts"],"sourcesContent":["/**\n * Chunk-specific style rendering.\n *\n * Renders styles for a specific chunk by filtering the styles object\n * to only include relevant keys before passing to renderStyles.\n */\n\nimport type { RenderResult } from '../pipeline';\nimport { hasPipelineCacheEntry, renderStyles } from '../pipeline';\nimport { extractLocalPredefinedStates } from '../states';\nimport type { Styles } from '../styles/types';\n\nimport { CHUNK_NAMES } from './definitions';\n\n/**\n * Build a filtered styles object for a regular chunk.\n */\nfunction buildFilteredStyles(styles: Styles, styleKeys: string[]): Styles {\n const localPredefinedStates = extractLocalPredefinedStates(styles);\n const filteredStyles: Styles = {};\n\n for (const [key, value] of Object.entries(localPredefinedStates)) {\n filteredStyles[key] = value;\n }\n\n for (const key of styleKeys) {\n const value = styles[key];\n if (value !== undefined) {\n filteredStyles[key] = value;\n }\n }\n\n return filteredStyles;\n}\n\n/**\n * Build a filtered styles object for the subcomponents chunk.\n */\nfunction buildSubcomponentFilteredStyles(\n styles: Styles,\n selectorKeys: string[],\n): Styles {\n const localPredefinedStates = extractLocalPredefinedStates(styles);\n const filteredStyles: Styles = {};\n\n for (const [key, value] of Object.entries(localPredefinedStates)) {\n filteredStyles[key] = value;\n }\n\n for (const key of selectorKeys) {\n const value = styles[key];\n if (value !== undefined) {\n filteredStyles[key] = value;\n }\n }\n\n if (styles.$ !== undefined) {\n filteredStyles.$ = styles.$;\n }\n\n return filteredStyles;\n}\n\n/**\n * Render styles for a specific chunk.\n *\n * On pipeline cache hit, avoids building the filtered styles object entirely.\n * Only constructs it on cache miss when the pipeline actually needs the styles.\n *\n * IMPORTANT: Local predefined states (e.g., '@mobile': '@media(w < 600px)')\n * are always included in the filtered styles, regardless of which chunk is\n * being rendered. This ensures that state references like '@mobile' in any\n * chunk can be properly resolved by the pipeline.\n *\n * @param styles - The full styles object\n * @param chunkName - Name of the chunk being rendered\n * @param styleKeys - Keys of styles belonging to this chunk\n * @returns RenderResult with rules for this chunk\n */\nexport function renderStylesForChunk(\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n pipelineCacheKey?: string,\n): RenderResult {\n if (styleKeys.length === 0) {\n return { rules: [], className: '' };\n }\n\n // Fast path: skip building filteredStyles when pipeline has a cached result\n if (pipelineCacheKey && hasPipelineCacheEntry(pipelineCacheKey)) {\n return renderStyles(undefined, undefined, undefined, pipelineCacheKey);\n }\n\n // Cache miss: build filtered styles and run pipeline\n const filteredStyles =\n chunkName === CHUNK_NAMES.SUBCOMPONENTS\n ? buildSubcomponentFilteredStyles(styles, styleKeys)\n : buildFilteredStyles(styles, styleKeys);\n\n return renderStyles(filteredStyles, undefined, undefined, pipelineCacheKey);\n}\n"],"mappings":";;;;;;;AAiBA,SAAS,oBAAoB,QAAgB,WAA6B;CACxE,MAAM,wBAAwB,6BAA6B,OAAO;CAClE,MAAM,iBAAyB,EAAE;AAEjC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,sBAAsB,CAC9D,gBAAe,OAAO;AAGxB,MAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,KAAA,EACZ,gBAAe,OAAO;;AAI1B,QAAO;;;;;AAMT,SAAS,gCACP,QACA,cACQ;CACR,MAAM,wBAAwB,6BAA6B,OAAO;CAClE,MAAM,iBAAyB,EAAE;AAEjC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,sBAAsB,CAC9D,gBAAe,OAAO;AAGxB,MAAK,MAAM,OAAO,cAAc;EAC9B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,KAAA,EACZ,gBAAe,OAAO;;AAI1B,KAAI,OAAO,MAAM,KAAA,EACf,gBAAe,IAAI,OAAO;AAG5B,QAAO;;;;;;;;;;;;;;;;;;AAmBT,SAAgB,qBACd,QACA,WACA,WACA,kBACc;AACd,KAAI,UAAU,WAAW,EACvB,QAAO;EAAE,OAAO,EAAE;EAAE,WAAW;EAAI;AAIrC,KAAI,oBAAoB,sBAAsB,iBAAiB,CAC7D,QAAO,aAAa,KAAA,GAAW,KAAA,GAAW,KAAA,GAAW,iBAAiB;AASxE,QAAO,aAJL,cAAc,YAAY,gBACtB,gCAAgC,QAAQ,UAAU,GAClD,oBAAoB,QAAQ,UAAU,EAER,KAAA,GAAW,KAAA,GAAW,iBAAiB"}