@tenphi/tasty 0.0.0-snapshot.05c1c22

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 (314) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +629 -0
  3. package/dist/_virtual/_rolldown/runtime.js +7 -0
  4. package/dist/chunks/cacheKey.d.ts +1 -0
  5. package/dist/chunks/cacheKey.js +77 -0
  6. package/dist/chunks/cacheKey.js.map +1 -0
  7. package/dist/chunks/definitions.d.ts +37 -0
  8. package/dist/chunks/definitions.js +258 -0
  9. package/dist/chunks/definitions.js.map +1 -0
  10. package/dist/chunks/index.d.ts +1 -0
  11. package/dist/chunks/renderChunk.d.ts +1 -0
  12. package/dist/chunks/renderChunk.js +59 -0
  13. package/dist/chunks/renderChunk.js.map +1 -0
  14. package/dist/config.d.ts +366 -0
  15. package/dist/config.js +503 -0
  16. package/dist/config.js.map +1 -0
  17. package/dist/core/index.d.ts +33 -0
  18. package/dist/core/index.js +26 -0
  19. package/dist/counter-style/index.js +51 -0
  20. package/dist/counter-style/index.js.map +1 -0
  21. package/dist/debug.d.ts +89 -0
  22. package/dist/debug.js +453 -0
  23. package/dist/debug.js.map +1 -0
  24. package/dist/font-face/index.js +63 -0
  25. package/dist/font-face/index.js.map +1 -0
  26. package/dist/hooks/index.d.ts +7 -0
  27. package/dist/hooks/resolve-ssr-collector.js +14 -0
  28. package/dist/hooks/resolve-ssr-collector.js.map +1 -0
  29. package/dist/hooks/useCounterStyle.d.ts +50 -0
  30. package/dist/hooks/useCounterStyle.js +46 -0
  31. package/dist/hooks/useCounterStyle.js.map +1 -0
  32. package/dist/hooks/useFontFace.d.ts +43 -0
  33. package/dist/hooks/useFontFace.js +70 -0
  34. package/dist/hooks/useFontFace.js.map +1 -0
  35. package/dist/hooks/useGlobalStyles.d.ts +30 -0
  36. package/dist/hooks/useGlobalStyles.js +78 -0
  37. package/dist/hooks/useGlobalStyles.js.map +1 -0
  38. package/dist/hooks/useKeyframes.d.ts +56 -0
  39. package/dist/hooks/useKeyframes.js +64 -0
  40. package/dist/hooks/useKeyframes.js.map +1 -0
  41. package/dist/hooks/useProperty.d.ts +79 -0
  42. package/dist/hooks/useProperty.js +109 -0
  43. package/dist/hooks/useProperty.js.map +1 -0
  44. package/dist/hooks/useRawCSS.d.ts +53 -0
  45. package/dist/hooks/useRawCSS.js +35 -0
  46. package/dist/hooks/useRawCSS.js.map +1 -0
  47. package/dist/hooks/useStyles.d.ts +45 -0
  48. package/dist/hooks/useStyles.js +237 -0
  49. package/dist/hooks/useStyles.js.map +1 -0
  50. package/dist/index.d.ts +50 -0
  51. package/dist/index.js +35 -0
  52. package/dist/injector/index.d.ts +183 -0
  53. package/dist/injector/index.js +179 -0
  54. package/dist/injector/index.js.map +1 -0
  55. package/dist/injector/injector.d.ts +166 -0
  56. package/dist/injector/injector.js +464 -0
  57. package/dist/injector/injector.js.map +1 -0
  58. package/dist/injector/sheet-manager.d.ts +136 -0
  59. package/dist/injector/sheet-manager.js +733 -0
  60. package/dist/injector/sheet-manager.js.map +1 -0
  61. package/dist/injector/types.d.ts +204 -0
  62. package/dist/keyframes/index.js +206 -0
  63. package/dist/keyframes/index.js.map +1 -0
  64. package/dist/parser/classify.js +319 -0
  65. package/dist/parser/classify.js.map +1 -0
  66. package/dist/parser/const.js +49 -0
  67. package/dist/parser/const.js.map +1 -0
  68. package/dist/parser/lru.js +109 -0
  69. package/dist/parser/lru.js.map +1 -0
  70. package/dist/parser/parser.d.ts +25 -0
  71. package/dist/parser/parser.js +115 -0
  72. package/dist/parser/parser.js.map +1 -0
  73. package/dist/parser/tokenizer.js +69 -0
  74. package/dist/parser/tokenizer.js.map +1 -0
  75. package/dist/parser/types.d.ts +51 -0
  76. package/dist/parser/types.js +46 -0
  77. package/dist/parser/types.js.map +1 -0
  78. package/dist/pipeline/conditions.d.ts +134 -0
  79. package/dist/pipeline/conditions.js +406 -0
  80. package/dist/pipeline/conditions.js.map +1 -0
  81. package/dist/pipeline/exclusive.js +230 -0
  82. package/dist/pipeline/exclusive.js.map +1 -0
  83. package/dist/pipeline/index.d.ts +55 -0
  84. package/dist/pipeline/index.js +708 -0
  85. package/dist/pipeline/index.js.map +1 -0
  86. package/dist/pipeline/materialize.js +1103 -0
  87. package/dist/pipeline/materialize.js.map +1 -0
  88. package/dist/pipeline/parseStateKey.d.ts +15 -0
  89. package/dist/pipeline/parseStateKey.js +446 -0
  90. package/dist/pipeline/parseStateKey.js.map +1 -0
  91. package/dist/pipeline/simplify.js +515 -0
  92. package/dist/pipeline/simplify.js.map +1 -0
  93. package/dist/pipeline/warnings.js +18 -0
  94. package/dist/pipeline/warnings.js.map +1 -0
  95. package/dist/plugins/index.d.ts +2 -0
  96. package/dist/plugins/okhsl-plugin.d.ts +35 -0
  97. package/dist/plugins/okhsl-plugin.js +97 -0
  98. package/dist/plugins/okhsl-plugin.js.map +1 -0
  99. package/dist/plugins/types.d.ts +76 -0
  100. package/dist/properties/index.js +222 -0
  101. package/dist/properties/index.js.map +1 -0
  102. package/dist/properties/property-type-resolver.d.ts +24 -0
  103. package/dist/properties/property-type-resolver.js +90 -0
  104. package/dist/properties/property-type-resolver.js.map +1 -0
  105. package/dist/ssr/astro.d.ts +29 -0
  106. package/dist/ssr/astro.js +64 -0
  107. package/dist/ssr/astro.js.map +1 -0
  108. package/dist/ssr/async-storage.d.ts +17 -0
  109. package/dist/ssr/async-storage.js +34 -0
  110. package/dist/ssr/async-storage.js.map +1 -0
  111. package/dist/ssr/collect-auto-properties.js +39 -0
  112. package/dist/ssr/collect-auto-properties.js.map +1 -0
  113. package/dist/ssr/collector.d.ts +102 -0
  114. package/dist/ssr/collector.js +226 -0
  115. package/dist/ssr/collector.js.map +1 -0
  116. package/dist/ssr/context.d.ts +8 -0
  117. package/dist/ssr/context.js +13 -0
  118. package/dist/ssr/context.js.map +1 -0
  119. package/dist/ssr/format-global-rules.js +22 -0
  120. package/dist/ssr/format-global-rules.js.map +1 -0
  121. package/dist/ssr/format-keyframes.js +69 -0
  122. package/dist/ssr/format-keyframes.js.map +1 -0
  123. package/dist/ssr/format-property.js +49 -0
  124. package/dist/ssr/format-property.js.map +1 -0
  125. package/dist/ssr/format-rules.js +73 -0
  126. package/dist/ssr/format-rules.js.map +1 -0
  127. package/dist/ssr/hydrate.d.ts +22 -0
  128. package/dist/ssr/hydrate.js +49 -0
  129. package/dist/ssr/hydrate.js.map +1 -0
  130. package/dist/ssr/index.d.ts +5 -0
  131. package/dist/ssr/index.js +11 -0
  132. package/dist/ssr/index.js.map +1 -0
  133. package/dist/ssr/next.d.ts +45 -0
  134. package/dist/ssr/next.js +69 -0
  135. package/dist/ssr/next.js.map +1 -0
  136. package/dist/ssr/ssr-collector-ref.js +12 -0
  137. package/dist/ssr/ssr-collector-ref.js.map +1 -0
  138. package/dist/states/index.d.ts +49 -0
  139. package/dist/states/index.js +170 -0
  140. package/dist/states/index.js.map +1 -0
  141. package/dist/static/index.d.ts +5 -0
  142. package/dist/static/index.js +4 -0
  143. package/dist/static/inject.d.ts +5 -0
  144. package/dist/static/inject.js +17 -0
  145. package/dist/static/inject.js.map +1 -0
  146. package/dist/static/tastyStatic.d.ts +46 -0
  147. package/dist/static/tastyStatic.js +30 -0
  148. package/dist/static/tastyStatic.js.map +1 -0
  149. package/dist/static/types.d.ts +49 -0
  150. package/dist/static/types.js +24 -0
  151. package/dist/static/types.js.map +1 -0
  152. package/dist/styles/align.d.ts +15 -0
  153. package/dist/styles/align.js +14 -0
  154. package/dist/styles/align.js.map +1 -0
  155. package/dist/styles/border.d.ts +25 -0
  156. package/dist/styles/border.js +113 -0
  157. package/dist/styles/border.js.map +1 -0
  158. package/dist/styles/color.d.ts +14 -0
  159. package/dist/styles/color.js +26 -0
  160. package/dist/styles/color.js.map +1 -0
  161. package/dist/styles/createStyle.js +79 -0
  162. package/dist/styles/createStyle.js.map +1 -0
  163. package/dist/styles/dimension.js +96 -0
  164. package/dist/styles/dimension.js.map +1 -0
  165. package/dist/styles/display.d.ts +37 -0
  166. package/dist/styles/display.js +66 -0
  167. package/dist/styles/display.js.map +1 -0
  168. package/dist/styles/fade.d.ts +15 -0
  169. package/dist/styles/fade.js +57 -0
  170. package/dist/styles/fade.js.map +1 -0
  171. package/dist/styles/fill.d.ts +42 -0
  172. package/dist/styles/fill.js +51 -0
  173. package/dist/styles/fill.js.map +1 -0
  174. package/dist/styles/flow.d.ts +16 -0
  175. package/dist/styles/flow.js +12 -0
  176. package/dist/styles/flow.js.map +1 -0
  177. package/dist/styles/gap.d.ts +31 -0
  178. package/dist/styles/gap.js +36 -0
  179. package/dist/styles/gap.js.map +1 -0
  180. package/dist/styles/height.d.ts +17 -0
  181. package/dist/styles/height.js +19 -0
  182. package/dist/styles/height.js.map +1 -0
  183. package/dist/styles/index.d.ts +1 -0
  184. package/dist/styles/index.js +8 -0
  185. package/dist/styles/index.js.map +1 -0
  186. package/dist/styles/inset.d.ts +52 -0
  187. package/dist/styles/inset.js +149 -0
  188. package/dist/styles/inset.js.map +1 -0
  189. package/dist/styles/justify.d.ts +15 -0
  190. package/dist/styles/justify.js +14 -0
  191. package/dist/styles/justify.js.map +1 -0
  192. package/dist/styles/list.d.ts +16 -0
  193. package/dist/styles/list.js +98 -0
  194. package/dist/styles/list.js.map +1 -0
  195. package/dist/styles/margin.d.ts +24 -0
  196. package/dist/styles/margin.js +103 -0
  197. package/dist/styles/margin.js.map +1 -0
  198. package/dist/styles/outline.d.ts +29 -0
  199. package/dist/styles/outline.js +64 -0
  200. package/dist/styles/outline.js.map +1 -0
  201. package/dist/styles/padding.d.ts +24 -0
  202. package/dist/styles/padding.js +103 -0
  203. package/dist/styles/padding.js.map +1 -0
  204. package/dist/styles/predefined.d.ts +71 -0
  205. package/dist/styles/predefined.js +237 -0
  206. package/dist/styles/predefined.js.map +1 -0
  207. package/dist/styles/preset.d.ts +52 -0
  208. package/dist/styles/preset.js +126 -0
  209. package/dist/styles/preset.js.map +1 -0
  210. package/dist/styles/radius.d.ts +12 -0
  211. package/dist/styles/radius.js +71 -0
  212. package/dist/styles/radius.js.map +1 -0
  213. package/dist/styles/scrollbar.d.ts +25 -0
  214. package/dist/styles/scrollbar.js +46 -0
  215. package/dist/styles/scrollbar.js.map +1 -0
  216. package/dist/styles/shadow.d.ts +14 -0
  217. package/dist/styles/shadow.js +23 -0
  218. package/dist/styles/shadow.js.map +1 -0
  219. package/dist/styles/transition.d.ts +14 -0
  220. package/dist/styles/transition.js +157 -0
  221. package/dist/styles/transition.js.map +1 -0
  222. package/dist/styles/types.d.ts +549 -0
  223. package/dist/styles/width.d.ts +17 -0
  224. package/dist/styles/width.js +19 -0
  225. package/dist/styles/width.js.map +1 -0
  226. package/dist/tasty.d.ts +119 -0
  227. package/dist/tasty.js +231 -0
  228. package/dist/tasty.js.map +1 -0
  229. package/dist/types.d.ts +184 -0
  230. package/dist/utils/cache-wrapper.js +21 -0
  231. package/dist/utils/cache-wrapper.js.map +1 -0
  232. package/dist/utils/case-converter.js +8 -0
  233. package/dist/utils/case-converter.js.map +1 -0
  234. package/dist/utils/color-math.d.ts +46 -0
  235. package/dist/utils/color-math.js +749 -0
  236. package/dist/utils/color-math.js.map +1 -0
  237. package/dist/utils/color-space.d.ts +5 -0
  238. package/dist/utils/color-space.js +228 -0
  239. package/dist/utils/color-space.js.map +1 -0
  240. package/dist/utils/colors.d.ts +5 -0
  241. package/dist/utils/colors.js +10 -0
  242. package/dist/utils/colors.js.map +1 -0
  243. package/dist/utils/css-types.d.ts +7 -0
  244. package/dist/utils/dotize.d.ts +26 -0
  245. package/dist/utils/dotize.js +122 -0
  246. package/dist/utils/dotize.js.map +1 -0
  247. package/dist/utils/filter-base-props.d.ts +15 -0
  248. package/dist/utils/filter-base-props.js +45 -0
  249. package/dist/utils/filter-base-props.js.map +1 -0
  250. package/dist/utils/get-display-name.d.ts +7 -0
  251. package/dist/utils/get-display-name.js +10 -0
  252. package/dist/utils/get-display-name.js.map +1 -0
  253. package/dist/utils/has-keys.js +13 -0
  254. package/dist/utils/has-keys.js.map +1 -0
  255. package/dist/utils/is-dev-env.js +19 -0
  256. package/dist/utils/is-dev-env.js.map +1 -0
  257. package/dist/utils/is-valid-element-type.js +15 -0
  258. package/dist/utils/is-valid-element-type.js.map +1 -0
  259. package/dist/utils/merge-styles.d.ts +7 -0
  260. package/dist/utils/merge-styles.js +145 -0
  261. package/dist/utils/merge-styles.js.map +1 -0
  262. package/dist/utils/mod-attrs.d.ts +6 -0
  263. package/dist/utils/mod-attrs.js +20 -0
  264. package/dist/utils/mod-attrs.js.map +1 -0
  265. package/dist/utils/process-tokens.d.ts +21 -0
  266. package/dist/utils/process-tokens.js +90 -0
  267. package/dist/utils/process-tokens.js.map +1 -0
  268. package/dist/utils/resolve-recipes.d.ts +17 -0
  269. package/dist/utils/resolve-recipes.js +146 -0
  270. package/dist/utils/resolve-recipes.js.map +1 -0
  271. package/dist/utils/selector-transform.js +32 -0
  272. package/dist/utils/selector-transform.js.map +1 -0
  273. package/dist/utils/string.js +8 -0
  274. package/dist/utils/string.js.map +1 -0
  275. package/dist/utils/styles.d.ts +99 -0
  276. package/dist/utils/styles.js +220 -0
  277. package/dist/utils/styles.js.map +1 -0
  278. package/dist/utils/typography.d.ts +47 -0
  279. package/dist/utils/typography.js +51 -0
  280. package/dist/utils/typography.js.map +1 -0
  281. package/dist/utils/warnings.d.ts +16 -0
  282. package/dist/utils/warnings.js +16 -0
  283. package/dist/utils/warnings.js.map +1 -0
  284. package/dist/zero/babel.d.ts +182 -0
  285. package/dist/zero/babel.js +438 -0
  286. package/dist/zero/babel.js.map +1 -0
  287. package/dist/zero/css-writer.d.ts +45 -0
  288. package/dist/zero/css-writer.js +73 -0
  289. package/dist/zero/css-writer.js.map +1 -0
  290. package/dist/zero/extractor.d.ts +24 -0
  291. package/dist/zero/extractor.js +266 -0
  292. package/dist/zero/extractor.js.map +1 -0
  293. package/dist/zero/index.d.ts +3 -0
  294. package/dist/zero/index.js +3 -0
  295. package/dist/zero/next.d.ts +86 -0
  296. package/dist/zero/next.js +143 -0
  297. package/dist/zero/next.js.map +1 -0
  298. package/docs/PIPELINE.md +519 -0
  299. package/docs/README.md +31 -0
  300. package/docs/adoption.md +296 -0
  301. package/docs/comparison.md +420 -0
  302. package/docs/configuration.md +326 -0
  303. package/docs/debug.md +318 -0
  304. package/docs/design-system.md +424 -0
  305. package/docs/dsl.md +673 -0
  306. package/docs/getting-started.md +217 -0
  307. package/docs/injector.md +528 -0
  308. package/docs/methodology.md +567 -0
  309. package/docs/runtime.md +485 -0
  310. package/docs/ssr.md +384 -0
  311. package/docs/styles.md +582 -0
  312. package/docs/tasty-static.md +520 -0
  313. package/package.json +215 -0
  314. package/tasty.config.ts +14 -0
