@yewdiel21/motion-kit 0.0.0-stage → 0.9.0

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 (391) hide show
  1. package/NOTICE +38 -0
  2. package/README.md +80 -2
  3. package/bin/motion-kit.mjs +242 -0
  4. package/css.d.ts +2 -0
  5. package/dist/auto/index.d.ts +63 -0
  6. package/dist/auto/index.js +207 -0
  7. package/dist/auto/index.js.map +1 -0
  8. package/dist/chunk-2BQGFOA7.js +177 -0
  9. package/dist/chunk-2BQGFOA7.js.map +1 -0
  10. package/dist/chunk-34XYOXHG.js +2374 -0
  11. package/dist/chunk-34XYOXHG.js.map +1 -0
  12. package/dist/chunk-6OJAP6ZL.js +832 -0
  13. package/dist/chunk-6OJAP6ZL.js.map +1 -0
  14. package/dist/chunk-ABNRDDO7.js +28 -0
  15. package/dist/chunk-ABNRDDO7.js.map +1 -0
  16. package/dist/chunk-FS2HJVVY.js +3320 -0
  17. package/dist/chunk-FS2HJVVY.js.map +1 -0
  18. package/dist/chunk-J27KH2A2.js +157 -0
  19. package/dist/chunk-J27KH2A2.js.map +1 -0
  20. package/dist/chunk-KLY5BRRI.js +18 -0
  21. package/dist/chunk-KLY5BRRI.js.map +1 -0
  22. package/dist/chunk-Q4LVFNLN.js +1617 -0
  23. package/dist/chunk-Q4LVFNLN.js.map +1 -0
  24. package/dist/chunk-RFDIZYFX.js +304 -0
  25. package/dist/chunk-RFDIZYFX.js.map +1 -0
  26. package/dist/chunk-TGV27ZWM.js +17 -0
  27. package/dist/chunk-TGV27ZWM.js.map +1 -0
  28. package/dist/confetti-BSX1CLLV.d.ts +41 -0
  29. package/dist/css/components/chat-background.css +87 -0
  30. package/dist/css/components/chat.css +237 -0
  31. package/dist/css/components/cursor.css +178 -0
  32. package/dist/css/components/dialog.css +259 -0
  33. package/dist/css/components/island.css +301 -0
  34. package/dist/css/components/menu.css +105 -0
  35. package/dist/css/components/nav.css +1054 -0
  36. package/dist/css/components/steps.css +50 -0
  37. package/dist/css/components/theme-picker.css +115 -0
  38. package/dist/css/components/toast.css +115 -0
  39. package/dist/css/controls.css +950 -0
  40. package/dist/css/effects.css +587 -0
  41. package/dist/css/fonts.css +3 -0
  42. package/dist/css/themes/black-green.css +126 -0
  43. package/dist/css/themes/black.css +126 -0
  44. package/dist/css/themes/blue.css +126 -0
  45. package/dist/css/themes/brown.css +126 -0
  46. package/dist/css/themes/cold-blue.css +126 -0
  47. package/dist/css/themes/funny.css +126 -0
  48. package/dist/css/themes/gray.css +126 -0
  49. package/dist/css/themes/green.css +126 -0
  50. package/dist/css/themes/modern-1.css +126 -0
  51. package/dist/css/themes/neogreen.css +126 -0
  52. package/dist/css/themes/new.css +126 -0
  53. package/dist/css/themes/new2.css +126 -0
  54. package/dist/css/themes/oled.css +126 -0
  55. package/dist/css/themes/orange.css +126 -0
  56. package/dist/css/themes/pastel-pink.css +126 -0
  57. package/dist/css/themes/pink.css +126 -0
  58. package/dist/css/themes/purple.css +126 -0
  59. package/dist/css/themes/red.css +126 -0
  60. package/dist/css/themes/super-blue.css +126 -0
  61. package/dist/css/themes.css +2431 -0
  62. package/dist/css/tokens.css +452 -0
  63. package/dist/elements/index.d.ts +183 -0
  64. package/dist/elements/index.js +472 -0
  65. package/dist/elements/index.js.map +1 -0
  66. package/dist/engine-CUr9QijM.d.ts +46 -0
  67. package/dist/gl/index.d.ts +61 -0
  68. package/dist/gl/index.js +18 -0
  69. package/dist/gl/index.js.map +1 -0
  70. package/dist/index.css +5296 -0
  71. package/dist/index.d.ts +440 -0
  72. package/dist/index.js +390 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/react/index.d.ts +476 -0
  75. package/dist/react/index.js +969 -0
  76. package/dist/react/index.js.map +1 -0
  77. package/dist/reduced-BM4Ls0g5.d.ts +566 -0
  78. package/dist/reveal-WFUWDrei.d.ts +416 -0
  79. package/dist/store-Bqy_obnD.d.ts +195 -0
  80. package/dist/theme/generate.d.ts +18 -0
  81. package/dist/theme/generate.js +64 -0
  82. package/dist/theme/generate.js.map +1 -0
  83. package/dist/theme/index.d.ts +17 -0
  84. package/dist/theme/index.js +32 -0
  85. package/dist/theme/index.js.map +1 -0
  86. package/dist/theme-BFMoCzi6.d.ts +158 -0
  87. package/dist/types-BkS_raL8.d.ts +7 -0
  88. package/package.json +114 -3
  89. package/registry.json +469 -0
  90. package/skills/motion-kit/SKILL.md +189 -0
  91. package/skills/motion-kit/reference/components.md +86 -0
  92. package/skills/motion-kit/reference/design.md +92 -0
  93. package/skills/motion-kit/reference/recipes.md +172 -0
  94. package/src/auto/auto-init.ts +151 -0
  95. package/src/auto/index.ts +3 -0
  96. package/src/auto/parse-options.ts +68 -0
  97. package/src/components/chat/README.md +90 -0
  98. package/src/components/chat/chat.css +236 -0
  99. package/src/components/chat/element.ts +98 -0
  100. package/src/components/chat/engine.ts +295 -0
  101. package/src/components/chat/react.tsx +196 -0
  102. package/src/components/chat-background/README.md +50 -0
  103. package/src/components/chat-background/audio.ts +56 -0
  104. package/src/components/chat-background/chat-background.css +86 -0
  105. package/src/components/chat-background/element.ts +31 -0
  106. package/src/components/chat-background/engine.ts +108 -0
  107. package/src/components/chat-background/react.tsx +36 -0
  108. package/src/components/cursor/README.md +53 -0
  109. package/src/components/cursor/cursor.css +177 -0
  110. package/src/components/cursor/element.ts +23 -0
  111. package/src/components/cursor/engine.ts +254 -0
  112. package/src/components/cursor/react.tsx +26 -0
  113. package/src/components/dialog/README.md +106 -0
  114. package/src/components/dialog/dialog.css +258 -0
  115. package/src/components/dialog/element.ts +118 -0
  116. package/src/components/dialog/engine.ts +432 -0
  117. package/src/components/dialog/react.tsx +200 -0
  118. package/src/components/icon.tsx +10 -0
  119. package/src/components/icons.ts +24 -0
  120. package/src/components/island/README.md +91 -0
  121. package/src/components/island/element.ts +45 -0
  122. package/src/components/island/engine.ts +200 -0
  123. package/src/components/island/island.css +300 -0
  124. package/src/components/island/react.tsx +115 -0
  125. package/src/components/menu/README.md +67 -0
  126. package/src/components/menu/element.ts +55 -0
  127. package/src/components/menu/engine.ts +142 -0
  128. package/src/components/menu/menu.css +104 -0
  129. package/src/components/menu/react.tsx +82 -0
  130. package/src/components/nav/README.md +85 -0
  131. package/src/components/nav/element.ts +39 -0
  132. package/src/components/nav/engine.ts +164 -0
  133. package/src/components/nav/nav.css +1053 -0
  134. package/src/components/nav/react.tsx +90 -0
  135. package/src/components/steps/README.md +70 -0
  136. package/src/components/steps/element.ts +43 -0
  137. package/src/components/steps/engine.ts +185 -0
  138. package/src/components/steps/react.tsx +91 -0
  139. package/src/components/steps/steps.css +49 -0
  140. package/src/components/theme-picker/README.md +57 -0
  141. package/src/components/theme-picker/element.ts +20 -0
  142. package/src/components/theme-picker/engine.ts +91 -0
  143. package/src/components/theme-picker/react.tsx +31 -0
  144. package/src/components/theme-picker/theme-picker.css +114 -0
  145. package/src/components/toast/README.md +60 -0
  146. package/src/components/toast/element.ts +25 -0
  147. package/src/components/toast/engine.ts +483 -0
  148. package/src/components/toast/react.tsx +22 -0
  149. package/src/components/toast/store.ts +122 -0
  150. package/src/components/toast/toast.css +114 -0
  151. package/src/core/channels.ts +165 -0
  152. package/src/core/curves.ts +35 -0
  153. package/src/core/frame.ts +12 -0
  154. package/src/core/gsap.ts +65 -0
  155. package/src/core/loop.ts +44 -0
  156. package/src/core/math.ts +9 -0
  157. package/src/core/physics-spring.ts +59 -0
  158. package/src/core/pointer.ts +63 -0
  159. package/src/core/reduced.ts +21 -0
  160. package/src/core/scope.ts +70 -0
  161. package/src/core/scroll-track.ts +106 -0
  162. package/src/core/split.ts +68 -0
  163. package/src/core/spring.ts +22 -0
  164. package/src/core/styles.ts +31 -0
  165. package/src/core/timing.ts +204 -0
  166. package/src/core/types.ts +20 -0
  167. package/src/core/visible.ts +19 -0
  168. package/src/css/base.css +142 -0
  169. package/src/css/controls/README.md +122 -0
  170. package/src/css/controls/burger.css +67 -0
  171. package/src/css/controls/button.css +226 -0
  172. package/src/css/controls/checkbox.css +53 -0
  173. package/src/css/controls/chip.css +85 -0
  174. package/src/css/controls/field.css +124 -0
  175. package/src/css/controls/glass.css +220 -0
  176. package/src/css/controls/icon.css +26 -0
  177. package/src/css/controls/segmented.css +60 -0
  178. package/src/css/controls/switch.css +72 -0
  179. package/src/css/default-theme.css +123 -0
  180. package/src/css/effects/README.md +134 -0
  181. package/src/css/effects/anim.css +224 -0
  182. package/src/css/effects/fade.css +15 -0
  183. package/src/css/effects/float.css +64 -0
  184. package/src/css/effects/frost.css +62 -0
  185. package/src/css/effects/grain.css +58 -0
  186. package/src/css/effects/loading.css +60 -0
  187. package/src/css/effects/success.css +22 -0
  188. package/src/css/effects/text.css +67 -0
  189. package/src/css/fonts.css +3 -0
  190. package/src/css/states.css +51 -0
  191. package/src/css/themes/black-green.css +126 -0
  192. package/src/css/themes/black.css +126 -0
  193. package/src/css/themes/blue.css +126 -0
  194. package/src/css/themes/brown.css +126 -0
  195. package/src/css/themes/cold-blue.css +126 -0
  196. package/src/css/themes/funny.css +126 -0
  197. package/src/css/themes/gray.css +126 -0
  198. package/src/css/themes/green.css +126 -0
  199. package/src/css/themes/modern-1.css +126 -0
  200. package/src/css/themes/neogreen.css +126 -0
  201. package/src/css/themes/new.css +126 -0
  202. package/src/css/themes/new2.css +126 -0
  203. package/src/css/themes/oled.css +126 -0
  204. package/src/css/themes/orange.css +126 -0
  205. package/src/css/themes/pastel-pink.css +126 -0
  206. package/src/css/themes/pink.css +126 -0
  207. package/src/css/themes/purple.css +126 -0
  208. package/src/css/themes/red.css +126 -0
  209. package/src/css/themes/super-blue.css +126 -0
  210. package/src/css/tokens.css +182 -0
  211. package/src/elements/base.ts +76 -0
  212. package/src/elements/index.ts +48 -0
  213. package/src/gl/index.ts +15 -0
  214. package/src/gl/liquid-blend/README.md +23 -0
  215. package/src/gl/liquid-blend/create-liquid-blend.ts +7 -0
  216. package/src/gl/liquid-blend/shader.ts +9 -0
  217. package/src/gl/matcap/README.md +23 -0
  218. package/src/gl/matcap/create-matcap.ts +7 -0
  219. package/src/gl/matcap/shader.ts +16 -0
  220. package/src/gl/particle-hero/README.md +41 -0
  221. package/src/gl/particle-hero/create-particle-hero.ts +544 -0
  222. package/src/gl/particle-hero/particle-hero.css +19 -0
  223. package/src/gl/particle-hero/shaders.ts +84 -0
  224. package/src/gl/shader-blur/README.md +23 -0
  225. package/src/gl/shader-blur/create-shader-blur.ts +7 -0
  226. package/src/gl/shader-blur/shader.ts +11 -0
  227. package/src/gl/shader-grain/README.md +23 -0
  228. package/src/gl/shader-grain/create-shader-grain.ts +7 -0
  229. package/src/gl/shader-grain/shader.ts +6 -0
  230. package/src/gl/shared/create-shader-effect.ts +186 -0
  231. package/src/gl/shared/shader.css +3 -0
  232. package/src/gl/shared/shaders.ts +30 -0
  233. package/src/index.ts +177 -0
  234. package/src/motion/anchor.ts +45 -0
  235. package/src/motion/chat.ts +97 -0
  236. package/src/motion/confetti.ts +130 -0
  237. package/src/motion/count.ts +95 -0
  238. package/src/motion/details.ts +70 -0
  239. package/src/motion/drag-dismiss.ts +231 -0
  240. package/src/motion/effects.ts +81 -0
  241. package/src/motion/entrances.ts +27 -0
  242. package/src/motion/floating.ts +389 -0
  243. package/src/motion/flow.ts +115 -0
  244. package/src/motion/indicator.ts +44 -0
  245. package/src/motion/layout.ts +39 -0
  246. package/src/motion/liquid.ts +227 -0
  247. package/src/motion/morph.ts +540 -0
  248. package/src/motion/reveal.ts +112 -0
  249. package/src/motion/sheet.ts +65 -0
  250. package/src/motion/surface.ts +108 -0
  251. package/src/react/animated-number.tsx +30 -0
  252. package/src/react/flip-group.tsx +44 -0
  253. package/src/react/frost.tsx +33 -0
  254. package/src/react/index.ts +80 -0
  255. package/src/react/slot-text.tsx +44 -0
  256. package/src/react/theme-provider.tsx +42 -0
  257. package/src/react/use-card-dock.ts +9 -0
  258. package/src/react/use-cascade.ts +9 -0
  259. package/src/react/use-contrast.ts +9 -0
  260. package/src/react/use-counter.ts +8 -0
  261. package/src/react/use-curved-carousel.ts +10 -0
  262. package/src/react/use-depth-grid.ts +9 -0
  263. package/src/react/use-details.ts +7 -0
  264. package/src/react/use-dock-pill.ts +10 -0
  265. package/src/react/use-drag-ruler.ts +10 -0
  266. package/src/react/use-fill-text.ts +9 -0
  267. package/src/react/use-fit-text.ts +8 -0
  268. package/src/react/use-flow.ts +8 -0
  269. package/src/react/use-frame-zoom.ts +9 -0
  270. package/src/react/use-infinite-reel.ts +8 -0
  271. package/src/react/use-letters.ts +9 -0
  272. package/src/react/use-liquid-blend.ts +8 -0
  273. package/src/react/use-marquee.ts +9 -0
  274. package/src/react/use-mask-text.ts +9 -0
  275. package/src/react/use-matcap.ts +8 -0
  276. package/src/react/use-parallax.ts +9 -0
  277. package/src/react/use-particle-hero.ts +9 -0
  278. package/src/react/use-pile.ts +8 -0
  279. package/src/react/use-pop.ts +8 -0
  280. package/src/react/use-radial-menu.ts +10 -0
  281. package/src/react/use-recipe.ts +38 -0
  282. package/src/react/use-reflow.ts +9 -0
  283. package/src/react/use-repel-bento.ts +8 -0
  284. package/src/react/use-reveal.ts +8 -0
  285. package/src/react/use-rotator.ts +10 -0
  286. package/src/react/use-shader-blur.ts +8 -0
  287. package/src/react/use-shader-grain.ts +8 -0
  288. package/src/react/use-slot-text.ts +17 -0
  289. package/src/react/use-snap-strip.ts +9 -0
  290. package/src/react/use-sortable.ts +14 -0
  291. package/src/react/use-spring-cards.ts +8 -0
  292. package/src/react/use-stretch-footer.ts +20 -0
  293. package/src/react/use-tabs.ts +10 -0
  294. package/src/react/use-text-roll.ts +9 -0
  295. package/src/react/use-ticket-menu.ts +15 -0
  296. package/src/react/use-words.ts +8 -0
  297. package/src/recipes/card-dock/README.md +36 -0
  298. package/src/recipes/card-dock/card-dock.css +178 -0
  299. package/src/recipes/card-dock/create-card-dock.ts +97 -0
  300. package/src/recipes/cascade/README.md +33 -0
  301. package/src/recipes/cascade/create-cascade.ts +111 -0
  302. package/src/recipes/contrast/README.md +29 -0
  303. package/src/recipes/contrast/create-contrast.ts +68 -0
  304. package/src/recipes/counter/README.md +42 -0
  305. package/src/recipes/counter/create-counter.ts +84 -0
  306. package/src/recipes/curved-carousel/README.md +50 -0
  307. package/src/recipes/curved-carousel/carousel-math.ts +20 -0
  308. package/src/recipes/curved-carousel/create-curved-carousel.ts +257 -0
  309. package/src/recipes/curved-carousel/curved-carousel.css +50 -0
  310. package/src/recipes/depth-grid/README.md +30 -0
  311. package/src/recipes/depth-grid/create-depth-grid.ts +120 -0
  312. package/src/recipes/details/README.md +31 -0
  313. package/src/recipes/details/create-details.ts +27 -0
  314. package/src/recipes/dock-pill/README.md +23 -0
  315. package/src/recipes/dock-pill/create-dock-pill.ts +68 -0
  316. package/src/recipes/dock-pill/dock-pill.css +5 -0
  317. package/src/recipes/drag-ruler/README.md +21 -0
  318. package/src/recipes/drag-ruler/create-drag-ruler.ts +95 -0
  319. package/src/recipes/drag-ruler/drag-ruler.css +16 -0
  320. package/src/recipes/fill-text/README.md +27 -0
  321. package/src/recipes/fill-text/create-fill-text.ts +93 -0
  322. package/src/recipes/fit-text/README.md +37 -0
  323. package/src/recipes/fit-text/create-fit-text.ts +55 -0
  324. package/src/recipes/fit-text/fit-text.css +6 -0
  325. package/src/recipes/flow/README.md +46 -0
  326. package/src/recipes/flow/create-flow.ts +55 -0
  327. package/src/recipes/frame-zoom/README.md +29 -0
  328. package/src/recipes/frame-zoom/create-frame-zoom.ts +91 -0
  329. package/src/recipes/infinite-reel/README.md +23 -0
  330. package/src/recipes/infinite-reel/create-infinite-reel.ts +140 -0
  331. package/src/recipes/infinite-reel/infinite-reel.css +8 -0
  332. package/src/recipes/letters/README.md +30 -0
  333. package/src/recipes/letters/create-letters.ts +113 -0
  334. package/src/recipes/marquee/README.md +32 -0
  335. package/src/recipes/marquee/create-marquee.ts +74 -0
  336. package/src/recipes/marquee/marquee.css +41 -0
  337. package/src/recipes/mask-text/README.md +39 -0
  338. package/src/recipes/mask-text/create-mask-text.ts +134 -0
  339. package/src/recipes/parallax/README.md +31 -0
  340. package/src/recipes/parallax/create-parallax.ts +74 -0
  341. package/src/recipes/pile/README.md +48 -0
  342. package/src/recipes/pile/create-pile.ts +50 -0
  343. package/src/recipes/pile/pile-math.ts +27 -0
  344. package/src/recipes/pile/pile.css +63 -0
  345. package/src/recipes/pill/README.md +30 -0
  346. package/src/recipes/pill/pill.css +48 -0
  347. package/src/recipes/pop/README.md +36 -0
  348. package/src/recipes/pop/create-pop.ts +52 -0
  349. package/src/recipes/radial-menu/README.md +44 -0
  350. package/src/recipes/radial-menu/create-radial-menu.ts +96 -0
  351. package/src/recipes/radial-menu/radial-menu.css +45 -0
  352. package/src/recipes/reflow/README.md +47 -0
  353. package/src/recipes/reflow/create-reflow.ts +49 -0
  354. package/src/recipes/repel-bento/README.md +21 -0
  355. package/src/recipes/repel-bento/create-repel-bento.ts +65 -0
  356. package/src/recipes/repel-bento/repel-bento.css +3 -0
  357. package/src/recipes/reveal/README.md +36 -0
  358. package/src/recipes/reveal/create-reveal.ts +48 -0
  359. package/src/recipes/rotator/README.md +21 -0
  360. package/src/recipes/rotator/create-rotator.ts +66 -0
  361. package/src/recipes/rotator/rotator.css +3 -0
  362. package/src/recipes/slot-text/README.md +44 -0
  363. package/src/recipes/slot-text/create-slot-text.ts +244 -0
  364. package/src/recipes/slot-text/slot-text.css +42 -0
  365. package/src/recipes/snap-strip/README.md +31 -0
  366. package/src/recipes/snap-strip/create-snap-strip.ts +133 -0
  367. package/src/recipes/snap-strip/snap-strip.css +23 -0
  368. package/src/recipes/sortable/README.md +88 -0
  369. package/src/recipes/sortable/create-sortable.ts +863 -0
  370. package/src/recipes/sortable/sortable.css +40 -0
  371. package/src/recipes/spring-cards/README.md +48 -0
  372. package/src/recipes/spring-cards/create-spring-cards.ts +140 -0
  373. package/src/recipes/spring-cards/spring-cards.css +23 -0
  374. package/src/recipes/stretch-footer/README.md +44 -0
  375. package/src/recipes/stretch-footer/create-stretch-footer.ts +69 -0
  376. package/src/recipes/stretch-footer/stretch-footer.css +28 -0
  377. package/src/recipes/tabs/README.md +39 -0
  378. package/src/recipes/tabs/create-tabs.ts +91 -0
  379. package/src/recipes/tabs/tabs.css +19 -0
  380. package/src/recipes/text-roll/README.md +35 -0
  381. package/src/recipes/text-roll/create-text-roll.ts +137 -0
  382. package/src/recipes/text-roll/text-roll.css +21 -0
  383. package/src/recipes/ticket-menu/README.md +24 -0
  384. package/src/recipes/ticket-menu/create-ticket-menu.ts +6 -0
  385. package/src/recipes/words/README.md +43 -0
  386. package/src/recipes/words/create-words.ts +114 -0
  387. package/src/theme/css-format.ts +32 -0
  388. package/src/theme/generate.ts +72 -0
  389. package/src/theme/index.ts +15 -0
  390. package/src/theme/presets.ts +79 -0
  391. package/src/theme/theme.ts +119 -0
