@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,189 @@
1
+ ---
2
+ name: motion-kit
3
+ description: Apply Yewdiel's motion and design line (@yewdiel21/motion-kit) in any web project — Stepbro's dialogs that grow out of their button (morph, liquid, shared title, drag to dismiss, floating window), options menu, toast deck, chat (glass bubbles, delayed "Pensando…" that morphs into the reply, live background driven by the microphone), navigation (7 styles + phone bar + app shell), onboarding steps, drag to reorder (lists and kanban, tilting floating copy, keyboard), Dousan Miao's glass, progressive frost, floating tags, film grain, success confetti and bottom sheet, Siena's burger icon and rolling button, text and scroll motion (OFFFICE masked type, Siena's slot title, reveal cascade, photo parallax, depth grid, frame zoom, header contrast, marquee, Dousan's playful letters) and two custom cursors (Dousan's glass chip, Siena's dot), iyo.ai's floating glass navbar (island), stacked product card dock, autoplaying snap strip, scroll fill-text and GPU particle hero (optional /gl entry), Stepbro's utility animations (exits, shake, highlight, loading sweeps, gradient text), design line (19 Material 3 themes light/dark, tokens, DM Sans type scale, capsule controls, theme picker, theme generator from a colour) and motion (flowIn/flowOut, step change, reveal wave, details, sliding tab indicator, counters, layout reflow, slot text, chat message entrance) plus spencergabor.com recipes (spring cards, curved inertia carousel, piles, elastic words, stretching footer, radial menu), for Astro/HTML (data-mk + autoInit, custom elements) or React/Next.js (hooks and components). Use when the user asks for "texto que sale de una máscara", "texto por líneas", "título que gira / tragaperras", "parallax", "grid desde el fondo", "marquee / banda infinita", "cursor personalizado", "cursor que sigue al ratón", "navbar flotante", "navbar de cristal / isla", "dock de tarjetas", "cards apiladas", "carrusel con snap", "texto que se rellena con el scroll", "hero de partículas", "partículas WebGL", "cristal", "glassmorphism", "liquid glass", "blur progresivo", "confeti", "animación de salida", "quitar fila animada", "shake", "skeleton / loading", "texto degradado", "sheet / hoja desde abajo", "hamburguesa a X", "diálogo que sale del botón", "chat como Stepbro", "burbujas de chat", "fondo del chat", "navegación de Stepbro", "barra de navegación", "sidebar / dock", "onboarding por pasos", "arrastrar para reordenar", "kanban", "drag and drop", "modal como Stepbro", "ventana flotante", "arrastrar para cerrar", "menú de opciones", "toasts", "la línea de diseño de Stepbro", "temas de Stepbro", "selector de tema", "botones/campos como Stepbro", "tabs con indicador", "contador animado", "slot text", "onda reveal", "flowIn", "las animaciones de Spencer", "carrusel curvo", "cards con resorte", "footer que se estira", "menú radial", "motion-kit", or wants the portfolio's or Kayro's look and motion in another project.
4
+ ---
5
+
6
+ # motion-kit
7
+
8
+ Stepbro (Tasks, Money) and spencergabor.com, rewritten as one framework-agnostic package. Layers, bottom up:
9
+
10
+ | Layer | Import | What |
11
+ |---|---|---|
12
+ | Core + motion functions | `@yewdiel21/motion-kit` | `registerMotion`, `EASE`, `TIMING`, `flowIn`, `revealFrom`, `slideIndicator`, `countTo`, `captureLayout`/`reflowLayout`, `enterMessages`… |
13
+ | Recipes | same + `/auto` + `/react` | `createX(root, options) → { destroy, …controls }`; `data-mk="x"` + `autoInit()`; `useX(ref, options)` |
14
+ | Components | engine in `.`, `<mk-x>` in `/elements`, `<X>` in `/react` | own markup + styles: `dialog`, `menu`, `toast`, `theme-picker`, `chat`, `chat-background`, `nav`, `steps`, `cursor`, `island` |
15
+ | WebGL (optional) | `@yewdiel21/motion-kit/gl` (+ matching hooks in `/react`) | `createParticleHero`, `createLiquidBlend`, `createMatcap`, `createShaderGrain`, `createShaderBlur`; reduced motion and fallback |
16
+ | Effects | `/css/effects` (in `/css`) + `.` / `/react` | `.mk-anim-*`, `.mk-loading`, `.mk-text-gradient`, `.mk-frost`, `.mk-float`, `.mk-grain`, `.mk-success`; `animate(el, name)`, `leave(el, exit)`, `confetti(origin)`, `<Frost />` |
17
+ | Design line | `/css`, `/css/themes`, `/css/fonts`, `/theme`, `/theme/generate` | tokens, 19 themes, controls, theme runtime and generator |
18
+
19
+ ## 1. Install (pick one)
20
+
21
+ **Package** (public npm registry, no token required):
22
+
23
+ ```sh
24
+ npm i @yewdiel21/motion-kit gsap
25
+ npm i -D @material/material-color-utilities@^0.3.0 # only for the theme generator
26
+ ```
27
+
28
+ **Copy the code** (editable, no runtime dependency on the package):
29
+
30
+ ```sh
31
+ npx @yewdiel21/motion-kit list
32
+ npx @yewdiel21/motion-kit add tabs slot-text theme-picker --react --elements --dir src/motion-kit
33
+ npx @yewdiel21/motion-kit add theme neogreen oled
34
+ npx @yewdiel21/motion-kit add controls
35
+ npx @yewdiel21/motion-kit theme brand --seed "#ff6a3d" --out src/styles
36
+ ```
37
+
38
+ `add` copies each entry, what it `requires`, only the core files it imports, the base CSS (tokens, default theme, base, entrance states) and, with `--react` / `--elements`, the hook/component and custom element. Imports are relative, so nothing needs rewriting. `gsap` must be installed.
39
+
40
+ ## 2. Wire it once
41
+
42
+ 1. CSS: `import '@yewdiel21/motion-kit/css'` (tokens, default theme, controls, recipe and component styles). Add `/css/themes` (all 19) or `/css/themes/<id>` and, for Stepbro's fonts, `/css/fonts`.
43
+ 2. Head, inline, in this order:
44
+ - `MK_THEME_HEAD_SCRIPT` (`/theme`): applies the stored theme/mode before the first paint.
45
+ - `MK_HEAD_SCRIPT` (`/auto` or `.`): adds `.mk-js` so entrances start hidden, `.mk-failed` if no recipe ran within 2.5 s.
46
+ 3. `<body class="mk-app">` gives the page the theme's background, text colour and DM Sans.
47
+ 4. Start things:
48
+ - **Astro / HTML**: `initTheme()`; `data-mk="<recipe>"` + `autoInit()`; `defineElements()` for `<mk-…>` components.
49
+ - **React / Next.js** (entries are `'use client'`): `<ThemeProvider>` at the root, hooks (`useTabs(ref)`), components (`<ThemePicker />`, `<SlotText text />`, `<AnimatedNumber value />`, `<FlipGroup>`). Keep `data-mk="<recipe>"` on recipe markup so the hidden entrance state applies before hydration.
50
+
51
+ ## 3. Design line
52
+
53
+ - Colour: 37 Material 3 roles `--md-sys-color-*` per theme; components use the semantic layer `--mk-bg`, `--mk-surface`, `--mk-surface-1…5` (container lowest→highest), `--mk-text`, `--mk-muted`, `--mk-accent`, `--mk-on-accent`, `--mk-accent-soft`, `--mk-secondary-soft`, `--mk-outline(-soft)`, `--mk-error(-soft)`, `--mk-scrim`, `--mk-shadow-color`. Never hard-code colours in components: use these.
54
+ - Themes: `<html data-mk-theme="<id>" data-mk-mode="system|light|dark">`. Ids: blue, purple, neogreen (default), red, pink, pastel-pink, brown, super-blue, gray, oled (the 10 Stepbro shows) + black, green, orange, cold-blue, modern-1, funny, new, new2, black-green. `setTheme(id)`, `setMode(mode)`, `getTheme()`, event `mk:theme`. Nested `[data-mk-theme]` sections work.
55
+ - Own brand colour: `applyTheme('#ff6a3d', { id: 'brand' })` at runtime, or `motion-kit theme brand --seed …` for a static file.
56
+ - Type: `--mk-font` (DM Sans), `--mk-font-mono` (Geist Mono), icons `.mk-icon` (Material Symbols Rounded). Scale classes `.mk-type-{display|headline|title|body|label}-{large|medium|small}`.
57
+ - Shape: capsules. `--mk-radius-{xs 8, sm 12, md 16, lg 24, xl 32, 2xl 40, 3xl 48, 4xl 64, full}`; bubble 32, dialog 40, chat 48, dock 64. Spacing `--mk-space-1…16` (4 px). Breakpoint 680 px.
58
+ - Controls (CSS only): `.mk-glass` (Dousan's glass on any button/chip; `<span class="mk-glass__shadow">` inside for its shadow), `.mk-burger` (three lines → X with `aria-expanded`), `.mk-button--roll` (Siena: `<span class="mk-roll" data-text>` + `<span class="mk-roll__arrow"><span class="mk-icon" data-icon>`), `.mk-button` (`--primary`, `--tonal`, `--outlined`, `--text`, `--icon`, `--sm`, `--lg`; squash on pointer focus), `.mk-field` (+ `__label`, `__hint`, `aria-invalid`), `.mk-switch` (`<input type=checkbox role=switch>`), `.mk-checkbox`, `.mk-chip` (`--filter[aria-pressed]`, `--input`), `.mk-segmented` (with the `tabs` recipe), `.mk-icon`.
59
+ - Full table: [reference/design.md](reference/design.md).
60
+
61
+ ## 4. Recipes
62
+
63
+ | Recipe | Markup | Key options (defaults) | Controls |
64
+ |---|---|---|---|
65
+ | `tabs` | root with `[data-mk-tab]` buttons and a `[data-mk-indicator]` (use `.mk-segmented`) | `orientation 'horizontal'`, `duration .45`, `onSelect(i, tab)` (event `mk:tab`) | `select(i)`, `index` |
66
+ | `counter` | element with a number | `to` (text), `from 0`, `duration .6`, `decimals`, `locale`, `prefix`, `suffix`, `trigger 'scroll'` | `play()`, `set(n)` |
67
+ | `details` | container of `<details>` (chevron `[data-mk-chevron]` in the summary) | — | — |
68
+ | `reveal` | element to uncover | `speed 'fast'\|'normal'\|'slow'\|'slower'`, `origin` (selector), `trigger 'manual'\|'load'` | `play(origin?)` |
69
+ | `reflow` | list with `[data-mk-item][data-flip-id]` | `duration .62` | `capture()` before the DOM change, `play()` after |
70
+ | `slot-text` | text element | `duration 300` ms, `stagger 45`, `maxTilt 5` | `set(text)` |
71
+ | `spring-cards` | root with `[data-mk-card]` children, each with `.mk-card__shadow` + image | `push .4`, `falloff 6`, `initialOffset 100`, `stiffness .05`, `friction .8`, `stagger .08`, `delay .4` | — |
72
+ | `curved-carousel` | root with `[data-mk-slide]`; optional `.mk-carousel__media`, `.mk-carousel__title` | `friction .85`, `throwBoost 2`, `wheel .2`, `startIndex 0`, `onSelect(i, slide)` (auto: `mk:select` event) | `next()`, `prev()`, `goTo(i)`, `index` |
73
+ | `pile` | `.mk-pile > .mk-pile__stack > .mk-pile__layer > .mk-pile__media`; back layers styled with `pileStyle(pileLayers(n, seed)[i])` | `threshold .25` | — |
74
+ | `words` | any text element | `by 'words'\|'chars'`, `trigger 'scroll'\|'load'\|'manual'`, `duration .8`, `stagger .1` | `reveal()`, `replace(mutate)` |
75
+ | `flow` | container; its children animate | `y 20`, `blur 16`, `duration .5`, `stagger .05`, `style 1\|2`, `trigger` | `play()` |
76
+ | `pop` | any element | `scale .7`, `duration 1.1`, `trigger` | `play()` |
77
+ | `fit-text` | one-line element inside a parent | `safety .995` | `refit()` |
78
+ | `stretch-footer` | `footer[data-mk="stretch-footer"]` containing `[data-mk-footer-title]`, plus a `[data-mk-phantom]` spacer after `<main>` | `lambda .9` | — |
79
+ | `radial-menu` | root with `[data-mk-toggle]` and `[data-mk-item]` direct children | `radius '3.6rem'`, `startAngle 1.5708`, `spacing .9` | `open()`, `close()`, `toggle()`, `isOpen` |
80
+ | `pill` | `.mk-pill` button, `.mk-stretch` link | CSS only | — |
81
+ | `mask-text` | any text | `by 'lines'|'words'|'chars'`, `y 100` (Siena titles 120), `duration 1.2`, `delay .1`, `stagger .02`, `trigger 'scroll'|'load'|'manual'`, `once` | `play()`, `hide()`, `split` |
82
+ | `cascade` | block with `[data-mk-cascade="rule|fade|rise"]` children (`rise` inside a clip) | `delay` (`data-mk-delay`), `step .03`, `duration 1.2`, `once`, `trigger` | `play()`, `reset()` |
83
+ | `text-roll` | a title | `trigger 'load'|'scroll'|'hover'|'manual'`, `delay`, `interval` (ms, Siena 6000), `duration 2`, `stagger .04` | `play()` |
84
+ | `letters` | a word | `effect 'scale'|'blink'`, `colors`, `autoplay` | `play()` |
85
+ | `parallax` | clipped frame with a picture (`[data-mk-parallax-target]`) | `amount 1`, `lerp 1`, `scroll` | `seek(p)` |
86
+ | `depth-grid` | grid container | `start 'top center'`, `end 'bottom bottom'`, `lerp 1`, `scroll` | `seek(p)` |
87
+ | `frame-zoom` | frame with one picture/video child | `start 'top bottom'`, `end 'bottom top'`, `lerp 1` | `seek(p)`, `open(on)` |
88
+ | `contrast` | header; sections with `data-mk-tone="light|dark"` | `sections`, `attribute 'data-mk-on'` | `tone` |
89
+ | `marquee` | strip whose first child is the group | `axis 'x'|'y'`, `speed 2` (s per item), `reverse` | `refresh()` |
90
+ | `card-dock` | `.mk-card-dock` with `.mk-card-dock__item` (> `a.mk-card-dock__card` + `.mk-card-dock__title`), optional `.mk-card-dock__cta`; `mk-card-dock--fixed` | `intro 1600` (ms spread on load, 0 skips) | `expand()`, `collapse()`, `expanded` |
91
+ | `snap-strip` | `.mk-snap-strip` with the cards as children | `autoplay`, `interval 5000`, `resume 2000`, `delay 500` (`data-mk-delay`) | `next()`, `previous()`, `index` |
92
+ | `fill-text` | any text | `from .2`, `start 'top 90%'`, `end 'center 40%'`, `scroll` | `seek(p)`, `progress` |
93
+ | `sortable` | root with `[data-mk-item][data-mk-id]` (optional `[data-mk-sortable-group="key"]` lists, `data-mk-sortable-locked`, `[data-mk-sort-handle]`) | `mutate true` (`false` in `useSortable`), `handle`, `autoScroll true`, `follow {1100, 38}`, `onReorder(detail)` (event `mk:reorder`, cancelable) | `cancel()`, `dragging` |
94
+
95
+ React hooks: `useCardDock`, `useSnapStrip`, `useFillText`, `useParticleHero`, `useMaskText`, `useCascade`, `useTextRoll`, `useLetters`, `useParallax`, `useDepthGrid`, `useFrameZoom`, `useContrast`, `useMarquee`, `useSortable`, `useTabs`, `useCounter`, `useDetails`, `useReveal`, `useReflow`, `useSlotText`, `useSpringCards`, `useCurvedCarousel`, `usePile`, `useWords`, `useFlow`, `usePop`, `useFitText`, `useStretchFooter`, `useRadialMenu`. Markup and options: each recipe's `README.md` (`src/recipes/<name>/`). Parameters and origin: [reference/recipes.md](reference/recipes.md).
96
+
97
+ ## 4b. Effects (v0.5)
98
+
99
+ | Name (`animate` / class `.mk-anim-<name>`) | Motion | Time |
100
+ |---|---|---|
101
+ | `out` / `out-left` / `out-bottom` | up 20 / left 64 / down 24 px, blur 16, fade | 0.5 s |
102
+ | `collapse` | folds height, padding and margin while blurring | 0.25 s |
103
+ | `highlight` | 4 px inner ring in primary-container | 1 s |
104
+ | `shake` / `shake-strong` | ±4 px / ±10 px | 0.5 s / 0.4 s |
105
+ | `fade`, `wipe`, `progress-in` | appear; a surface veil leaves to the right; appear late | 0.3 / 0.7 / 1 s |
106
+
107
+ `await animate(el, name)` replays even if already playing; `await leave(row, 'out-left' | 'collapse', { remove })` animates then removes (or hides). In React lists: `leave(item, 'out', { remove: false })`, then drop it from state. Classes: `.mk-loading` (+`--soft`), `.mk-shimmer`, `.mk-text-gradient` (theme), `.mk-text-rainbow`, `.mk-frost` (+ five `<i>`; `--top`, `--fixed`; `<Frost />`), `.mk-float` / `.mk-float-group`, `.mk-grain` (`--mk-grain`), `.mk-success` + `confetti(button)`. Reduced motion: loops stop, exits jump to the end, confetti does not draw.
108
+
109
+ ## 5. Motion functions (no instance)
110
+
111
+ Use them inside your own components; they all honour reduced motion and return the tween (or `null`).
112
+
113
+ | Function | Stepbro use |
114
+ |---|---|
115
+ | `flowIn(targets, { style, reverse, target })` / `flowOut(targets)` | lists, steps, settings entering / leaving |
116
+ | `stepChange(container, () => render next)` | wizard / onboarding steps (flow out → swap → flow in) |
117
+ | `flowInMenu`, `flowInPalette`, `flowInForm`, `flowInLanding`, `flowInContent` | menu options, swatches, form cards, landing, articles |
118
+ | `revealFrom(el, { origin, speed })` | a surface uncovered like a wave from the control that opened it |
119
+ | `toggleDetails(details, open)`, `installDetailsMotion(root)` | disclosures |
120
+ | `slideIndicator(indicator, target)` | any sliding selection indicator |
121
+ | `countTo(el, n)`, `revealCounter(el, n)`, `tweenNumber(a, b, cb)` | changing numbers, balances |
122
+ | `captureLayout` + `reflowLayout` | reorder / filter / add in lists |
123
+ | `enterMessages(bubbles)`, `fitComposer(textarea)`, `scrollToLatest(scroller)` | chat / comments |
124
+ | `capturePending(el)` + `morphPendingToReply(state, reply, list)` | a "thinking" placeholder turning into the answer (other frameworks) |
125
+ | `audioEnergy(stream, onEnergy)` | microphone level 0…1 for `chat-background` (never asks for the mic itself) |
126
+ | `channels(start, onChange)` + `spring()` / `eased()` | multi-property spring motion (liquid surfaces) |
127
+ | `trackScroll(el, { start, end, lerp, onUpdate })` | an element's 0→1 progress through the viewport (follows the page; `lerp < 1` smooths it) — build your own scroll effects |
128
+ | `motionSpring(stiffness, damping, mass?)` → `{ ease, duration }`, `springLinear(…)` | Motion/Framer springs (Dousan) as a GSAP ease or a CSS `linear()` |
129
+ | `sheetOpen`, `sheetClose`, `pushUnderlay`, `releaseUnderlay` | a sheet rising from the bottom while the page behind sinks |
130
+ | `morphOpen(surface, opener)`, `morphClose`, `morphCloseWithGhost`, `liquidOpen/Close` | any surface growing out of / folding into a control |
131
+ | `attachDragDismiss(surface, { onDismiss })`, `attachFloating`, `anchorSurface` | drag to dismiss, floating windows, anchored panels |
132
+ | `fadeBackdrop(dialog, 'in')`, `ghostOf`, `clearStyles` | backdrop tint, inert close copies, cleaning without GSAP measuring hidden nodes |
133
+
134
+ Curves: `EASE.signature` (the Stepbro line), `EASE.flow`, `EASE.fadeAway`, `EASE.blurAway`, `EASE.layoutReflow`…; CSS: `var(--mk-ease-signature)`, `--mk-ease-flow`, `--mk-ease-close`. Times: `TIMING.*`.
135
+
136
+ ## 6. Components
137
+
138
+ | Component | HTML / Astro | React |
139
+ |---|---|---|
140
+ | `dialog` | `<button data-mk-open="crear">` + `<mk-dialog id="crear" heading="…" [description] [variant="liquid|sheet"] [placement="anchor\|side"] [size="sm\|lg\|xl"] [float="key"] [shared="id"] [no-backdrop] [persistent] [no-drag]>` (children → body; `data-mk-part="footer"` stays below) | `<Dialog.Root variant placement float…><Dialog.Trigger/><Dialog.Content size><Dialog.Header><Dialog.Title/><Dialog.Close/></Dialog.Header><Dialog.Body/><Dialog.Footer/></Dialog.Content></Dialog.Root>` |
141
+ | `menu` | `<button data-mk-open="m">` + `<mk-menu id="m" label="…">` with `<button data-mk-item data-mk-value>`; listen `mk:select` | `<Menu.Root onSelect><Menu.Trigger/><Menu.Content><Menu.Item value/><Menu.Separator/></Menu.Content></Menu.Root>` |
142
+ | `toast` | `<mk-toaster>` once + `toast('Copiado', { origin: button, tone, action })` | `<Toaster />` once + `toast(…)` |
143
+ | `theme-picker` | `<mk-theme-picker [show-hidden] [themes="a,b"]>` | `<ThemePicker themes showHidden onChange />` inside `<ThemeProvider>` |
144
+ | `chat` | `<mk-chat [background] [mic] placeholder="…">` (history `[data-mk-message][data-mk-role]` inside); `mk:send` → `el.add('user', t)`, `el.pending()`, `el.reply(answer)` | `<Chat.Root onSend><ChatBackground /><Chat.Messages pending>{<Chat.Message role/>}</Chat.Messages><Chat.Confirm/><Chat.Composer/></Chat.Root>`; set the reply and `pending=false` in the same render |
145
+ | `chat-background` | `<mk-chat-background energy scale>` first inside a positioned box; `el.busy(true)` | `<ChatBackground energy scale />` |
146
+ | `nav` | `<div class="mk-shell"><main class="mk-shell__view">…</main><mk-nav class="mk-nav mk-nav--bonita" nav-style="bonita" [accent] [reveal] [storage-key]><div class="mk-nav__buttons"><a class="mk-nav__button" href aria-current="page">…</a></div></mk-nav></div>`; `mk:navigate` | `<Nav navStyle items active onSelect first actions accent reveal />` inside `.mk-shell` |
147
+ | `cursor` | `<mk-cursor>` once (chip over `[data-mk-cursor="Label"]`, `data-mk-cursor-shape="circle"`, `data-mk-cursor-icon`) · `<mk-cursor variant="dot" text arrows>` inside a zone (`data-mk-cursor-hide` hides it) | `<Cursor />` · `<Cursor variant="dot" text arrows zone />` |
148
+ | `island` | `<mk-island [class="mk-island--contained"] [entrance="false|manual"]>` with `.mk-island__backdrop[data-mk-island-close]`, `.mk-island__shell` (glass, header, `[data-mk-island-toggle="menu"]` buttons, `.mk-island__cta`), `.mk-island__content > div > [data-mk-island-panel="menu"]` with `[data-mk-island-item]` rows and `.mk-island-card`s; `mk:island` | `<Island brand cta panels={[{ name, label, icon, light, content }]} contained onChange />` |
149
+ | `steps` | `<mk-steps class="mk-steps">` + `[data-mk-step]` sections, `[data-mk-flow-item]`, `[data-mk-counter]`, `[data-mk-step-next/prev/goto]`; `el.busy(on)`, `mk:step` | `<Steps index onChange busy><ChatBackground /><Steps.Counter /><Steps.Step>…</Steps.Step></Steps>` |
150
+
151
+ Register elements with `defineElements()` (or a list of tag names). Engines for your own markup: `createDialog(dialogEl, opts)`, `createMenu(dialogEl, opts)`, `createToaster(hostEl)`, `createThemePicker(el)`, `createChat(el)`, `createChatBackground(el)`, `createNav(navEl, { style })`, `createSteps(el)`.
152
+
153
+ Dialog behaviour (Stepbro ModalHost): the opener turns into the dialog (0.7 s signature, colour handover 0.3 s, content from an 8 px blur) and it folds back on close (0.5 s, content blur 32 + radius 400, via an inert ghost so focus returns at once); without an opener it scales from .92. `data-mk-shared="id"` on a piece of the opener and of the dialog flies that text between them. Auto-resize 0.45 s; drag down > 100 px dismisses (also on phones); `float` lifts it into a movable, resizable window on desktop; full screen with radius 0 under 680 px. Events: `mk:open`, `mk:close` (cancelable), `mk:closed`. `[data-mk-close]` inside closes it. `open(null)` opens with no opener (scale-in).
154
+
155
+ Chat (PrettyChat): bubbles grow from their speaker's side (scale .5, blur 24, 0.7 s) while words rise; "Pensando…" only after 500 ms, then it morphs (Flip 0.7 s) into the reply; Enter sends, Shift+Enter breaks the line; the composer grows from 64 px (0.18 s). Nav styles: `clasica`, `bonita` (Money default), `dock`, `guapa`, `tasks`, `melon`, `tunnel` (`NAV_STYLES`), one glass bar under 680 px; `accent` = Stepbro's variant-1. Steps: flow out → swap → flow in (0.5 s), counter rolls, focus goes to the step's title. Sortable: lift 0.18 s, spring 1100/38, tilt up to 14°, neighbours slide 0.62 s, land 0.42 s; touch drags only from the handle; Space/arrows/Escape on the keyboard.
156
+
157
+ **Adding a component** (in the motion-kit repo): `pnpm new component <name>` scaffolds `src/components/<name>/{engine.ts, <name>.css, element.ts, react.tsx, README.md}`, a test, playground demos, the registry entry and the exports. Fill the engine (works on existing markup, defaults from `TIMING`, reduced motion, `destroy()` restores the DOM), then `pnpm test`. See [reference/components.md](reference/components.md).
158
+
159
+ ## 7. Rules
160
+
161
+ - Call `destroy()` when the element leaves (hooks, components and `autoInit()`'s returned function do it). Never leave instances running on removed nodes.
162
+ - Respect reduced motion: everything here already shows the final state under `prefers-reduced-motion: reduce`; custom motion must check `reduced()` too.
163
+ - Flip contract: measure before the DOM change, animate after it (`capture()`/`play()`, `<FlipGroup signature>`).
164
+ - Text that changes: `words.replace(() => …)`, `fit.refit()`, `slot.set(text)` — don't re-create.
165
+ - Elements inside a closed `<dialog>`: create `words` with `trigger: 'manual'` and call `reveal()` after `showModal()`.
166
+ - `stretch-footer` needs the page above it: `main { position: relative; z-index: 1; background: var(--mk-bg) }`.
167
+ - Theme: style with `--mk-*` tokens and controls; switch with `setTheme`/`setMode` (or `useTheme()`), never by swapping stylesheets.
168
+ - Override look with CSS variables (`--mk-button-bg`, `--mk-field-radius`, `--mk-swatch-size`, `--mk-tabs-indicator`, `--mk-shadow`…), not by editing package code.
169
+
170
+ ## 8. Choosing
171
+
172
+ - New app with Stepbro's look → `/css` + `/css/themes` + `/css/fonts`, `.mk-app`, controls, `theme-picker` in settings.
173
+ - Segmented filters / view switch → `.mk-segmented` + `tabs`. Totals → `counter` / `<AnimatedNumber>`.
174
+ - Lists that reorder → `reflow` / `<FlipGroup>`; lists the user reorders by dragging (tasks, kanban) → `sortable`; lists that appear → `flow` (or `flowIn`).
175
+ - Editorial headlines → `mask-text` (lines; `y 120` for film-title feel); credits / spec lists → `cascade`; a hero title that keeps alive → `text-roll` with `interval`; a playful word → `letters`.
176
+ - Photos in long pages → `parallax`; a gallery section → `depth-grid`; a video section → `frame-zoom`; header over dark/light sections → `contrast`; logos / festivals → `marquee` (+ `.mk-fade-x`).
177
+ - Site header for a product / landing → `island` (menu + cart share it); a product switcher pinned to a corner → `card-dock` (`--fixed`); phone carousels → `snap-strip`; a manifesto paragraph → `fill-text`; a hero with a living background → `particle-hero` from `/gl` (keep the text in HTML).
178
+ - Portfolio cards → `cursor` chip (`data-mk-cursor="View project"`); a draggable reel or full-bleed hero → `cursor` dot.
179
+ - Glass actions or tags over photos / colour → `.mk-glass` (+ `.mk-float-group` for floating tags); bottom fade over scrolling content → `.mk-frost`; a cinematic hero → `.mk-grain`.
180
+ - Removing rows → `leave(row, 'out-left' | 'collapse')`; form error → `aria-invalid="true"` (vibrates) or `animate(el, 'shake')`; success → `.mk-success` + `confetti(button)`; loading cards → `.mk-loading`; AI / premium labels → `.mk-text-gradient`.
181
+ - Project detail over the page → `dialog` with `variant="sheet"` and `data-mk-underlay` on the page; mobile menu toggle → `.mk-burger`; "Explore" CTA → `.mk-button--roll`.
182
+ - App frame → `.mk-shell` + `nav` (let the user pick the style; `storageKey` remembers it). Assistant / support chat → `chat` + `chat-background`. First-run setup → `steps` (with `ChatBackground` and `busy` while saving).
183
+ - FAQ / settings groups → `details`. Date/time popovers → `revealFrom` from the field.
184
+ - Language switch on a title → `slot-text`.
185
+ - Portfolio hero → `spring-cards` + a giant `fit-text words` title; featured projects → `curved-carousel`; archive → `pile`; last screen → `stretch-footer`; corner quick actions → `radial-menu`.
186
+ - Film navigation → `ticket-menu` or `<Menu.Root ticket>` / `<mk-menu ticket>`; preserves menu focus, selection and keyboard handling.
187
+ - Circular archive → `infinite-reel` (wheel, drag, keyboard, axis x/y); interactive card grid → `repel-bento`; testimonials → `rotator` (8 s, pause on focus/hover).
188
+ - Numeric experience/time selector → `drag-ruler` (role slider, 60 px/unit, step .1); a shared active indicator in navigation → `dock-pill` (spring 380/30). Navigation remains the app's responsibility.
189
+ - GPU image transitions → `createLiquidBlend`; metal study → `createMatcap`; film processing → `createShaderGrain` / `createShaderBlur`, all from `/gl`. React hooks `useLiquidBlend`, `useMatcap`, `useShaderGrain`, `useShaderBlur` are in `/react`. Import explicitly: these are not autoInit recipes. Retain accessible HTML images and set dimensions on the host. See [reference/recipes.md](reference/recipes.md) and each recipe README for the source parameters and declared adaptations.
@@ -0,0 +1,86 @@
1
+ # Components
2
+
3
+ A component brings its own markup and styles; recipes animate yours. Each one lives in `src/components/<name>/`:
4
+
5
+ | File | Job |
6
+ |---|---|
7
+ | `engine.ts` | `create<Name>(root, options) → { …controls, destroy }`. Vanilla. Works on existing markup; creates only temporary internals (ghosts, generated children) and removes them. Defaults from `core/timing.ts`; reduced motion gives the end state; `destroy()` restores the DOM. |
8
+ | `<name>.css` | Styles on `--mk-*` tokens; parts as `[data-mk-part="…"]`; own variables `--mk-<name>-*`. |
9
+ | `element.ts` | `<mk-<name>>` custom element on `MkElement` (light DOM). `static options` maps attributes (kebab-case) to engine options: `string`, `number`, `boolean` (presence), `list` (comma separated), `json`. The engine is created on connect, recreated on attribute change, destroyed on disconnect. |
10
+ | `react.tsx` | `'use client'` component that renders the root and creates the engine in a layout effect; callbacks read through refs; `className`, `style`, `unstyled`. |
11
+ | `README.md` | Markup (HTML and React), options/attributes, events, CSS variables, accessibility, origin of every parameter. |
12
+
13
+ `registry.json` lists it (`kind: "component"`, `files`, `element`, `react`, `requires`, `since`). The CLI (`list`, `add --elements --react`), the CSS build (`dist/css/components/<name>.css`, included in `/css`), the playground and this skill read from there. `pnpm test` checks that every component has the five files.
14
+
15
+ ## Add one
16
+
17
+ ```sh
18
+ pnpm new component demo-card # from the repo root
19
+ ```
20
+
21
+ It writes the five files, `test/components/demo-card.test.ts`, `apps/playground/src/demos/demo-card/{Vanilla.astro,React.tsx}`, the registry entry (`since` = next minor) and the exports in `src/index.ts`, `src/react/index.ts` and `src/elements/index.ts`. Then:
22
+
23
+ 1. Write the engine from the kit (`Stepbro - Kit de replicacion/…`): every duration in `TIMING`, curves in `EASE`.
24
+ 2. Fill the README and the description in `registry.json`.
25
+ 3. Extend the test (behaviour + destroy restores markup, with and without reduced motion).
26
+ 4. `pnpm test`, `pnpm dev` and check the playground page in light and dark.
27
+
28
+ ## theme-picker (v0.2)
29
+
30
+ - Engine `createThemePicker(root, { themes, showHidden, labels, onChange })` → `{ select(id), destroy }`. Generates `button.mk-swatch[data-mk-swatch]` for each theme unless the root already has swatches; `aria-pressed` follows `getTheme()`; clicking calls `setTheme`.
31
+ - `<mk-theme-picker show-hidden themes="a,b">`, `<ThemePicker />` (+ `<ThemeProvider>` / `useTheme()`).
32
+ - Motion: entrance `flowInPalette` (scale .5, blur 4, .1 s, 10 ms). Ball: circle → 32 px radius on hover (+ saturate 1.5) → 16 px when chosen, 0.5 s `--mk-ease-swatch`; inner ring; two blurred lights drift (3.5 s, background 4.5 s, `--mk-ease-swatch-light`).
33
+ - Variables: `--mk-swatch-size` 72px, `--mk-swatch-gap` 12px, `--mk-swatch` per ball.
34
+
35
+ ## dialog (v0.3)
36
+
37
+ - Engine `createDialog(dialog, options)` on a native `<dialog>` → `{ open(origin?), close(), toggle(), float(), dock(), isOpen, isFloating, element, destroy }`. `open()` without argument grows from the focused control; `open(null)` scales in from .92.
38
+ - Options: `variant` morph | liquid, `placement` center | anchor | side, `backdrop`, `closeOnBackdrop`, `drag`, `float` (key), `autoResize`, `triggers` (`[data-mk-open="<id>"]`), `modal`, `labels`, `onOpen`, `onClose`.
39
+ - Parts: `[data-mk-part]` header, title (names the dialog), description, actions, body (scrolls), footer, close, handle (floating). `[data-mk-close]` anywhere inside closes.
40
+ - Motion (TIMING.dialog): open 0.7 s signature, colour 0.3 s, content blur 8 → 0; close 0.5 s through an inert ghost (content toward the opener on a 2 s path cut at 0.5 s, blur 32, radius 400, fade); fallback .92 / 0.35 s; backdrop 0.5 s signature linear(); auto-resize 0.45 s. Liquid (TIMING.liquid): springs 144/14 (kick 2400) open, 130/18 close. Drag: follow ×0.5, scale 1 − y/400, dismiss > 100 px, restore elastic.out(.5,.5) 0.5 s. Floating: spring 1100/38, glide decay 0.18 s, min 320 × 240, margin 8, `mk-float.<key>`.
41
+ - Element `<mk-dialog>` builds the parts from `heading` / `description` / `shared` / `float`; React `Dialog.*` compound components.
42
+
43
+ ## menu (v0.3)
44
+
45
+ - `createMenu(dialog, { closeOnSelect, onSelect, … })`: a dialog anchored to its button, transparent backdrop, no drag or auto-resize, content not grown as a group; options `[data-mk-item]` cascade with `flowInMenu` (opacity only, so they can take focus). role menu / menuitem; arrows, Home, End; Enter/Space on non-button items. Event `mk:select` (value = `data-mk-value` or text).
46
+ - `<mk-menu id label keep-open>`; React `Menu.Root/Trigger/Content/Item/Separator`.
47
+
48
+ ## toast (v0.3)
49
+
50
+ - Store (`components/toast/store.ts`): `toast(message, { origin, duration 4000, tone success|error|info|neutral, action, icon })`, `toast.dismiss(id)`, `toast.clear()`, at most 5 live; timers pause while the deck is open.
51
+ - `createToaster(host, { labels })` draws the cards (the one component that creates markup): stack peek 16 / −5 % / 3 drawn, hover expands (springEase .55, 60 ms) and collapses after 150 ms; origin morph by channels; letters 0.4 s / 20 ms after 0.225 s; exit 24 px + blur 4 in 0.3 s; swipe up 48 px or wheel 80 px dismisses. Host is a manual popover (top layer, above dialogs), role status / aria-live polite.
52
+ - `<mk-toaster>`, React `<Toaster />`; `toast` is exported from `.`, `/elements` and `/react` (one store).
53
+
54
+ ## chat-background (v0.4)
55
+
56
+ - `createChatBackground(root, { energy 0, scale 1 })` → `{ setEnergy, setScale, busy(on), energy, scale, destroy }`; `chatBackgroundOf(el)` finds a running one. Writes `--mk-chat-orbit-1` (10 − 3.5·e s), `--mk-chat-orbit-2` (15 − 4.5·e s), `--mk-chat-blob-opacity` (.5 + .15·e), `--mk-chat-scale` ((1 + .1·e)·scale); adds the grain (`.mk-chat-background__noise`, SVG turbulence). `busy(true)`: energy 1.6 / scale 1.8 in 0.8 s power2.out; back 0.6 s power2.inOut.
57
+ - `audioEnergy(stream, onEnergy)` → `{ stop }`: FFT 256, smoothing .3, −90…−10 dB, lowest 40 % ×3, attack .8, release .15. The app asks for the microphone.
58
+ - `<mk-chat-background energy scale>` (attributes update in place), `<ChatBackground energy scale />`.
59
+
60
+ ## chat (v0.4)
61
+
62
+ - `createChat(root, { animateExisting, clearOnSend, labels: { thinking }, onSend })` over `[data-mk-part="messages"]` (+ composer, input, send, mic) → `{ add(role, text), pending(), reply(el | text), confirm(bar), setRecording(on), scrollToLatest(), adopt(el), messages, destroy }`.
63
+ - A MutationObserver animates any new `[data-mk-message]` (`enterMessages` + `scrollToLatest`); history stays still unless `animateExisting`. `pending()` shows `[data-mk-pending]` after 500 ms; `reply` morphs it (Flip 0.7 s, `scale: false`, text width fixed, words rise) or enters normally. Enter / submit → cancelable `mk:send` → `onSend`. `capturePending` + `morphPendingToReply` for frameworks.
64
+ - `<mk-chat background mic placeholder thinking animate-existing>` builds its own list and composer when empty. React `Chat.Root/Messages(pending)/Message(role)/Composer/Confirm` + `useChat()`; `Chat.Messages` is a class component (getSnapshotBeforeUpdate measures the placeholder before React swaps it).
65
+ - Variables: `--mk-chat-radius` 40, `--mk-chat-bubble-radius` 32, `--mk-chat-user-bg/-fg`, `--mk-chat-assistant-bg/-fg`.
66
+
67
+ ## nav (v0.4)
68
+
69
+ - `createNav(nav, { style, accent, reveal, storageKey, labels, onSelect })` → `{ select(i | value), setStyle(name), setAccent(on), index, style, items, destroy }`. Items: `.mk-nav__buttons .mk-nav__button` (value = `data-mk-value` or `href`); current = `aria-current="page"`; click → cancelable `mk:navigate` (`detail.originalEvent` for SPA routers). `revealShell(nav)` uncovers `.mk-shell` from the first button.
70
+ - CSS: Stepbro's seven `.app-nav.style-N` as `.mk-nav--{clasica|bonita|dock|guapa|tasks|melon|tunnel}`, `variant-1` → `.mk-nav--accent`, mobile-style-1 under 680 px, shell `.mk-shell` / `__view` / `__header` / `__title` / `__content`. Names hidden by a style stay readable (clip, not display none). Current icon fills (`mk-nav-icon` 125 ms, `--mk-icon-fill` registered with `@property`).
71
+ - `<mk-nav nav-style accent reveal storage-key>` (style and accent change in place), `<Nav items active onSelect navStyle accent reveal first actions />`.
72
+
73
+ ## steps (v0.4)
74
+
75
+ - `createSteps(root, { index, duration .5, animateFirst, target '[data-mk-flow-item]', onChange })` → `{ next, prev, goTo, busy(on), index, count, steps, destroy }` (promises settle on the last requested step). Hidden steps get `hidden`; items flow out/in (opacity only on the way in so the title can take focus); `[data-mk-counter]` rolls (0.9 s); focus to `[data-mk-step-title]` or the first heading; `mk:step`; `busy` → `aria-busy` + the chat background inside.
76
+ - `<mk-steps index>` (index changes move), `<Steps index onChange busy>` + `Steps.Step` (direct children) + `Steps.Counter`.
77
+
78
+ ## Gotcha: GSAP and hidden elements
79
+
80
+ `gsap.set(el, { clearProps: 'transform' })` measures the element first; an element with no width (a closed dialog, display: none) is moved to the end of the document and back to be measured, which shifts whitespace and disconnects custom elements inside. Engines clear their inline styles with `clearStyles(elements, properties)` instead when the element may be hidden.
81
+
82
+ Reading a transform (`gsap.getProperty(el, 'y')`, Flip) also writes `translate/rotate/scale: none` inline; engines that only measure clear those afterwards (see `sortable`'s `untouch`).
83
+
84
+ ## Ticket menu (v0.8)
85
+
86
+ Use `<mk-menu ticket label="Sections">` or `<Menu.Root ticket>` for Siena's ticket presentation. The existing menu engine still handles roles, roving focus, disabled items, selection, Escape, focus return and the dialog lifecycle. Direct factory: `createTicketMenu(dialog, MenuOptions)`; declarative: `data-mk="ticket-menu"`. It uses a local clip-path opening (.9 s) and rows from 150% (1.1 s, .02 stagger), adapting Siena's grid-row panels. The underlying `createMenu` also accepts `ticket: true`.
@@ -0,0 +1,92 @@
1
+ # Design line (Stepbro)
2
+
3
+ Values from the Stepbro replication kit (`tasks/source/frontend.css`, `tasks/themes/theme.{light,dark}-*.css`, runtime observations of 2026-10-13). Colour values are reproduced; no Stepbro code is shipped.
4
+
5
+ ## Colour
6
+
7
+ Every theme defines the 37 Material 3 roles `--md-sys-color-<role>` in light and dark:
8
+ background, on-background, surface, surface-dim, surface-bright, surface-container-lowest, -low, (plain), -high, -highest, on-surface, surface-variant, on-surface-variant, inverse-surface, inverse-on-surface, outline, outline-variant, shadow, scrim, surface-tint, primary, on-primary, primary-container, on-primary-container, inverse-primary, secondary, on-secondary, secondary-container, on-secondary-container, tertiary, on-tertiary, tertiary-container, on-tertiary-container, error, on-error, error-container, on-error-container.
9
+
10
+ Semantic layer (declared on `:root` and every `[data-mk-theme]`):
11
+
12
+ | `--mk-*` | Role |
13
+ |---|---|
14
+ | `bg` | background |
15
+ | `surface` | surface |
16
+ | `surface-1` … `surface-5` | surface-container-lowest, -low, container, -high, -highest |
17
+ | `text` / `muted` | on-surface / on-surface-variant |
18
+ | `accent` / `on-accent` | primary / on-primary |
19
+ | `accent-soft` / `on-accent-soft` | primary-container / on-primary-container |
20
+ | `secondary-soft` / `on-secondary-soft` | secondary-container / on-secondary-container |
21
+ | `outline` / `outline-soft` | outline / outline-variant |
22
+ | `error` / `on-error` | error / on-error |
23
+ | `error-soft` / `on-error-soft` | error-container / on-error-container |
24
+ | `scrim` / `shadow-color` | scrim / shadow |
25
+
26
+ Note: Stepbro's dark palettes often set `primary` to white (neogreen dark: `#ffffff`) and put the colour in `primary-container` (`#00f46b`). That is why the default `.mk-button` uses `accent-soft`.
27
+
28
+ ## Themes
29
+
30
+ | Id | Name | Shown by Stepbro | Seed (dark surface-tint) |
31
+ |---|---|---|---|
32
+ | blue | Blue | yes | #b3c5ff |
33
+ | purple | Purple | yes | #d2bcff |
34
+ | neogreen | Neogreen (default; Money's default) | yes | #00e563 |
35
+ | red | Red | yes | #ffb4ac |
36
+ | pink | Pink | yes | #f7acff |
37
+ | pastel-pink | Pastel Pink | yes | #ffcee8 |
38
+ | brown | Brown | yes | #ffb783 |
39
+ | super-blue | Super Blue | yes | #bcc2ff |
40
+ | gray | Gray | yes | #c6c6c6 |
41
+ | oled | Oled (Tasks' default; dark only, light uses Black) | yes | #9fb8e4 |
42
+ | black, green, orange, cold-blue, modern-1, funny, new, new2, black-green | — | hidden | see `THEMES` |
43
+
44
+ Selectors: light `[data-mk-theme="id"]`; dark `[data-mk-theme="id"][data-mk-mode="dark"]` or inside `[data-mk-mode="dark"]`; system dark through `prefers-color-scheme` unless `data-mk-mode="light"`. Default palette (neogreen) sits in `:where(:root)` with zero specificity.
45
+
46
+ Generator: `generateTheme(seed, { variant, contrast })` (Material Color Utilities 0.3; variants tonal-spot, vibrant, expressive, neutral, content, fidelity, monochrome) → same 37 roles; `themeCss(id, palettes)` → same selectors; `applyTheme(seed, { id })` injects and selects it.
47
+
48
+ ## Type
49
+
50
+ | Role | Large | Medium | Small |
51
+ |---|---|---|---|
52
+ | display | 57/64, 400, −0.25 | 45/52, 400 | 36/44, 400 |
53
+ | headline | 32/40, 400 | 28/36, 400 | 24/32, 400 |
54
+ | title | 22/28, 400 | 16/24, 500, .15 | 14/20, 500, .1 |
55
+ | body | 16/24, 400, .5 | 14/20, 400, .25 | 12/16, 400, .4 |
56
+ | label | 14/20, 500, .1 | 12/16, 500, .5 | 11/16, 500, .5 |
57
+
58
+ Tokens `--md-sys-typescale-<role>-<size>-{font,size,line-height,weight,tracking}`; classes `.mk-type-<role>-<size>`. Stepbro's bundle writes weights as `400px`; here they are plain numbers (adaptation). Fonts: DM Sans (`--mk-font`), Geist Mono (`--mk-font-mono`), Material Symbols Rounded (`.mk-icon`).
59
+
60
+ ## Shape, space, motion
61
+
62
+ - Radii: xs 8, sm 12, md 16, lg 24, xl 32 (bubble), 2xl 40 (dialog, observed 400 × 40 on desktop), 3xl 48 (chat window), 4xl 64 (mobile dock), full 999. Mobile dialogs go full screen with radius 0 under 680 px.
63
+ - Space: `--mk-space-n` = n × 4 px (1…16).
64
+ - CSS curves: `--mk-ease-signature`, `--mk-ease-tint` (same), `--mk-ease-flow` (`cubic-bezier(.25,.1,.25,1)`), `--mk-ease-close`, `--mk-ease-blur-away`, `--mk-ease-nav-radius` (`.38,.49,0,2`), `--mk-ease-nav-move` (`.38,.49,0,1.2`), `--mk-ease-swatch` (`.38,.49,0,1.5`), `--mk-ease-swatch-light` (`.11,1,0,1`), `--mk-ease-slot`. Durations `--mk-dur-fast .25s`, `--mk-dur .45s`, `--mk-dur-slow .7s`.
65
+
66
+ ## Controls (Stepbro's Material Web overrides)
67
+
68
+ | Control | Values |
69
+ |---|---|
70
+ | Button | 40 px, 24 px sides, capsule; filled = primary-container; small 32 px, 16 px sides, radius 12; state layer 8 % hover, 10 % focus/press |
71
+ | Field | filled, surface-container-high, radius 16, no active indicator, 56 px; error → error-container |
72
+ | Filter chip | capsule, no outline, container-high; selected primary-container / on-primary-container |
73
+ | Checkbox | radius 6 |
74
+ | Menu | radius 16 |
75
+ | Switch | 52 × 32 track, thumb 16 → 24 px |
76
+
77
+ ## Glass (Dousan Miao, v0.5)
78
+
79
+ `.mk-glass` on any button, link or chip; one element, no JS (`css/controls/glass.css`). Dousan's layers:
80
+
81
+ | Layer | Value |
82
+ |---|---|
83
+ | Surface | `linear-gradient(-75deg, a, b, a)`: a 5 % / b 22 % white (dark: 2 % / 8 %); `backdrop-filter: blur(4px)`; inner/outer shadows (`--mk-glass-shadow`, `-hover`, `-active`); radius 999vw |
84
+ | Ring (`::before`) | `conic-gradient(from var(--mk-glass-angle-1), …)` + vertical gradient, masked to a `clamp(1px, .0625em, 4px)` border (`mask-composite: exclude`); angle −75° → −125° on hover, 0.5 s (`@property`) |
85
+ | Shine (`::after`) | band `transparent → 55 % white 40–50 % → transparent` at `--mk-glass-angle-2` (−45°), `200% 200%`, `mix-blend-mode: screen`; position 0 % → 25 % hover → `50% 15%` and −15° pressed, 0.5 s `cubic-bezier(.25,1,.5,1)` |
86
+ | Shadow (`.mk-glass__shadow`, optional) | blurred gradient ring 22 % → 10 % black (dark 85 % → 55 %) under the button; only its lower part shows |
87
+ | Press | `scale(.95)` on Dousan's 400/10 spring (`--mk-ease-press`, `--mk-dur-press`) |
88
+
89
+ Light/dark through `light-dark()` (themes set `color-scheme`). Without `backdrop-filter`: an almost opaque background. It is layered CSS, not refraction — like the original.
90
+
91
+ Related tokens: `--mk-ease-glass`, `--mk-ease-sheet`, `--mk-ease-siena`, `--mk-ease-quint`, `--mk-ease-press` / `--mk-dur-press` (400/10), `--mk-ease-success` / `--mk-dur-success` (260/18). `motionSpring(k, c)` and `springLinear(k, c)` turn any Motion spring into a GSAP ease or a CSS `linear()`.
92
+
@@ -0,0 +1,172 @@
1
+ # Recipe parameters and origin
2
+
3
+ Values were read from the public bundles of spencergabor.com (Nuxt 3, analysed with REA on 2026-10-14) and Stepbro Tasks/Money, then re-implemented. No third-party code is shipped.
4
+
5
+ ## Shared
6
+
7
+ | Name | Value | Origin |
8
+ |---|---|---|
9
+ | Spring | `v = v·friction^dt + (target − x)·stiffness·dt; x += v·dt`, stiffness .05, friction .8 | spencergabor.com `useSpring` |
10
+ | Frame loop | one `gsap.ticker` callback, `dt = Δms / (1000/60)`, capped at 3; recipes subscribe only while visible | spencergabor.com shared rAF loop |
11
+ | `--mk-ease-elastic` | `linear(0, .5737 7.6%, … 1.0011 81.26%, 1)` | spencergabor.com `--ease-out-elastic` |
12
+ | `--mk-ease-elastic-hover` | `linear(0, .2178 2.1%, … .9981 86.66%, 1)` | spencergabor.com `--ease-out-elastic-100-30` |
13
+ | `ELASTIC` (GSAP) | `elastic.out(1, 0.45)` | visual match of `--mk-ease-elastic` |
14
+ | `mk-signature` | `M0,0 C0.308,0.19 0.107,0.633 0.288,0.866 0.382,0.987 0.656,1 1,1` | Stepbro signature curve |
15
+ | `mk-flow` | `M0,0 C0.25,0.1 0.25,1 1,1` | Stepbro flowSoft |
16
+ | `mk-close` | `.56,.27,0,1` | Stepbro close fade |
17
+ | `--mk-shadow` | `0 9px 21px rgb(0 0 0/.1), 0 38px 38px rgb(0 0 0/.09), 0 85px 51px rgb(0 0 0/.05)` | spencergabor.com `--shadow` |
18
+
19
+ ## spring-cards
20
+
21
+ - Start offset: random ±100 px, released after 100 ms.
22
+ - Pointer push: `dx = (px − cx)/innerWidth·2`, `dy = (py − cy)/innerHeight·2`, `k = max(0, 1 − hypot(dx, dy))^6 · .4`, `push += movement·k`, `push *= .9^dt`.
23
+ - CSS: `rotate: calc(tilt − velocity·.25deg)`; tilts 2°, −3°, 6°, −2°; vertical offsets 5 %, −6 %, 5 %, −4 %.
24
+ - Entrance: `scale 0 → 1`, 1 s, elastic, `i·.08s + .4s`.
25
+ - Portfolio change: 4:3 cards and −1.5 % overlap (Spencer: square, −6 %) so text on covers stays readable.
26
+
27
+ ## curved-carousel
28
+
29
+ - Friction .85; drag velocity = pointer movement per frame; on release `v·2`, rest `x − v·f/(1−f)`, snap to the nearest slide, approach `x += (target − x)(1 − f^dt)`.
30
+ - Tap: under 6 px of travel (Spencer: < 10 px and < 100 ms).
31
+ - Wheel: `v −= deltaX·.2` when horizontal; snap 150 ms after the last wheel event.
32
+ - Arc: `rotate: t·.03deg`, origin `50% 70vw`, `translateY: cos(t·π/1000)·−3rem + 3rem`, title opacity `cos(t·π/600)`; `t` = slide centre − viewport centre in px.
33
+ - Entrance: media from `scale .8; translate 0 20%`, 1.4 s elastic, delay `sin(k·π/2)·.12s`.
34
+
35
+ ## pile
36
+
37
+ - Layers: `rotation = rand(2,5)·(±1 alternating)`, `offsetX = rand(4,10)·random sign`, `offsetY = (n − i)·5`; seeded (mulberry32) so SSR and client match.
38
+ - CSS: `rotate: rot·.6deg`, `translate: px·.3% py·.3%` (Spencer: .5deg / .25%).
39
+ - In view: media `scale .7 → 1`, shadow `0 → 1`, .8 s elastic; hover 1.01/1.02/1.03, active .97/.98/.99.
40
+
41
+ ## words
42
+
43
+ - `scaleY 0 → 1`, `yPercent 25 → 0`, origin bottom, .8 s, `ELASTIC`, 100 ms between words, 60 ms between letters.
44
+ - Spencer did this with CSS transitions and `@starting-style`; the package uses GSAP SplitText so reduced motion and text replacement work.
45
+
46
+ ## flow (Stepbro)
47
+
48
+ - From `y −20`, `blur(16px)`, opacity 0 → 0, 0.5 s, 50 ms stagger, `mk-flow`.
49
+
50
+ ## pop
51
+
52
+ - `scale .7 → 1` + opacity, 1.1 s, `ELASTIC`.
53
+
54
+ ## fit-text
55
+
56
+ - Measures at 100 px, sets `font-size = 100 · available · .995 / width`; refits on parent width changes only.
57
+
58
+ ## stretch-footer
59
+
60
+ - Phantom of 100dvh at the end of the flow; `g = lerp(g, scrollY, 1 − e^(−.9·dt))`.
61
+ - `scaleY = map(g, phantomTop − vh, phantomTop, 0, 1)`; `offsetY = map(…, vh − titleBottom, 0)`, both clamped.
62
+
63
+ ## radial-menu
64
+
65
+ - Item position: `translate(sin(i·.9 + 1.5708)·−r, cos(i·.9 + 1.5708)·r)`, r = 3.6rem (Spencer: spacing .75, start 1.25).
66
+ - Open: `scale .5 → 1`, .5 s elastic, `i·.05s`; close: .15 s `cubic-bezier(.55,.055,.675,.19)`.
67
+
68
+ ## pill
69
+
70
+ - Hover `scale 1.05` + shadow, active `.95`, .8 s elastic; `.mk-stretch` hover `scaleY(1.2)`, 1 s elastic-hover.
71
+
72
+ ## Stepbro v0.2 (Tasks `especificacion-replicacion.json`, Money `replication-catalog.json`)
73
+
74
+ | Name | Value | Kit id |
75
+ |---|---|---|
76
+ | `mk-fade-away` / `mk-blur-away` | `.56,.27,0,1` / `.37,.35,0,1` | modal-close-origin |
77
+ | `mk-chat-scroll` | `M0,0 C0.308,0.19 0.318,0.567 0.5,0.8 0.593,0.921 0.656,1 1,1` | chat-scroll |
78
+ | `mk-layout-reflow` / `mk-sortable-land` | spring path `FA(.52)` / `FA(.62)` (stiffness 5.6, sampled 72 steps) | task-layout, task-drag |
79
+ | `--mk-ease-signature` | Stepbro's own `linear()` sampling of the signature (its backdrop tint) | tasks-modal-backdrop |
80
+
81
+ ### flowIn / flowOut / stepChange
82
+
83
+ - In: `y −20 → 0` (style 2: `scale .9 → 1`), blur 16 → 0, opacity, 0.5 s, 50 ms, `mk-flow`; `reverse`, `target` selector. Out: `y 0 → 20`, blur 0 → 16, opacity 1 → 0.
84
+ - Step change: await out → mutate → next frame → in; 0.5 s (0.45 in verification dialogs).
85
+ - Named: menu `style 2`, .9, blur 4, .35 s, 40 ms, delay .1, reverse · palette .5, blur 4, .1 s, 10 ms · form `[data-flow-item]` .9, blur 8, .35 s, 40 ms · landing .85, blur 24, .4 s, 50 ms, delay .15 · content `[data-flow], .markdown-content > *` style 2, .6 s, 60 ms.
86
+
87
+ ### tabs
88
+
89
+ - Indicator to the selected tab's box (Flip.fit in the original), 0.45 s, signature; centres a scrolling list; reduced: instant.
90
+
91
+ ### counter
92
+
93
+ - `numberSoft` (= `mk-flow`), 0.6 s (prices 0.7 s), from the value shown. Onboarding reveal: rAF, 0.9 s, `1 − (1 − p)^3`.
94
+
95
+ ### details
96
+
97
+ - Height `0 ↔ scrollHeight` + content opacity, 0.32 s, signature; chevron 0 ↔ 180°, 0.25 s; `clearProps` after opening so nested content can grow.
98
+
99
+ ### reveal
100
+
101
+ - Radial gradient veil from the origin's centre (else last pointer, else the centre), progress 0 → 100 % with `power2.out`, then the veil fades with `power1.out`; overshoot 2. Speeds (travel/fade): fast .5/.25, normal .7/.35, slow 1.05/.5, slower 1.6/.75 s.
102
+
103
+ ### reflow
104
+
105
+ - `Flip.from` on `data-flip-id`, 0.62 s `mk-layout-reflow`, `zIndex 3`; new items `scale .94 → 1` + opacity in 0.372 s.
106
+
107
+ ### slot-text
108
+
109
+ - Per changed letter: out and in by one line height, 300 ms `cubic-bezier(.34,1.56,.64,1)`, 45 ms cascade, 50 ms exit offset, letter fade 280 ms, deterministic tilt ≤ 5° (noise `sin((i+1)·12.9898 + c·78.233)·43758.5453`), width eases `cubic-bezier(.2,0,0,1)` (≥ 140 ms); trailing letters collapse at 55 % of their time.
110
+
111
+ ### chat (motion functions)
112
+
113
+ - Bubble `scale .5 → 1`, blur 24 → 0, autoAlpha, 0.7 s signature, 60 ms; origin right (user) / left (assistant). Words `y 16 → 0`, blur 6 → 0, 0.25 s, 50 ms, `back.out(1)`; skip `table`/`pre`; whole block if splitting grows the text by > ½ line.
114
+ - Composer: height → scrollHeight (min 64), 0.18 s `power2.out`. Scroll to bottom: 0.35 s `mk-chat-scroll`.
115
+
116
+ ### sortable
117
+
118
+ - Stepbro Tasks `useDragReorder` ("transform" mode). Threshold 6 px; floating copy (cloned into the item's own list, `position: fixed`, so the list's CSS still styles it) follows a spring 1100/38 with mass `clamp(√(w·h / 24000), 1, 2.5)`; tilt spring 170/13 toward `14·tanh(torque / 14)`, torque from smoothed velocity (×2e-3) and acceleration (×45e-5) about the grab point (arm Y ≥ .4). Lift 0.18 s `power2.out`, scale `1 + .03·lift`, shadow `0 12·lift 32·lift rgb(0 0 0 / .22·lift)`.
119
+ - Neighbours: `y` by the item's height + the space to its neighbour, 0.62 s `mk-layout-reflow` (FA .52); insertion index by the pointer against item midpoints; group by pointer x (then y). Land 0.42 s `mk-sortable-land` (FA .62) to the slot, cross-fade 0.277 s `power1.inOut` to a copy styled by the destination list.
120
+ - Auto-scroll: scrollable ancestors (and the page) within 56 px of an edge, `ceil(16 · min(1, d / 56))` px a frame. Touch (`pointer: coarse`): handle only. Escape / pointercancel: back. Keyboard (added): Space/Enter, arrows, Escape; reflow 0.62 s; `aria-live` announcements.
121
+
122
+ ## iyo.ai (v0.7, REA research `REA - iyo.ai`)
123
+
124
+ ### island (navbar `nav`/`d3`)
125
+
126
+ - Shell fixed top 2rem, 23.5rem wide (28rem ≥ 1920 px; 1rem / 5vw side margins ≤ 767 / 479 px), radius 3em → 2em (0.4 s `cubic-bezier(.4,0,.2,1)`, closing 0.45 s late). Glass `blur(20px)` on `#38383899` + `linear-gradient(141deg, #a9c2f214, #62718c24)`.
127
+ - Content `grid-template-rows 0fr → 1fr`, 0.6 s `cubic-bezier(.625,.05,0,1)`, 0.125 s late. Backdrop black 0 → .4, 0.7 s `cubic-bezier(.5,.5,0,1)`, 0.1 s late. Rows `opacity 0 → 1`, `translateY 3em → 0`, 0.6 s, delay `0.15 + i·0.025` s.
128
+ - Entrance `yPercent −300 → 0`, 1.8 s (no ease given, so GSAP's `power1.out`). Card hover: siblings 20 % (0.15 s ease-out), picture 1.04 (0.4 s, back 0.3 s `cubic-bezier(.25,.46,.45,.94)`). Closes on a scroll > 1 px after wheel / touchmove.
129
+
130
+ ### card-dock (`product-cards`, `PM`)
131
+
132
+ - Stack, from the end: `translateY(90 % / 180 % / 270 % / 360 %) scale(.93 / .87 / .8 / .73)`; open 0.8 s, close 0.64 s, `cubic-bezier(.48,.01,0,1.08)`. Card 4rem → 4.5rem, 0.6 s `cubic-bezier(.77,0,.175,1)`; surface `#38383899 → #555555b0` 0.3 s `cubic-bezier(.65,0,.35,1)`; `blur(19px)`, border `#8a8a8a1a`, radius .9rem.
133
+ - Title opacity 0.5 s, `translateX(−15 % → −120 %)` 0.4 s. Intro: spread 1600 ms from the call, then the CTA fades in. Tablet (768–1024 px, `hover: none`): the first tap opens.
134
+
135
+ ### snap-strip (`product-card-mobile`, `CM`)
136
+
137
+ - 5000 ms a card, 2000 ms wait after mousedown / touchstart / wheel, `data-delay 500`, cards 68vw. iyo uses Smooothy (snap physics, cloned loop); here native scroll-snap.
138
+
139
+ ### fill-text (`GM`)
140
+
141
+ - SplitText `words, chars`; `fromTo(chars, { autoAlpha: .2 }, { autoAlpha: 1, stagger: .1, ease: 'linear' })` (0.5 s each), scrubbed `top 90%` → `center 40%`.
142
+
143
+ ### particle-hero (`wK` / `YW` / `dE`, adapted)
144
+
145
+ - iyo: 1280² points, RGBA float ping-pong, Newton step .4 × 4 toward the field's zeros, Chladni `sin(mπx/a)·sin(nπy/b)`, destination by hash vs `texturePercent`, damping `.985^dt60`, pointer radius .16 / force .0225 × smoothed speed (`min(d·5, 1)`, `.8 old + .2 new`), fade in 1 s after .3 s `power2.out`. Timeline: modes m, n ∈ {3, 5, 7} every 6–12 s; frequency 20–45 every 5–10 s; Chladni 0–5 over 1.2–3 s `power2.inOut`; word cycle N = 3–6 s (out .4N, in .6N to .4–.8, hold 2–4 s).
146
+ - Kit: own shaders, size² points (128² by default), damping .91, pointer radius .28, words drawn locally (dark pixels < 120 of a 640 × 320 canvas), Canvas2D fallback (~1800 points).
147
+
148
+ ## Collections and menu (v0.8)
149
+
150
+ All six have a `createX(root, options)` factory in the main entry, `useX(ref, options, deps)` in `/react`, CLI entry and vanilla/React playground page. `autoInit()` supports all six. Destroy restores the host and clears timers, tweens, listeners, observers and generated markup.
151
+
152
+ | Name | Markup | Options and controls | Source |
153
+ |---|---|---|---|
154
+ | ticket-menu | `.mk-dialog.mk-menu` dialog with `[data-mk-item]` | `MenuOptions`; `open`, `close`, `toggle`, `items`, `isOpen`; also `<Menu.Root ticket>` / `<mk-menu ticket>` | Siena: .9 s opening, 1.1 s rows from 150%, stagger .02; clip-path adapts the site's grid panels |
155
+ | infinite-reel | `.mk-infinite-reel > [data-mk-reel-track]` | `axis x/y`, `speed 0`, `friction .95`; `moveBy`, `position`, `pause`, `resume` | Siena: DOM/inertia adaptation, without original scene or snap |
156
+ | repel-bento | `.mk-repel-bento` with cards | `distance 30`, `breakpoint 1024`; `play`, `reset` | Dousan: entry spring 60/15, stagger .006, .6 vector; neighbours min(5000/d,30), spring 300/25 |
157
+ | rotator | `.mk-rotator` with one item per child | `interval 8000`, `autoplay true`, `index`, `onChange`; `select`, `next`, `previous`, `index` | Dousan testimonials: .25 s, y 5→0 |
158
+ | drag-ruler | `.mk-drag-ruler[aria-label]` | `min 0`, `max 8`, `step .1`, `value max`, `pixelsPerUnit 60`, `onChange`; `set`, `value`; `mk:change` | Dousan's years ruler |
159
+ | dock-pill | `.mk-dock-pill` with `[data-mk-dock-item]` links/buttons | `index`, `onSelect`; `select`, `index`; `mk:select` | Dousan's activeTabPill, spring 380/30 |
160
+
161
+ Reduced motion disables inertia, autoplay, entry and repulsion, keeping manual navigation. Reel copies are inert, aria-hidden and have no duplicate IDs. Ruler supports arrows, Home/End and PageUp/PageDown. Dock supports arrows/Home/End and skips disabled items. Keep rotator controls outside its child list.
162
+
163
+ ## Image shaders (v0.9, explicit `/gl`)
164
+
165
+ `createLiquidBlend`, `createMatcap`, `createShaderGrain`, `createShaderBlur` share `{ images?, progress?, amount?, maxDpr? }` and return `{ setProgress, setAmount, pause, resume, mode, destroy }`. Images default to the host's first two `<img>` elements; one image is repeated. Same-origin or CORS-enabled images; loading is observed. A host with explicit dimensions is required. React hooks have the corresponding `use…` names. These factories are not exported from the main entry or registered with autoInit.
166
+
167
+ - Liquid: `amount .15`, progress 0→1 blends the two textures with centre-weighted UV waves. OFFFICE `liquid-swap` mechanism adapted to images without models or render targets.
168
+ - Matcap: analytic sphere, normal UV .495+.5, hemispheric light and gain .15; optional image used as matcap. OFFFICE's model renderer is not included.
169
+ - Grain: own temporal hash, amount .07 from Siena's postprocess; 24 Hz grain, fixed frame for reduced motion.
170
+ - Blur: amount 5 image pixels, seven weighted samples with Siena's .19648255/.29690696/.09447040/.01038136 weights and 1.41176471/3.29411765/5.17647059 offsets. Progress rotates the direction x→y.
171
+
172
+ WebGL unsupported, compilation/texture error or context loss leaves the original HTML available. Context restoration rebuilds resources. The canvas is decorative; loops pause offscreen and in hidden tabs. No Three/OGL dependency and no shaders/assets copied from the researched sites.