@@ -0,0 +1,64 @@
1
+ import { keyframes } from "../injector/index.js";
2
+ import { resolveSSRCollector } from "./resolve-ssr-collector.js";
3
+ import { TastySSRContext } from "../ssr/context.js";
4
+ import { formatKeyframesCSS } from "../ssr/format-keyframes.js";
5
+ import { useContext, useInsertionEffect, useMemo, useRef } from "react";
6
+ //#region src/hooks/useKeyframes.ts
7
+ function useKeyframes(stepsOrFactory, depsOrOptions, options) {
8
+ const ssrCollector = resolveSSRCollector(useContext(TastySSRContext));
9
+ const isFactory = typeof stepsOrFactory === "function";
10
+ const deps = isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : void 0;
11
+ const opts = isFactory ? options : depsOrOptions;
12
+ const stepsData = useMemo(() => {
13
+ const steps = isFactory ? stepsOrFactory() : stepsOrFactory;
14
+ if (!steps || Object.keys(steps).length === 0) return null;
15
+ return steps;
16
+ }, isFactory ? deps ?? [] : [stepsOrFactory]);
17
+ const renderResultRef = useRef(null);
18
+ const effectResultRef = useRef(null);
19
+ const name = useMemo(() => {
20
+ if (!stepsData) return "";
21
+ if (ssrCollector) {
22
+ const actualName = ssrCollector.allocateKeyframeName(opts?.name);
23
+ const css = formatKeyframesCSS(actualName, stepsData);
24
+ ssrCollector.collectKeyframes(actualName, css);
25
+ return actualName;
26
+ }
27
+ renderResultRef.current?.dispose();
28
+ renderResultRef.current = null;
29
+ const result = keyframes(stepsData, {
30
+ name: opts?.name,
31
+ root: opts?.root
32
+ });
33
+ renderResultRef.current = result;
34
+ return result.toString();
35
+ }, [
36
+ stepsData,
37
+ opts?.name,
38
+ opts?.root,
39
+ ssrCollector
40
+ ]);
41
+ useInsertionEffect(() => {
42
+ effectResultRef.current?.dispose();
43
+ effectResultRef.current = null;
44
+ if (stepsData) effectResultRef.current = keyframes(stepsData, {
45
+ name: opts?.name,
46
+ root: opts?.root
47
+ });
48
+ return () => {
49
+ effectResultRef.current?.dispose();
50
+ effectResultRef.current = null;
51
+ renderResultRef.current?.dispose();
52
+ renderResultRef.current = null;
53
+ };
54
+ }, [
55
+ stepsData,
56
+ opts?.name,
57
+ opts?.root
58
+ ]);
59
+ return name;
60
+ }
61
+ //#endregion
62
+ export { useKeyframes };
63
+
64
+ //# sourceMappingURL=useKeyframes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useKeyframes.js","names":[],"sources":["../../src/hooks/useKeyframes.ts"],"sourcesContent":["import { useContext, useInsertionEffect, useMemo, useRef } from 'react';\n\nimport { keyframes } from '../injector';\nimport type { KeyframesResult, KeyframesSteps } from '../injector/types';\nimport { resolveSSRCollector } from './resolve-ssr-collector';\nimport { TastySSRContext } from '../ssr/context';\nimport { formatKeyframesCSS } from '../ssr/format-keyframes';\n\ninterface UseKeyframesOptions {\n name?: string;\n root?: Document | ShadowRoot;\n}\n\n/**\n * Hook to inject CSS @keyframes and return the generated animation name.\n * Handles keyframes injection with proper cleanup on unmount or dependency changes.\n *\n * @example Basic usage - steps object is the dependency\n * ```tsx\n * function MyComponent() {\n * const bounce = useKeyframes({\n * '0%': { transform: 'scale(1)' },\n * '50%': { transform: 'scale(1.1)' },\n * '100%': { transform: 'scale(1)' },\n * });\n *\n * return <div style={{ animation: `${bounce} 1s infinite` }}>Bouncing</div>;\n * }\n * ```\n *\n * @example With custom name\n * ```tsx\n * function MyComponent() {\n * const fadeIn = useKeyframes(\n * { from: { opacity: 0 }, to: { opacity: 1 } },\n * { name: 'fadeIn' }\n * );\n *\n * return <div style={{ animation: `${fadeIn} 0.3s ease-out` }}>Fading in</div>;\n * }\n * ```\n *\n * @example Factory function with dependencies\n * ```tsx\n * function MyComponent({ scale }: { scale: number }) {\n * const pulse = useKeyframes(\n * () => ({\n * '0%': { transform: 'scale(1)' },\n * '100%': { transform: `scale(${scale})` },\n * }),\n * [scale]\n * );\n *\n * return <div style={{ animation: `${pulse} 1s infinite` }}>Pulsing</div>;\n * }\n * ```\n */\n\n// Overload 1: Static steps object\nexport function useKeyframes(\n steps: KeyframesSteps,\n options?: UseKeyframesOptions,\n): string;\n\n// Overload 2: Factory function with dependencies\nexport function useKeyframes(\n factory: () => KeyframesSteps,\n deps: readonly unknown[],\n options?: UseKeyframesOptions,\n): string;\n\n// Implementation\nexport function useKeyframes(\n stepsOrFactory: KeyframesSteps | (() => KeyframesSteps),\n depsOrOptions?: readonly unknown[] | UseKeyframesOptions,\n options?: UseKeyframesOptions,\n): string {\n const ssrContextValue = useContext(TastySSRContext);\n const ssrCollector = resolveSSRCollector(ssrContextValue);\n\n // Detect which overload is being used\n const isFactory = typeof stepsOrFactory === 'function';\n\n // Parse arguments based on overload\n const deps =\n isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : undefined;\n const opts = isFactory\n ? options\n : (depsOrOptions as UseKeyframesOptions | undefined);\n\n // Memoize the keyframes steps to get a stable reference\n const stepsData = useMemo(\n () => {\n const steps = isFactory\n ? (stepsOrFactory as () => KeyframesSteps)()\n : (stepsOrFactory as KeyframesSteps);\n\n if (!steps || Object.keys(steps).length === 0) {\n return null;\n }\n\n return steps;\n },\n\n isFactory ? (deps ?? []) : [stepsOrFactory],\n );\n\n // Store keyframes results for cleanup (client only)\n const renderResultRef = useRef<KeyframesResult | null>(null);\n const effectResultRef = useRef<KeyframesResult | null>(null);\n\n const name = useMemo(() => {\n if (!stepsData) {\n return '';\n }\n\n // SSR path: format and collect, return name without DOM injection\n if (ssrCollector) {\n const actualName = ssrCollector.allocateKeyframeName(opts?.name);\n const css = formatKeyframesCSS(actualName, stepsData);\n ssrCollector.collectKeyframes(actualName, css);\n return actualName;\n }\n\n // Client path: inject keyframes synchronously for immediate name availability\n renderResultRef.current?.dispose();\n renderResultRef.current = null;\n\n const result = keyframes(stepsData, {\n name: opts?.name,\n root: opts?.root,\n });\n\n renderResultRef.current = result;\n\n return result.toString();\n }, [stepsData, opts?.name, opts?.root, ssrCollector]);\n\n // Client path: handle Strict Mode double-invocation and cleanup\n useInsertionEffect(() => {\n effectResultRef.current?.dispose();\n effectResultRef.current = null;\n\n if (stepsData) {\n const result = keyframes(stepsData, {\n name: opts?.name,\n root: opts?.root,\n });\n effectResultRef.current = result;\n }\n\n return () => {\n effectResultRef.current?.dispose();\n effectResultRef.current = null;\n renderResultRef.current?.dispose();\n renderResultRef.current = null;\n };\n }, [stepsData, opts?.name, opts?.root]);\n\n return name;\n}\n"],"mappings":";;;;;;AAwEA,SAAgB,aACd,gBACA,eACA,SACQ;CAER,MAAM,eAAe,oBADG,WAAW,gBAAgB,CACM;CAGzD,MAAM,YAAY,OAAO,mBAAmB;CAG5C,MAAM,OACJ,aAAa,MAAM,QAAQ,cAAc,GAAG,gBAAgB,KAAA;CAC9D,MAAM,OAAO,YACT,UACC;CAGL,MAAM,YAAY,cACV;EACJ,MAAM,QAAQ,YACT,gBAAyC,GACzC;AAEL,MAAI,CAAC,SAAS,OAAO,KAAK,MAAM,CAAC,WAAW,EAC1C,QAAO;AAGT,SAAO;IAGT,YAAa,QAAQ,EAAE,GAAI,CAAC,eAAe,CAC5C;CAGD,MAAM,kBAAkB,OAA+B,KAAK;CAC5D,MAAM,kBAAkB,OAA+B,KAAK;CAE5D,MAAM,OAAO,cAAc;AACzB,MAAI,CAAC,UACH,QAAO;AAIT,MAAI,cAAc;GAChB,MAAM,aAAa,aAAa,qBAAqB,MAAM,KAAK;GAChE,MAAM,MAAM,mBAAmB,YAAY,UAAU;AACrD,gBAAa,iBAAiB,YAAY,IAAI;AAC9C,UAAO;;AAIT,kBAAgB,SAAS,SAAS;AAClC,kBAAgB,UAAU;EAE1B,MAAM,SAAS,UAAU,WAAW;GAClC,MAAM,MAAM;GACZ,MAAM,MAAM;GACb,CAAC;AAEF,kBAAgB,UAAU;AAE1B,SAAO,OAAO,UAAU;IACvB;EAAC;EAAW,MAAM;EAAM,MAAM;EAAM;EAAa,CAAC;AAGrD,0BAAyB;AACvB,kBAAgB,SAAS,SAAS;AAClC,kBAAgB,UAAU;AAE1B,MAAI,UAKF,iBAAgB,UAJD,UAAU,WAAW;GAClC,MAAM,MAAM;GACZ,MAAM,MAAM;GACb,CAAC;AAIJ,eAAa;AACX,mBAAgB,SAAS,SAAS;AAClC,mBAAgB,UAAU;AAC1B,mBAAgB,SAAS,SAAS;AAClC,mBAAgB,UAAU;;IAE3B;EAAC;EAAW,MAAM;EAAM,MAAM;EAAK,CAAC;AAEvC,QAAO"}
@@ -0,0 +1,79 @@
1
+ //#region src/hooks/useProperty.d.ts
2
+ interface UsePropertyOptions {
3
+ /**
4
+ * CSS syntax string for the property (e.g., '<color>', '<length>', '<angle>').
5
+ * For color tokens (#name), this is auto-set to '<color>' and cannot be overridden.
6
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@property/syntax
7
+ */
8
+ syntax?: string;
9
+ /**
10
+ * Whether the property inherits from parent elements
11
+ * @default true
12
+ */
13
+ inherits?: boolean;
14
+ /**
15
+ * Initial value for the property.
16
+ * For color tokens (#name), this defaults to 'transparent' if not specified.
17
+ */
18
+ initialValue?: string | number;
19
+ /**
20
+ * Shadow root or document to inject into
21
+ */
22
+ root?: Document | ShadowRoot;
23
+ }
24
+ /**
25
+ * Hook to register a CSS @property custom property.
26
+ * This enables advanced features like animating custom properties.
27
+ *
28
+ * Note: @property rules are global and persistent once defined.
29
+ * The hook ensures the property is only registered once per root.
30
+ *
31
+ * Accepts tasty token syntax for the property name:
32
+ * - `$name` → defines `--name`
33
+ * - `#name` → defines `--name-color` (auto-sets syntax: '<color>', defaults initialValue: 'transparent')
34
+ * - `--name` → defines `--name` (legacy format)
35
+ *
36
+ * @param name - The property token ($name, #name) or CSS property name (--name)
37
+ * @param options - Property configuration
38
+ *
39
+ * @example Basic property with token syntax
40
+ * ```tsx
41
+ * function Spinner() {
42
+ * useProperty('$rotation', {
43
+ * syntax: '<angle>',
44
+ * inherits: false,
45
+ * initialValue: '0deg',
46
+ * });
47
+ *
48
+ * return <div className="spinner" />;
49
+ * }
50
+ * ```
51
+ *
52
+ * @example Color property with token syntax (auto-sets syntax)
53
+ * ```tsx
54
+ * function MyComponent() {
55
+ * useProperty('#theme', {
56
+ * initialValue: 'red', // syntax: '<color>' is auto-set
57
+ * });
58
+ *
59
+ * // Now --theme-color can be animated with CSS transitions
60
+ * return <div style={{ '--theme-color': 'blue' } as React.CSSProperties}>Colored</div>;
61
+ * }
62
+ * ```
63
+ *
64
+ * @example Legacy format (still supported)
65
+ * ```tsx
66
+ * function ResizableBox() {
67
+ * useProperty('--box-size', {
68
+ * syntax: '<length>',
69
+ * initialValue: '100px',
70
+ * });
71
+ *
72
+ * return <div style={{ width: 'var(--box-size)' }} />;
73
+ * }
74
+ * ```
75
+ */
76
+ declare function useProperty(name: string, options?: UsePropertyOptions): void;
77
+ //#endregion
78
+ export { UsePropertyOptions, useProperty };
79
+ //# sourceMappingURL=useProperty.d.ts.map
@@ -0,0 +1,109 @@
1
+ import { getGlobalInjector } from "../config.js";
2
+ import { formatPropertyCSS } from "../ssr/format-property.js";
3
+ import { resolveSSRCollector } from "./resolve-ssr-collector.js";
4
+ import { TastySSRContext } from "../ssr/context.js";
5
+ import { useContext, useInsertionEffect, useMemo } from "react";
6
+ //#region src/hooks/useProperty.ts
7
+ /**
8
+ * Hook to register a CSS @property custom property.
9
+ * This enables advanced features like animating custom properties.
10
+ *
11
+ * Note: @property rules are global and persistent once defined.
12
+ * The hook ensures the property is only registered once per root.
13
+ *
14
+ * Accepts tasty token syntax for the property name:
15
+ * - `$name` → defines `--name`
16
+ * - `#name` → defines `--name-color` (auto-sets syntax: '<color>', defaults initialValue: 'transparent')
17
+ * - `--name` → defines `--name` (legacy format)
18
+ *
19
+ * @param name - The property token ($name, #name) or CSS property name (--name)
20
+ * @param options - Property configuration
21
+ *
22
+ * @example Basic property with token syntax
23
+ * ```tsx
24
+ * function Spinner() {
25
+ * useProperty('$rotation', {
26
+ * syntax: '<angle>',
27
+ * inherits: false,
28
+ * initialValue: '0deg',
29
+ * });
30
+ *
31
+ * return <div className="spinner" />;
32
+ * }
33
+ * ```
34
+ *
35
+ * @example Color property with token syntax (auto-sets syntax)
36
+ * ```tsx
37
+ * function MyComponent() {
38
+ * useProperty('#theme', {
39
+ * initialValue: 'red', // syntax: '<color>' is auto-set
40
+ * });
41
+ *
42
+ * // Now --theme-color can be animated with CSS transitions
43
+ * return <div style={{ '--theme-color': 'blue' } as React.CSSProperties}>Colored</div>;
44
+ * }
45
+ * ```
46
+ *
47
+ * @example Legacy format (still supported)
48
+ * ```tsx
49
+ * function ResizableBox() {
50
+ * useProperty('--box-size', {
51
+ * syntax: '<length>',
52
+ * initialValue: '100px',
53
+ * });
54
+ *
55
+ * return <div style={{ width: 'var(--box-size)' }} />;
56
+ * }
57
+ * ```
58
+ */
59
+ function useProperty(name, options) {
60
+ const ssrCollector = resolveSSRCollector(useContext(TastySSRContext));
61
+ const optionsKey = useMemo(() => {
62
+ if (!options) return "";
63
+ return JSON.stringify({
64
+ syntax: options.syntax,
65
+ inherits: options.inherits,
66
+ initialValue: options.initialValue
67
+ });
68
+ }, [
69
+ options?.syntax,
70
+ options?.inherits,
71
+ options?.initialValue
72
+ ]);
73
+ useMemo(() => {
74
+ if (!ssrCollector || !name) return;
75
+ ssrCollector.collectInternals();
76
+ const css = formatPropertyCSS(name, {
77
+ syntax: options?.syntax,
78
+ inherits: options?.inherits,
79
+ initialValue: options?.initialValue
80
+ });
81
+ if (css) ssrCollector.collectProperty(name, css);
82
+ }, [
83
+ ssrCollector,
84
+ name,
85
+ optionsKey
86
+ ]);
87
+ useInsertionEffect(() => {
88
+ if (!name) {
89
+ console.warn(`[Tasty] useProperty: property name is required`);
90
+ return;
91
+ }
92
+ const injector = getGlobalInjector();
93
+ if (injector.isPropertyDefined(name, { root: options?.root })) return;
94
+ injector.property(name, {
95
+ syntax: options?.syntax,
96
+ inherits: options?.inherits,
97
+ initialValue: options?.initialValue,
98
+ root: options?.root
99
+ });
100
+ }, [
101
+ name,
102
+ optionsKey,
103
+ options?.root
104
+ ]);
105
+ }
106
+ //#endregion
107
+ export { useProperty };
108
+
109
+ //# sourceMappingURL=useProperty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useProperty.js","names":[],"sources":["../../src/hooks/useProperty.ts"],"sourcesContent":["import { useContext, useInsertionEffect, useMemo } from 'react';\n\nimport { getGlobalInjector } from '../config';\nimport { resolveSSRCollector } from './resolve-ssr-collector';\nimport { TastySSRContext } from '../ssr/context';\nimport { formatPropertyCSS } from '../ssr/format-property';\n\nexport interface UsePropertyOptions {\n /**\n * CSS syntax string for the property (e.g., '<color>', '<length>', '<angle>').\n * For color tokens (#name), this is auto-set to '<color>' and cannot be overridden.\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@property/syntax\n */\n syntax?: string;\n /**\n * Whether the property inherits from parent elements\n * @default true\n */\n inherits?: boolean;\n /**\n * Initial value for the property.\n * For color tokens (#name), this defaults to 'transparent' if not specified.\n */\n initialValue?: string | number;\n /**\n * Shadow root or document to inject into\n */\n root?: Document | ShadowRoot;\n}\n\n/**\n * Hook to register a CSS @property custom property.\n * This enables advanced features like animating custom properties.\n *\n * Note: @property rules are global and persistent once defined.\n * The hook ensures the property is only registered once per root.\n *\n * Accepts tasty token syntax for the property name:\n * - `$name` → defines `--name`\n * - `#name` → defines `--name-color` (auto-sets syntax: '<color>', defaults initialValue: 'transparent')\n * - `--name` → defines `--name` (legacy format)\n *\n * @param name - The property token ($name, #name) or CSS property name (--name)\n * @param options - Property configuration\n *\n * @example Basic property with token syntax\n * ```tsx\n * function Spinner() {\n * useProperty('$rotation', {\n * syntax: '<angle>',\n * inherits: false,\n * initialValue: '0deg',\n * });\n *\n * return <div className=\"spinner\" />;\n * }\n * ```\n *\n * @example Color property with token syntax (auto-sets syntax)\n * ```tsx\n * function MyComponent() {\n * useProperty('#theme', {\n * initialValue: 'red', // syntax: '<color>' is auto-set\n * });\n *\n * // Now --theme-color can be animated with CSS transitions\n * return <div style={{ '--theme-color': 'blue' } as React.CSSProperties}>Colored</div>;\n * }\n * ```\n *\n * @example Legacy format (still supported)\n * ```tsx\n * function ResizableBox() {\n * useProperty('--box-size', {\n * syntax: '<length>',\n * initialValue: '100px',\n * });\n *\n * return <div style={{ width: 'var(--box-size)' }} />;\n * }\n * ```\n */\nexport function useProperty(name: string, options?: UsePropertyOptions): void {\n const ssrContextValue = useContext(TastySSRContext);\n const ssrCollector = resolveSSRCollector(ssrContextValue);\n\n // Memoize the options to create a stable dependency\n const optionsKey = useMemo(() => {\n if (!options) return '';\n return JSON.stringify({\n syntax: options.syntax,\n inherits: options.inherits,\n initialValue: options.initialValue,\n });\n }, [options?.syntax, options?.inherits, options?.initialValue]);\n\n // SSR path: collect @property CSS during render\n useMemo(() => {\n if (!ssrCollector || !name) return;\n\n ssrCollector.collectInternals();\n\n const css = formatPropertyCSS(name, {\n syntax: options?.syntax,\n inherits: options?.inherits,\n initialValue: options?.initialValue,\n });\n if (css) {\n ssrCollector.collectProperty(name, css);\n }\n }, [ssrCollector, name, optionsKey]);\n\n // Client path: inject via DOM\n useInsertionEffect(() => {\n if (!name) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(`[Tasty] useProperty: property name is required`);\n }\n return;\n }\n\n const injector = getGlobalInjector();\n\n if (injector.isPropertyDefined(name, { root: options?.root })) {\n return;\n }\n\n injector.property(name, {\n syntax: options?.syntax,\n inherits: options?.inherits,\n initialValue: options?.initialValue,\n root: options?.root,\n });\n }, [name, optionsKey, options?.root]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkFA,SAAgB,YAAY,MAAc,SAAoC;CAE5E,MAAM,eAAe,oBADG,WAAW,gBAAgB,CACM;CAGzD,MAAM,aAAa,cAAc;AAC/B,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,KAAK,UAAU;GACpB,QAAQ,QAAQ;GAChB,UAAU,QAAQ;GAClB,cAAc,QAAQ;GACvB,CAAC;IACD;EAAC,SAAS;EAAQ,SAAS;EAAU,SAAS;EAAa,CAAC;AAG/D,eAAc;AACZ,MAAI,CAAC,gBAAgB,CAAC,KAAM;AAE5B,eAAa,kBAAkB;EAE/B,MAAM,MAAM,kBAAkB,MAAM;GAClC,QAAQ,SAAS;GACjB,UAAU,SAAS;GACnB,cAAc,SAAS;GACxB,CAAC;AACF,MAAI,IACF,cAAa,gBAAgB,MAAM,IAAI;IAExC;EAAC;EAAc;EAAM;EAAW,CAAC;AAGpC,0BAAyB;AACvB,MAAI,CAAC,MAAM;AAEP,WAAQ,KAAK,iDAAiD;AAEhE;;EAGF,MAAM,WAAW,mBAAmB;AAEpC,MAAI,SAAS,kBAAkB,MAAM,EAAE,MAAM,SAAS,MAAM,CAAC,CAC3D;AAGF,WAAS,SAAS,MAAM;GACtB,QAAQ,SAAS;GACjB,UAAU,SAAS;GACnB,cAAc,SAAS;GACvB,MAAM,SAAS;GAChB,CAAC;IACD;EAAC;EAAM;EAAY,SAAS;EAAK,CAAC"}
@@ -0,0 +1,53 @@
1
+ //#region src/hooks/useRawCSS.d.ts
2
+ interface UseRawCSSOptions {
3
+ root?: Document | ShadowRoot;
4
+ }
5
+ /**
6
+ * Hook to inject raw CSS text directly without parsing.
7
+ * This is a low-overhead alternative for injecting global CSS that doesn't need tasty processing.
8
+ *
9
+ * The CSS is inserted into a separate style element (data-tasty-raw) to avoid conflicts
10
+ * with tasty's chunked style sheets.
11
+ *
12
+ * @example Static CSS string
13
+ * ```tsx
14
+ * function GlobalStyles() {
15
+ * useRawCSS(`
16
+ * body {
17
+ * margin: 0;
18
+ * padding: 0;
19
+ * font-family: sans-serif;
20
+ * }
21
+ * `);
22
+ *
23
+ * return null;
24
+ * }
25
+ * ```
26
+ *
27
+ * @example Factory function with dependencies (like useMemo)
28
+ * ```tsx
29
+ * function ThemeStyles({ theme }: { theme: 'light' | 'dark' }) {
30
+ * useRawCSS(() => `
31
+ * :root {
32
+ * --bg-color: ${theme === 'dark' ? '#1a1a1a' : '#ffffff'};
33
+ * --text-color: ${theme === 'dark' ? '#ffffff' : '#1a1a1a'};
34
+ * }
35
+ * `, [theme]);
36
+ *
37
+ * return null;
38
+ * }
39
+ * ```
40
+ *
41
+ * @example With options
42
+ * ```tsx
43
+ * function ShadowStyles({ shadowRoot }) {
44
+ * useRawCSS(() => `.scoped { color: red; }`, [], { root: shadowRoot });
45
+ * return null;
46
+ * }
47
+ * ```
48
+ */
49
+ declare function useRawCSS(css: string, options?: UseRawCSSOptions): void;
50
+ declare function useRawCSS(factory: () => string, deps: readonly unknown[], options?: UseRawCSSOptions): void;
51
+ //#endregion
52
+ export { useRawCSS };
53
+ //# sourceMappingURL=useRawCSS.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { injectRawCSS } from "../injector/index.js";
2
+ import { resolveSSRCollector } from "./resolve-ssr-collector.js";
3
+ import { TastySSRContext } from "../ssr/context.js";
4
+ import { useContext, useInsertionEffect, useMemo, useRef } from "react";
5
+ //#region src/hooks/useRawCSS.ts
6
+ function useRawCSS(cssOrFactory, depsOrOptions, options) {
7
+ const ssrCollector = resolveSSRCollector(useContext(TastySSRContext));
8
+ const isFactory = typeof cssOrFactory === "function";
9
+ const deps = isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : void 0;
10
+ const opts = isFactory ? options : depsOrOptions;
11
+ const css = useMemo(() => isFactory ? cssOrFactory() : cssOrFactory, isFactory ? deps ?? [] : [cssOrFactory]);
12
+ useMemo(() => {
13
+ if (!ssrCollector || !css.trim()) return;
14
+ const key = `raw:${css.length}:${css.slice(0, 64)}`;
15
+ ssrCollector.collectRawCSS(key, css);
16
+ }, [ssrCollector, css]);
17
+ const disposeRef = useRef(null);
18
+ useInsertionEffect(() => {
19
+ disposeRef.current?.();
20
+ if (!css.trim()) {
21
+ disposeRef.current = null;
22
+ return;
23
+ }
24
+ const { dispose } = injectRawCSS(css, opts);
25
+ disposeRef.current = dispose;
26
+ return () => {
27
+ disposeRef.current?.();
28
+ disposeRef.current = null;
29
+ };
30
+ }, [css, opts?.root]);
31
+ }
32
+ //#endregion
33
+ export { useRawCSS };
34
+
35
+ //# sourceMappingURL=useRawCSS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRawCSS.js","names":[],"sources":["../../src/hooks/useRawCSS.ts"],"sourcesContent":["import { useContext, useInsertionEffect, useMemo, useRef } from 'react';\n\nimport { injectRawCSS } from '../injector';\nimport { resolveSSRCollector } from './resolve-ssr-collector';\nimport { TastySSRContext } from '../ssr/context';\n\ninterface UseRawCSSOptions {\n root?: Document | ShadowRoot;\n}\n\n/**\n * Hook to inject raw CSS text directly without parsing.\n * This is a low-overhead alternative for injecting global CSS that doesn't need tasty processing.\n *\n * The CSS is inserted into a separate style element (data-tasty-raw) to avoid conflicts\n * with tasty's chunked style sheets.\n *\n * @example Static CSS string\n * ```tsx\n * function GlobalStyles() {\n * useRawCSS(`\n * body {\n * margin: 0;\n * padding: 0;\n * font-family: sans-serif;\n * }\n * `);\n *\n * return null;\n * }\n * ```\n *\n * @example Factory function with dependencies (like useMemo)\n * ```tsx\n * function ThemeStyles({ theme }: { theme: 'light' | 'dark' }) {\n * useRawCSS(() => `\n * :root {\n * --bg-color: ${theme === 'dark' ? '#1a1a1a' : '#ffffff'};\n * --text-color: ${theme === 'dark' ? '#ffffff' : '#1a1a1a'};\n * }\n * `, [theme]);\n *\n * return null;\n * }\n * ```\n *\n * @example With options\n * ```tsx\n * function ShadowStyles({ shadowRoot }) {\n * useRawCSS(() => `.scoped { color: red; }`, [], { root: shadowRoot });\n * return null;\n * }\n * ```\n */\n\n// Overload 1: Static CSS string\nexport function useRawCSS(css: string, options?: UseRawCSSOptions): void;\n\n// Overload 2: Factory function with dependencies\nexport function useRawCSS(\n factory: () => string,\n deps: readonly unknown[],\n options?: UseRawCSSOptions,\n): void;\n\n// Implementation\nexport function useRawCSS(\n cssOrFactory: string | (() => string),\n depsOrOptions?: readonly unknown[] | UseRawCSSOptions,\n options?: UseRawCSSOptions,\n): void {\n const ssrContextValue = useContext(TastySSRContext);\n const ssrCollector = resolveSSRCollector(ssrContextValue);\n\n // Detect which overload is being used\n const isFactory = typeof cssOrFactory === 'function';\n\n // Parse arguments based on overload\n const deps =\n isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : undefined;\n const opts = isFactory\n ? options\n : (depsOrOptions as UseRawCSSOptions | undefined);\n\n // Memoize CSS - for factory functions, use provided deps; for strings, use the string itself\n const css = useMemo(\n () =>\n isFactory ? (cssOrFactory as () => string)() : (cssOrFactory as string),\n\n isFactory ? (deps ?? []) : [cssOrFactory],\n );\n\n // SSR path: collect raw CSS during render\n useMemo(() => {\n if (!ssrCollector || !css.trim()) return;\n\n const key = `raw:${css.length}:${css.slice(0, 64)}`;\n ssrCollector.collectRawCSS(key, css);\n }, [ssrCollector, css]);\n\n const disposeRef = useRef<(() => void) | null>(null);\n\n // Client path: inject via DOM\n useInsertionEffect(() => {\n disposeRef.current?.();\n\n if (!css.trim()) {\n disposeRef.current = null;\n return;\n }\n\n const { dispose } = injectRawCSS(css, opts);\n disposeRef.current = dispose;\n\n return () => {\n disposeRef.current?.();\n disposeRef.current = null;\n };\n }, [css, opts?.root]);\n}\n"],"mappings":";;;;;AAkEA,SAAgB,UACd,cACA,eACA,SACM;CAEN,MAAM,eAAe,oBADG,WAAW,gBAAgB,CACM;CAGzD,MAAM,YAAY,OAAO,iBAAiB;CAG1C,MAAM,OACJ,aAAa,MAAM,QAAQ,cAAc,GAAG,gBAAgB,KAAA;CAC9D,MAAM,OAAO,YACT,UACC;CAGL,MAAM,MAAM,cAER,YAAa,cAA+B,GAAI,cAElD,YAAa,QAAQ,EAAE,GAAI,CAAC,aAAa,CAC1C;AAGD,eAAc;AACZ,MAAI,CAAC,gBAAgB,CAAC,IAAI,MAAM,CAAE;EAElC,MAAM,MAAM,OAAO,IAAI,OAAO,GAAG,IAAI,MAAM,GAAG,GAAG;AACjD,eAAa,cAAc,KAAK,IAAI;IACnC,CAAC,cAAc,IAAI,CAAC;CAEvB,MAAM,aAAa,OAA4B,KAAK;AAGpD,0BAAyB;AACvB,aAAW,WAAW;AAEtB,MAAI,CAAC,IAAI,MAAM,EAAE;AACf,cAAW,UAAU;AACrB;;EAGF,MAAM,EAAE,YAAY,aAAa,KAAK,KAAK;AAC3C,aAAW,UAAU;AAErB,eAAa;AACX,cAAW,WAAW;AACtB,cAAW,UAAU;;IAEtB,CAAC,KAAK,MAAM,KAAK,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { Styles } from "../styles/types.js";
2
+
3
+ //#region src/hooks/useStyles.d.ts
4
+ /**
5
+ * Tasty styles object to generate CSS classes for.
6
+ * Can be undefined or empty object for no styles.
7
+ */
8
+ type UseStylesOptions = Styles | undefined;
9
+ interface UseStylesResult {
10
+ /**
11
+ * Generated className(s) to apply to the element.
12
+ * Can be empty string if no styles are provided.
13
+ * With chunking enabled, may contain multiple space-separated class names.
14
+ */
15
+ className: string;
16
+ }
17
+ /**
18
+ * Hook to generate CSS classes from Tasty styles.
19
+ * Handles style rendering, className allocation, and CSS injection.
20
+ *
21
+ * SSR-aware: when a ServerStyleCollector is available (via React context
22
+ * or AsyncLocalStorage), CSS is collected during the render phase instead
23
+ * of being injected into the DOM. useInsertionEffect does not run on the
24
+ * server, so the collector path is the only active path during SSR.
25
+ *
26
+ * Uses chunking to split styles into logical groups for better caching
27
+ * and CSS reuse across components.
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * function MyComponent() {
32
+ * const { className } = useStyles({
33
+ * padding: '2x',
34
+ * fill: '#purple',
35
+ * radius: '1r',
36
+ * });
37
+ *
38
+ * return <div className={className}>Styled content</div>;
39
+ * }
40
+ * ```
41
+ */
42
+ declare function useStyles(styles: UseStylesOptions): UseStylesResult;
43
+ //#endregion
44
+ export { UseStylesOptions, UseStylesResult, useStyles };
45
+ //# sourceMappingURL=useStyles.d.ts.map
@@ -0,0 +1,237 @@
1
+ import { extractLocalProperties, hasLocalProperties } from "../properties/index.js";
2
+ import { stringifyStyles } from "../utils/styles.js";
3
+ import { extractLocalFontFace, fontFaceContentHash, formatFontFaceRule, hasLocalFontFace } from "../font-face/index.js";
4
+ import { extractLocalCounterStyle, formatCounterStyleRule, hasLocalCounterStyle } from "../counter-style/index.js";
5
+ import { getConfig, getGlobalKeyframes, hasGlobalKeyframes } from "../config.js";
6
+ import { categorizeStyleKeys } from "../chunks/definitions.js";
7
+ import { generateChunkCacheKey } from "../chunks/cacheKey.js";
8
+ import { renderStylesForChunk } from "../chunks/renderChunk.js";
9
+ import { allocateClassName, counterStyle, fontFace, inject, keyframes, property } from "../injector/index.js";
10
+ import { resolveRecipes } from "../utils/resolve-recipes.js";
11
+ import { extractAnimationNamesFromStyles, extractLocalKeyframes, filterUsedKeyframes, hasLocalKeyframes, mergeKeyframes, replaceAnimationNames } from "../keyframes/index.js";
12
+ import { formatPropertyCSS } from "../ssr/format-property.js";
13
+ import { collectAutoInferredProperties } from "../ssr/collect-auto-properties.js";
14
+ import { resolveSSRCollector } from "./resolve-ssr-collector.js";
15
+ import { TastySSRContext } from "../ssr/context.js";
16
+ import { formatKeyframesCSS } from "../ssr/format-keyframes.js";
17
+ import { hasKeys } from "../utils/has-keys.js";
18
+ import { useContext, useInsertionEffect, useMemo, useRef } from "react";
19
+ //#region src/hooks/useStyles.ts
20
+ /**
21
+ * Render, cache-key, and allocate a className for a single chunk.
22
+ * Returns a ProcessedChunk, or null if the chunk produces no CSS rules.
23
+ *
24
+ * Always runs the pipeline and calls allocateClassName. The inject()
25
+ * call in useInsertionEffect handles all edge cases: placeholders from
26
+ * abandoned concurrent renders, hydration hits (ruleIndex -2), and
27
+ * runtime cache hits (already injected). The pipeline's own LRU cache
28
+ * makes repeated calls for identical styles cheap.
29
+ */
30
+ function processChunk(styles, chunkName, styleKeys) {
31
+ if (styleKeys.length === 0) return null;
32
+ const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);
33
+ const renderResult = renderStylesForChunk(styles, chunkName, styleKeys, cacheKey);
34
+ if (renderResult.rules.length === 0) return null;
35
+ const { className } = allocateClassName(cacheKey);
36
+ return {
37
+ name: chunkName,
38
+ styleKeys,
39
+ cacheKey,
40
+ renderResult,
41
+ className
42
+ };
43
+ }
44
+ /**
45
+ * Get keyframes that are actually used in styles.
46
+ * Returns null if no keyframes are used (fast path for zero overhead).
47
+ *
48
+ * Optimization order:
49
+ * 1. Check if any keyframes are defined (local or global) - if not, return null
50
+ * 2. Extract animation names from styles - if none, return null
51
+ * 3. Merge and filter keyframes to only used ones
52
+ */
53
+ function getUsedKeyframes(styles) {
54
+ const hasLocal = hasLocalKeyframes(styles);
55
+ const hasGlobal = hasGlobalKeyframes();
56
+ if (!hasLocal && !hasGlobal) return null;
57
+ const usedNames = extractAnimationNamesFromStyles(styles);
58
+ if (usedNames.size === 0) return null;
59
+ return filterUsedKeyframes(mergeKeyframes(hasLocal ? extractLocalKeyframes(styles) : null, hasGlobal ? getGlobalKeyframes() : null), usedNames);
60
+ }
61
+ /**
62
+ * Process a chunk on the SSR path: allocate via collector, render, collect CSS.
63
+ * Returns null if the chunk produces no CSS rules.
64
+ */
65
+ function processChunkSSR(collector, styles, chunkName, styleKeys) {
66
+ if (styleKeys.length === 0) return null;
67
+ const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);
68
+ const { className, isNewAllocation } = collector.allocateClassName(cacheKey);
69
+ if (isNewAllocation) {
70
+ const renderResult = renderStylesForChunk(styles, chunkName, styleKeys);
71
+ if (renderResult.rules.length > 0) {
72
+ collector.collectChunk(cacheKey, className, renderResult.rules);
73
+ return {
74
+ name: chunkName,
75
+ styleKeys,
76
+ cacheKey,
77
+ renderResult,
78
+ className
79
+ };
80
+ }
81
+ return null;
82
+ }
83
+ return {
84
+ name: chunkName,
85
+ styleKeys,
86
+ cacheKey,
87
+ renderResult: { rules: [] },
88
+ className
89
+ };
90
+ }
91
+ /**
92
+ * Hook to generate CSS classes from Tasty styles.
93
+ * Handles style rendering, className allocation, and CSS injection.
94
+ *
95
+ * SSR-aware: when a ServerStyleCollector is available (via React context
96
+ * or AsyncLocalStorage), CSS is collected during the render phase instead
97
+ * of being injected into the DOM. useInsertionEffect does not run on the
98
+ * server, so the collector path is the only active path during SSR.
99
+ *
100
+ * Uses chunking to split styles into logical groups for better caching
101
+ * and CSS reuse across components.
102
+ *
103
+ * @example
104
+ * ```tsx
105
+ * function MyComponent() {
106
+ * const { className } = useStyles({
107
+ * padding: '2x',
108
+ * fill: '#purple',
109
+ * radius: '1r',
110
+ * });
111
+ *
112
+ * return <div className={className}>Styled content</div>;
113
+ * }
114
+ * ```
115
+ */
116
+ function useStyles(styles) {
117
+ const ssrCollector = resolveSSRCollector(useContext(TastySSRContext));
118
+ const disposeRef = useRef([]);
119
+ const stylesRef = useRef({
120
+ key: "",
121
+ styles: void 0
122
+ });
123
+ const resolvedStyles = useMemo(() => {
124
+ if (!styles) return styles;
125
+ return resolveRecipes(styles);
126
+ }, [styles]);
127
+ const styleKey = useMemo(() => {
128
+ if (!resolvedStyles || !hasKeys(resolvedStyles)) return "";
129
+ return stringifyStyles(resolvedStyles);
130
+ }, [resolvedStyles]);
131
+ if (stylesRef.current.key !== styleKey) stylesRef.current = {
132
+ key: styleKey,
133
+ styles: resolvedStyles
134
+ };
135
+ const processedChunks = useMemo(() => {
136
+ const currentStyles = stylesRef.current.styles;
137
+ if (!styleKey || !currentStyles) return [];
138
+ const chunkMap = categorizeStyleKeys(currentStyles);
139
+ const chunks = [];
140
+ if (ssrCollector) {
141
+ ssrCollector.collectInternals();
142
+ for (const [chunkName, chunkStyleKeys] of chunkMap) {
143
+ const chunk = processChunkSSR(ssrCollector, currentStyles, chunkName, chunkStyleKeys);
144
+ if (chunk) chunks.push(chunk);
145
+ }
146
+ const usedKeyframes = getUsedKeyframes(currentStyles);
147
+ if (usedKeyframes) for (const [name, steps] of Object.entries(usedKeyframes)) {
148
+ const css = formatKeyframesCSS(name, steps);
149
+ ssrCollector.collectKeyframes(name, css);
150
+ }
151
+ if (hasLocalProperties(currentStyles)) {
152
+ const localProperties = extractLocalProperties(currentStyles);
153
+ if (localProperties) for (const [token, definition] of Object.entries(localProperties)) {
154
+ const css = formatPropertyCSS(token, definition);
155
+ if (css) ssrCollector.collectProperty(token, css);
156
+ }
157
+ }
158
+ if (hasLocalFontFace(currentStyles)) {
159
+ const localFontFace = extractLocalFontFace(currentStyles);
160
+ if (localFontFace) for (const [family, input] of Object.entries(localFontFace)) {
161
+ const descriptors = Array.isArray(input) ? input : [input];
162
+ for (const desc of descriptors) {
163
+ const hash = fontFaceContentHash(family, desc);
164
+ const css = formatFontFaceRule(family, desc);
165
+ ssrCollector.collectFontFace(hash, css);
166
+ }
167
+ }
168
+ }
169
+ if (hasLocalCounterStyle(currentStyles)) {
170
+ const localCounterStyle = extractLocalCounterStyle(currentStyles);
171
+ if (localCounterStyle) for (const [name, descriptors] of Object.entries(localCounterStyle)) {
172
+ const css = formatCounterStyleRule(name, descriptors);
173
+ ssrCollector.collectCounterStyle(name, css);
174
+ }
175
+ }
176
+ if (getConfig().autoPropertyTypes !== false) {
177
+ const allRules = chunks.flatMap((c) => c.renderResult.rules);
178
+ if (allRules.length > 0) collectAutoInferredProperties(allRules, ssrCollector, currentStyles);
179
+ }
180
+ } else for (const [chunkName, chunkStyleKeys] of chunkMap) {
181
+ const chunk = processChunk(currentStyles, chunkName, chunkStyleKeys);
182
+ if (chunk) chunks.push(chunk);
183
+ }
184
+ return chunks;
185
+ }, [styleKey]);
186
+ useInsertionEffect(() => {
187
+ disposeRef.current.forEach((dispose) => dispose?.());
188
+ disposeRef.current = [];
189
+ if (processedChunks.length === 0) return;
190
+ const currentStyles = stylesRef.current.styles;
191
+ if (currentStyles && hasLocalProperties(currentStyles)) {
192
+ const localProperties = extractLocalProperties(currentStyles);
193
+ if (localProperties) for (const [token, definition] of Object.entries(localProperties)) property(token, definition);
194
+ }
195
+ if (currentStyles && hasLocalFontFace(currentStyles)) {
196
+ const localFontFace = extractLocalFontFace(currentStyles);
197
+ if (localFontFace) for (const [family, input] of Object.entries(localFontFace)) {
198
+ const descriptors = Array.isArray(input) ? input : [input];
199
+ for (const desc of descriptors) fontFace(family, desc);
200
+ }
201
+ }
202
+ if (currentStyles && hasLocalCounterStyle(currentStyles)) {
203
+ const localCounterStyle = extractLocalCounterStyle(currentStyles);
204
+ if (localCounterStyle) for (const [name, descriptors] of Object.entries(localCounterStyle)) counterStyle(name, descriptors);
205
+ }
206
+ const usedKeyframes = currentStyles ? getUsedKeyframes(currentStyles) : null;
207
+ let nameMap = null;
208
+ if (usedKeyframes) {
209
+ nameMap = /* @__PURE__ */ new Map();
210
+ for (const [name, steps] of Object.entries(usedKeyframes)) {
211
+ const result = keyframes(steps, { name });
212
+ const injectedName = result.toString();
213
+ if (injectedName !== name) nameMap.set(name, injectedName);
214
+ disposeRef.current.push(result.dispose);
215
+ }
216
+ if (nameMap.size === 0) nameMap = null;
217
+ }
218
+ for (const chunk of processedChunks) if (chunk.renderResult.rules.length > 0) {
219
+ const { dispose } = inject(nameMap ? chunk.renderResult.rules.map((rule) => ({
220
+ ...rule,
221
+ declarations: replaceAnimationNames(rule.declarations, nameMap)
222
+ })) : chunk.renderResult.rules, { cacheKey: chunk.cacheKey });
223
+ disposeRef.current.push(dispose);
224
+ }
225
+ return () => {
226
+ disposeRef.current.forEach((dispose) => dispose?.());
227
+ disposeRef.current = [];
228
+ };
229
+ }, [processedChunks]);
230
+ return { className: useMemo(() => {
231
+ return processedChunks.map((chunk) => chunk.className).join(" ");
232
+ }, [processedChunks]) };
233
+ }
234
+ //#endregion
235
+ export { useStyles };
236
+
237
+ //# sourceMappingURL=useStyles.js.map