@@ -0,0 +1,65 @@
1
+ import { gsap, EASE, registerMotion } from '../core/gsap';
2
+ import { motionSpring } from '../core/physics-spring';
3
+ import { reduced } from '../core/reduced';
4
+ import { clearStyles } from '../core/styles';
5
+ import { TIMING } from '../core/timing';
6
+ import { backdropVeil, ghostOf } from './surface';
7
+
8
+ /*
9
+ * Dousan's project sheet: the surface rises from below the screen (0.72 s,
10
+ * cubic(.16,1,.3,1)) while the page behind ([data-mk-underlay]) sinks to
11
+ * scale .98 / opacity .4 on a 300/30 spring. It closes through an inert copy
12
+ * that drops away, so focus returns at once.
13
+ */
14
+
15
+ const UNDERLAY_STYLES = ['transform', 'translate', 'rotate', 'scale', 'transform-origin', 'opacity'];
16
+
17
+ export function sheetOpen(surface: HTMLElement) {
18
+ if (reduced()) return null;
19
+ registerMotion();
20
+ return gsap.fromTo(surface, { yPercent: 100 }, { yPercent: 0, duration: TIMING.sheet.open, ease: EASE.sheet, clearProps: 'transform' });
21
+ }
22
+
23
+ /** Drop a copy of the (still open) sheet off the bottom of the screen. */
24
+ export function sheetClose(surface: HTMLElement, { inPlace = false }: { inPlace?: boolean } = {}) {
25
+ registerMotion();
26
+ const dragged = inPlace ? Number(gsap.getProperty(surface, 'y')) || 0 : 0;
27
+ gsap.killTweensOf(surface);
28
+ clearStyles([surface], ['transform', 'translate', 'rotate', 'scale']);
29
+ const ghost = reduced() ? null : ghostOf(surface);
30
+ if (!ghost) return null;
31
+ const veil = backdropVeil(surface);
32
+ document.body.append(veil, ghost);
33
+ gsap.to(veil, { opacity: 0, duration: TIMING.sheet.close, ease: 'power3.in', onComplete: () => veil.remove() });
34
+ return gsap.fromTo(
35
+ ghost,
36
+ { y: dragged, yPercent: 0 },
37
+ { yPercent: 100, duration: TIMING.sheet.close, ease: EASE.sheet, onComplete: () => ghost.remove() },
38
+ );
39
+ }
40
+
41
+ export function pushUnderlay(elements: HTMLElement[]) {
42
+ if (!elements.length || reduced()) return;
43
+ registerMotion();
44
+ const { ease, duration } = motionSpring(...TIMING.springs.underlay);
45
+ gsap.to(elements, {
46
+ scale: TIMING.sheet.underlayScale,
47
+ opacity: TIMING.sheet.underlayOpacity,
48
+ transformOrigin: '50% 0',
49
+ duration,
50
+ ease,
51
+ overwrite: 'auto',
52
+ });
53
+ }
54
+
55
+ export function releaseUnderlay(elements: HTMLElement[], animated = true) {
56
+ if (!elements.length) return;
57
+ const restore = () => clearStyles(elements, UNDERLAY_STYLES);
58
+ if (!animated || reduced()) {
59
+ gsap.killTweensOf(elements);
60
+ restore();
61
+ return;
62
+ }
63
+ const { ease, duration } = motionSpring(...TIMING.springs.underlay);
64
+ gsap.to(elements, { scale: 1, opacity: 1, duration, ease, overwrite: 'auto', onComplete: restore });
65
+ }
@@ -0,0 +1,108 @@
1
+ import { SIGNATURE_LINEAR } from '../core/curves';
2
+ import { reduced } from '../core/reduced';
3
+ import { TIMING } from '../core/timing';
4
+
5
+ /*
6
+ * Helpers shared by surfaces (dialogs, menus): opening a <dialog> with a
7
+ * fallback where showModal is missing, the backdrop tint, and an inert visual
8
+ * copy (ghost) that plays a close while the real element is already gone.
9
+ */
10
+
11
+ /** A computed colour with no alpha: `transparent`, `rgba(…, 0)` or `color(… / 0)`. */
12
+ export const transparentColor = (color: string) =>
13
+ !color || color === 'transparent' || /,\s*0\)$/.test(color) || /\/\s*0\)$/.test(color);
14
+
15
+ type DialogLike = HTMLElement & Partial<Pick<HTMLDialogElement, 'showModal' | 'show' | 'close'>>;
16
+
17
+ /** Open a <dialog> modally (or not). Falls back to the `open` attribute. */
18
+ export function openModal(dialog: DialogLike, modal = true) {
19
+ if (dialog.hasAttribute('open')) return;
20
+ if (modal && typeof dialog.showModal === 'function') dialog.showModal();
21
+ else if (typeof dialog.show === 'function') dialog.show();
22
+ else dialog.setAttribute('open', '');
23
+ }
24
+
25
+ export function closeModal(dialog: DialogLike) {
26
+ if (typeof dialog.close === 'function') dialog.close();
27
+ else dialog.removeAttribute('open');
28
+ }
29
+
30
+ /**
31
+ * Fade a modal <dialog>'s ::backdrop in (Stepbro's tint: 0.5 s on the signature
32
+ * curve) or out (linear). Resolves at once without the Web Animations API.
33
+ */
34
+ export function fadeBackdrop(dialog: HTMLElement, direction: 'in' | 'out'): Promise<void> {
35
+ if (typeof dialog.animate !== 'function' || reduced()) return Promise.resolve();
36
+ try {
37
+ const animation = dialog.animate(direction === 'in' ? [{ opacity: 0 }, { opacity: 1 }] : [{ opacity: 1 }, { opacity: 0 }], {
38
+ duration: (direction === 'in' ? TIMING.dialog.backdrop : TIMING.dialog.close) * 1000,
39
+ easing: direction === 'in' ? SIGNATURE_LINEAR : 'linear',
40
+ fill: direction === 'out' ? 'forwards' : 'none',
41
+ pseudoElement: '::backdrop',
42
+ });
43
+ return animation.finished.then(
44
+ () => undefined,
45
+ () => undefined,
46
+ );
47
+ } catch {
48
+ return Promise.resolve();
49
+ }
50
+ }
51
+
52
+ /**
53
+ * An inert visual copy of `surface`, fixed over the place it occupies now and not
54
+ * yet in the document (null when it has no size). A picture, not a second form:
55
+ * no ids, labels, names or roles to collide with the page.
56
+ */
57
+ /** A fixed copy of a modal's backdrop (tint and blur), for a closing ghost to fade with. Not appended. */
58
+ export function backdropVeil(surface: HTMLElement): HTMLElement {
59
+ const backdrop = getComputedStyle(surface, '::backdrop');
60
+ const veil = document.createElement('div');
61
+ veil.setAttribute('aria-hidden', 'true');
62
+ veil.dataset.mkVeil = '';
63
+ Object.assign(veil.style, {
64
+ position: 'fixed',
65
+ inset: '0',
66
+ zIndex: '2147483645',
67
+ background: backdrop.background || backdrop.backgroundColor,
68
+ backdropFilter: backdrop.backdropFilter,
69
+ pointerEvents: 'none',
70
+ });
71
+ return veil;
72
+ }
73
+
74
+ export function ghostOf(surface: HTMLElement): HTMLElement | null {
75
+ const rect = surface.getBoundingClientRect();
76
+ if (rect.width === 0 || rect.height === 0) return null;
77
+ const style = getComputedStyle(surface);
78
+ const ghost = document.createElement('div');
79
+ ghost.className = surface.className;
80
+ ghost.innerHTML = surface.innerHTML;
81
+ ghost.setAttribute('aria-hidden', 'true');
82
+ ghost.inert = true;
83
+ ghost.dataset.mkGhost = '';
84
+ const strip = ['id', 'for', 'name', 'aria-labelledby', 'aria-describedby', 'aria-label', 'role'];
85
+ for (const element of ghost.querySelectorAll(strip.map((name) => `[${name}]`).join(','))) {
86
+ for (const name of strip) element.removeAttribute(name);
87
+ }
88
+ for (const label of ghost.querySelectorAll('label')) {
89
+ const span = document.createElement('span');
90
+ span.className = label.className;
91
+ span.append(...label.childNodes);
92
+ label.replaceWith(span);
93
+ }
94
+ Object.assign(ghost.style, {
95
+ position: 'fixed',
96
+ inset: 'auto',
97
+ left: `${rect.left}px`,
98
+ top: `${rect.top}px`,
99
+ width: `${rect.width}px`,
100
+ height: `${rect.height}px`,
101
+ margin: '0',
102
+ display: style.display === 'none' ? 'flex' : style.display,
103
+ flexDirection: style.flexDirection,
104
+ zIndex: '2147483646',
105
+ pointerEvents: 'none',
106
+ });
107
+ return ghost;
108
+ }
@@ -0,0 +1,30 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { tweenNumber } from '../motion/count';
5
+
6
+ export interface AnimatedNumberProps {
7
+ value: number;
8
+ /** How the shown value is written. Default: whole numbers. */
9
+ format?: (shown: number) => string;
10
+ /** Seconds. Default 0.6 (Stepbro's numberSoft); prices use 0.7. */
11
+ duration?: number;
12
+ }
13
+
14
+ const whole = (shown: number) => String(Math.round(shown));
15
+
16
+ /** A number that counts to each new value from the one shown (Stepbro's AnimatedNumber). */
17
+ export function AnimatedNumber({ value, format = whole, duration }: AnimatedNumberProps) {
18
+ const [shown, setShown] = useState(value);
19
+ const current = useRef(value);
20
+ useEffect(() => {
21
+ const tween = tweenNumber(current.current, value, (next) => {
22
+ current.current = next;
23
+ setShown(next);
24
+ }, { duration });
25
+ return () => {
26
+ tween?.kill();
27
+ };
28
+ }, [value, duration]);
29
+ return <>{format(shown)}</>;
30
+ }
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+
3
+ import { Component, type ReactNode, type RefObject } from 'react';
4
+ import { captureLayout, reflowLayout, type CapturedLayout } from '../motion/layout';
5
+
6
+ export interface FlipGroupProps {
7
+ /** The list element. */
8
+ root: RefObject<HTMLElement | null>;
9
+ /** Items inside it; they need a stable `data-flip-id`. */
10
+ selector?: string;
11
+ /** Changes whenever the layout should animate (order, filter, grouping): e.g. the ids joined. */
12
+ signature: string;
13
+ children?: ReactNode;
14
+ }
15
+
16
+ /**
17
+ * Plays Stepbro's layout reflow when `signature` changes: the layout is
18
+ * measured just before React commits and the items travel from there. A class
19
+ * component, because only `getSnapshotBeforeUpdate` reads the DOM before the commit.
20
+ */
21
+ export class FlipGroup extends Component<FlipGroupProps> {
22
+ private flip: ReturnType<typeof reflowLayout> = null;
23
+
24
+ override getSnapshotBeforeUpdate(previous: Readonly<FlipGroupProps>): CapturedLayout | null {
25
+ const node = this.props.root.current;
26
+ if (previous.signature === this.props.signature || !node) return null;
27
+ return captureLayout(node, this.props.selector ?? '[data-flip-id]');
28
+ }
29
+
30
+ override componentDidUpdate(_previous: unknown, _state: unknown, before: CapturedLayout | null) {
31
+ const node = this.props.root.current;
32
+ if (!before || !node) return;
33
+ this.flip?.kill();
34
+ this.flip = reflowLayout(node, this.props.selector ?? '[data-flip-id]', before);
35
+ }
36
+
37
+ override componentWillUnmount() {
38
+ this.flip?.kill();
39
+ }
40
+
41
+ override render() {
42
+ return this.props.children;
43
+ }
44
+ }
@@ -0,0 +1,33 @@
1
+ import type { CSSProperties } from 'react';
2
+
3
+ export interface FrostProps {
4
+ /** Which edge the blur grows toward (default bottom). */
5
+ position?: 'bottom' | 'top';
6
+ /** Pin it to the window instead of the positioned parent. */
7
+ fixed?: boolean;
8
+ height?: number | string;
9
+ /** Multiplies the five blurs (default 1). */
10
+ strength?: number;
11
+ className?: string;
12
+ style?: CSSProperties;
13
+ }
14
+
15
+ /** Dousan's progressive frost: five blur layers. Put it inside a positioned container (or use `fixed`). */
16
+ export function Frost({ position = 'bottom', fixed = false, height, strength, className, style }: FrostProps) {
17
+ const vars: Record<string, string> = {};
18
+ if (height !== undefined) vars['--mk-frost-height'] = typeof height === 'number' ? `${height}px` : height;
19
+ if (strength !== undefined) vars['--mk-frost-strength'] = String(strength);
20
+ return (
21
+ <div
22
+ aria-hidden="true"
23
+ className={['mk-frost', position === 'top' && 'mk-frost--top', fixed && 'mk-frost--fixed', className].filter(Boolean).join(' ')}
24
+ style={{ ...style, ...vars } as CSSProperties}
25
+ >
26
+ <i />
27
+ <i />
28
+ <i />
29
+ <i />
30
+ <i />
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+
3
+ export { useRecipe, useLatest, useIsomorphicLayoutEffect } from './use-recipe';
4
+ export * from './use-spring-cards';
5
+ export * from './use-curved-carousel';
6
+ export * from './use-pile';
7
+ export * from './use-words';
8
+ export * from './use-flow';
9
+ export * from './use-pop';
10
+ export * from './use-fit-text';
11
+ export * from './use-stretch-footer';
12
+ export * from './use-radial-menu';
13
+ export * from './use-tabs';
14
+ export * from './use-counter';
15
+ export * from './use-details';
16
+ export * from './use-reveal';
17
+ export * from './use-reflow';
18
+ export * from './use-slot-text';
19
+ export * from './use-sortable';
20
+ export * from './use-mask-text';
21
+ export * from './use-cascade';
22
+ export * from './use-text-roll';
23
+ export * from './use-letters';
24
+ export * from './use-parallax';
25
+ export * from './use-depth-grid';
26
+ export * from './use-frame-zoom';
27
+ export * from './use-contrast';
28
+ export * from './use-marquee';
29
+ export * from './use-card-dock';
30
+ export * from './use-snap-strip';
31
+ export * from './use-fill-text';
32
+ export * from './use-ticket-menu';
33
+ export * from './use-infinite-reel';
34
+ export * from './use-repel-bento';
35
+ export * from './use-rotator';
36
+ export * from './use-drag-ruler';
37
+ export * from './use-dock-pill';
38
+ export * from './use-liquid-blend';
39
+ export * from './use-matcap';
40
+ export * from './use-shader-grain';
41
+ export * from './use-shader-blur';
42
+ export * from './use-particle-hero';
43
+ export { moveItem, type ReorderDetail } from '../recipes/sortable/create-sortable';
44
+ export { pileLayers, pileStyle, type PileLayer } from '../recipes/pile/create-pile';
45
+ export { ThemeProvider, useTheme, type ThemeProviderProps, type ThemeContextValue } from './theme-provider';
46
+ export { ThemePicker, type ThemePickerProps } from '../components/theme-picker/react';
47
+ export { SlotText, type SlotTextProps } from './slot-text';
48
+ export { AnimatedNumber, type AnimatedNumberProps } from './animated-number';
49
+ export { FlipGroup, type FlipGroupProps } from './flip-group';
50
+ export { Dialog, useDialog, MkIcon, type DialogContentProps, type SurfaceRootProps } from '../components/dialog/react';
51
+ export { Menu, type MenuRootProps } from '../components/menu/react';
52
+ export {
53
+ Toaster,
54
+ toast,
55
+ dismissToast,
56
+ clearToasts,
57
+ type ToasterProps,
58
+ type ToastOptions,
59
+ type ToastTone,
60
+ type ToastAction,
61
+ } from '../components/toast/react';
62
+ export { ChatBackground, type ChatBackgroundProps } from '../components/chat-background/react';
63
+ export {
64
+ Chat,
65
+ useChat,
66
+ type ChatRootProps,
67
+ type ChatMessagesProps,
68
+ type ChatMessageProps,
69
+ type ChatComposerProps,
70
+ } from '../components/chat/react';
71
+ export { Nav, type NavProps, type NavItem } from '../components/nav/react';
72
+ export { NAV_STYLES, type NavStyle } from '../components/nav/engine';
73
+ export { Steps, type StepsProps } from '../components/steps/react';
74
+ export { Cursor, type CursorProps } from '../components/cursor/react';
75
+ export { Frost, type FrostProps } from './frost';
76
+ export { animate, leave, ANIMATIONS, type AnimationName } from '../motion/effects';
77
+ export { confetti, type ConfettiOptions } from '../motion/confetti';
78
+ export { MK_HEAD_SCRIPT } from '../core/reduced';
79
+ export { MK_THEME_HEAD_SCRIPT, themeHeadScript } from '../theme/theme';
80
+ export { Island, IslandDots, type IslandProps, type IslandPanel } from '../components/island/react';
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+
3
+ import { useRef, useState } from 'react';
4
+ import { useIsomorphicLayoutEffect } from './use-recipe';
5
+ import { createSlotText, type SlotTextInstance, type SlotTextOptions } from '../recipes/slot-text/create-slot-text';
6
+
7
+ export interface SlotTextProps extends Omit<SlotTextOptions, 'text' | 'accessibleCopy'> {
8
+ text: string;
9
+ className?: string;
10
+ }
11
+
12
+ const escapeHtml = (text: string) => text.replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
13
+
14
+ /**
15
+ * Text whose letters roll when it changes (Stepbro's SlotText). The server
16
+ * renders the text itself; React never reconciles the letters (they belong to
17
+ * the recipe), and screen readers read the plain copy.
18
+ */
19
+ export function SlotText({ text, className, direction, duration, stagger, exitOffset, colorFade, maxTilt }: SlotTextProps) {
20
+ const ref = useRef<HTMLSpanElement>(null);
21
+ const instance = useRef<SlotTextInstance | null>(null);
22
+ const [initial] = useState(text);
23
+ // A stable object: React 19 re-applies innerHTML when this prop changes identity.
24
+ const [html] = useState(() => ({ __html: escapeHtml(text) }));
25
+ useIsomorphicLayoutEffect(() => {
26
+ const created = createSlotText(ref.current, { direction, duration, stagger, exitOffset, colorFade, maxTilt, text: initial, accessibleCopy: false });
27
+ instance.current = created;
28
+ return () => {
29
+ created.destroy();
30
+ instance.current = null;
31
+ };
32
+ // The recipe is created once; the text is pushed below.
33
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34
+ }, []);
35
+ useIsomorphicLayoutEffect(() => {
36
+ instance.current?.set(text);
37
+ }, [text]);
38
+ return (
39
+ <span className={className}>
40
+ <span className="mk-sr-only">{text}</span>
41
+ <span ref={ref} aria-hidden="true" dangerouslySetInnerHTML={html} />
42
+ </span>
43
+ );
44
+ }
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext, useMemo, useState, type ReactNode } from 'react';
4
+ import { useIsomorphicLayoutEffect } from './use-recipe';
5
+ import { DEFAULT_THEME } from '../theme/presets';
6
+ import { THEME_EVENT, initTheme, setMode, setTheme, type InitThemeOptions, type ThemeMode, type ThemeState } from '../theme/theme';
7
+
8
+ export interface ThemeContextValue extends ThemeState {
9
+ setTheme: typeof setTheme;
10
+ setMode: typeof setMode;
11
+ }
12
+
13
+ const ThemeContext = createContext<ThemeContextValue | null>(null);
14
+
15
+ export interface ThemeProviderProps {
16
+ children?: ReactNode;
17
+ defaultTheme?: InitThemeOptions['default'];
18
+ mode?: ThemeMode;
19
+ storageKey?: string;
20
+ }
21
+
22
+ /**
23
+ * Applies the stored (or default) theme on mount and shares it. Add
24
+ * MK_THEME_HEAD_SCRIPT to <head> as well, so the first paint already has it.
25
+ */
26
+ export function ThemeProvider({ children, defaultTheme, mode, storageKey }: ThemeProviderProps) {
27
+ const [state, setState] = useState<ThemeState>(() => ({ id: defaultTheme ?? DEFAULT_THEME, mode: mode ?? 'system', resolved: 'light' }));
28
+ useIsomorphicLayoutEffect(() => {
29
+ const onChange = (event: Event) => setState((event as CustomEvent<ThemeState>).detail);
30
+ document.addEventListener(THEME_EVENT, onChange);
31
+ setState(initTheme({ default: defaultTheme, mode, storageKey }));
32
+ return () => document.removeEventListener(THEME_EVENT, onChange);
33
+ }, [defaultTheme, mode, storageKey]);
34
+ const value = useMemo(() => ({ ...state, setTheme, setMode }), [state]);
35
+ return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
36
+ }
37
+
38
+ export function useTheme(): ThemeContextValue {
39
+ const value = useContext(ThemeContext);
40
+ if (!value) throw new Error('useTheme() needs a <ThemeProvider> above it');
41
+ return value;
42
+ }
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createCardDock, type CardDockOptions } from '../recipes/card-dock/create-card-dock';
6
+
7
+ /** iyo.ai's product dock (stacked glass cards that spread on hover). */
8
+ export const useCardDock = (ref: RefObject<HTMLElement | null>, options?: CardDockOptions, deps?: DependencyList) =>
9
+ useRecipe(ref, createCardDock, options, deps);
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createCascade, type CascadeOptions } from '../recipes/cascade/create-cascade';
6
+
7
+ /** Siena's reveal group: rules, fades and rises one after another. */
8
+ export const useCascade = (ref: RefObject<HTMLElement | null>, options?: CascadeOptions, deps?: DependencyList) =>
9
+ useRecipe(ref, createCascade, options, deps);
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createContrast, type ContrastOptions } from '../recipes/contrast/create-contrast';
6
+
7
+ /** Siena's header contrast from the section under it. */
8
+ export const useContrast = (ref: RefObject<HTMLElement | null>, options?: ContrastOptions, deps?: DependencyList) =>
9
+ useRecipe(ref, createContrast, options, deps);
@@ -0,0 +1,8 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createCounter, type CounterOptions } from '../recipes/counter/create-counter';
6
+
7
+ export const useCounter = (ref: RefObject<HTMLElement | null>, options?: CounterOptions, deps?: DependencyList) =>
8
+ useRecipe(ref, createCounter, options, deps);
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useLatest, useRecipe } from './use-recipe';
5
+ import { createCurvedCarousel, type CurvedCarouselOptions } from '../recipes/curved-carousel/create-curved-carousel';
6
+
7
+ export function useCurvedCarousel(ref: RefObject<HTMLElement | null>, options: CurvedCarouselOptions = {}, deps?: DependencyList) {
8
+ const onSelect = useLatest(options.onSelect);
9
+ return useRecipe(ref, createCurvedCarousel, { ...options, onSelect: (index, slide) => onSelect.current?.(index, slide) }, deps);
10
+ }
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createDepthGrid, type DepthGridOptions } from '../recipes/depth-grid/create-depth-grid';
6
+
7
+ /** Siena's grid gathering from depth as it scrolls. */
8
+ export const useDepthGrid = (ref: RefObject<HTMLElement | null>, options?: DepthGridOptions, deps?: DependencyList) =>
9
+ useRecipe(ref, createDepthGrid, options, deps);
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createDetails } from '../recipes/details/create-details';
6
+
7
+ export const useDetails = (ref: RefObject<HTMLElement | null>, deps?: DependencyList) => useRecipe(ref, createDetails, undefined, deps);
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useLatest, useRecipe } from './use-recipe';
5
+ import { createDockPill, type DockPillOptions } from '../recipes/dock-pill/create-dock-pill';
6
+
7
+ export function useDockPill(ref: RefObject<HTMLElement | null>, options: DockPillOptions = {}, deps?: DependencyList) {
8
+ const callback = useLatest(options.onSelect);
9
+ return useRecipe(ref, createDockPill, { ...options, onSelect: (index, item) => callback.current?.(index, item) }, deps);
10
+ }
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useLatest, useRecipe } from './use-recipe';
5
+ import { createDragRuler, type DragRulerOptions } from '../recipes/drag-ruler/create-drag-ruler';
6
+
7
+ export function useDragRuler(ref: RefObject<HTMLElement | null>, options: DragRulerOptions = {}, deps?: DependencyList) {
8
+ const callback = useLatest(options.onChange);
9
+ return useRecipe(ref, createDragRuler, { ...options, onChange: (value) => callback.current?.(value) }, deps);
10
+ }
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createFillText, type FillTextOptions } from '../recipes/fill-text/create-fill-text';
6
+
7
+ /** iyo.ai's fill-text: letters fill in as the text scrolls up. */
8
+ export const useFillText = (ref: RefObject<HTMLElement | null>, options?: FillTextOptions, deps?: DependencyList) =>
9
+ useRecipe(ref, createFillText, options, deps);
@@ -0,0 +1,8 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createFitText, type FitTextOptions } from '../recipes/fit-text/create-fit-text';
6
+
7
+ export const useFitText = (ref: RefObject<HTMLElement | null>, options?: FitTextOptions, deps?: DependencyList) =>
8
+ useRecipe(ref, createFitText, options, deps);
@@ -0,0 +1,8 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createFlow, type FlowOptions } from '../recipes/flow/create-flow';
6
+
7
+ export const useFlow = (ref: RefObject<HTMLElement | null>, options?: FlowOptions, deps?: DependencyList) =>
8
+ useRecipe(ref, createFlow, options, deps);
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createFrameZoom, type FrameZoomOptions } from '../recipes/frame-zoom/create-frame-zoom';
6
+
7
+ /** Siena's video mask: frame and picture scale in opposite directions. */
8
+ export const useFrameZoom = (ref: RefObject<HTMLElement | null>, options?: FrameZoomOptions, deps?: DependencyList) =>
9
+ useRecipe(ref, createFrameZoom, options, deps);
@@ -0,0 +1,8 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createInfiniteReel, type InfiniteReelOptions } from '../recipes/infinite-reel/create-infinite-reel';
6
+
7
+ export const useInfiniteReel = (ref: RefObject<HTMLElement | null>, options?: InfiniteReelOptions, deps?: DependencyList) =>
8
+ useRecipe(ref, createInfiniteReel, options, deps);
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createLetters, type LettersOptions } from '../recipes/letters/create-letters';
6
+
7
+ /** Dousan's hero letters (scale on hover or pastel blink). */
8
+ export const useLetters = (ref: RefObject<HTMLElement | null>, options?: LettersOptions, deps?: DependencyList) =>
9
+ useRecipe(ref, createLetters, options, deps);
@@ -0,0 +1,8 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createLiquidBlend, type LiquidBlendOptions } from '../gl/liquid-blend/create-liquid-blend';
6
+
7
+ export const useLiquidBlend = (ref: RefObject<HTMLElement | null>, options?: LiquidBlendOptions, deps?: DependencyList) =>
8
+ useRecipe(ref, createLiquidBlend, options, deps);
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createMarquee, type MarqueeOptions } from '../recipes/marquee/create-marquee';
6
+
7
+ /** Siena's seamless marquee. */
8
+ export const useMarquee = (ref: RefObject<HTMLElement | null>, options?: MarqueeOptions, deps?: DependencyList) =>
9
+ useRecipe(ref, createMarquee, options, deps);
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createMaskText, type MaskTextOptions } from '../recipes/mask-text/create-mask-text';
6
+
7
+ /** Masked lines / words / letters rising into view (OFFFICE). */
8
+ export const useMaskText = (ref: RefObject<HTMLElement | null>, options?: MaskTextOptions, deps?: DependencyList) =>
9
+ useRecipe(ref, createMaskText, options, deps);
@@ -0,0 +1,8 @@
1
+ 'use client';
2
+
3
+ import type { DependencyList, RefObject } from 'react';
4
+ import { useRecipe } from './use-recipe';
5
+ import { createMatcap, type MatcapOptions } from '../gl/matcap/create-matcap';
6
+
7
+ export const useMatcap = (ref: RefObject<HTMLElement | null>, options?: MatcapOptions, deps?: DependencyList) =>
8
+ useRecipe(ref, createMatcap, options, deps);