@yewdiel21/motion-kit 0.9.0 → 0.10.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 (399) hide show
  1. package/NOTICE +3 -35
  2. package/README.md +93 -81
  3. package/dist/auto/index.d.ts +22 -3
  4. package/dist/auto/index.js +43 -5
  5. package/dist/auto/index.js.map +1 -1
  6. package/dist/{chunk-KLY5BRRI.js → chunk-34EZ7DF4.js} +1 -1
  7. package/dist/chunk-34EZ7DF4.js.map +1 -0
  8. package/dist/{chunk-RFDIZYFX.js → chunk-DOOMULVV.js} +20 -20
  9. package/dist/chunk-DOOMULVV.js.map +1 -0
  10. package/dist/{chunk-ABNRDDO7.js → chunk-I3DXF2KQ.js} +2 -2
  11. package/dist/{chunk-Q4LVFNLN.js → chunk-IKQ5FODN.js} +4 -4
  12. package/dist/chunk-IKQ5FODN.js.map +1 -0
  13. package/dist/{chunk-2BQGFOA7.js → chunk-J6OCFNGU.js} +2 -2
  14. package/dist/chunk-J6OCFNGU.js.map +1 -0
  15. package/dist/{chunk-6OJAP6ZL.js → chunk-QFBOHPWZ.js} +3 -3
  16. package/dist/chunk-QFBOHPWZ.js.map +1 -0
  17. package/dist/{chunk-J27KH2A2.js → chunk-RVSKEBIC.js} +1 -1
  18. package/dist/chunk-RVSKEBIC.js.map +1 -0
  19. package/dist/{chunk-34XYOXHG.js → chunk-TFNRIZHU.js} +1615 -12
  20. package/dist/chunk-TFNRIZHU.js.map +1 -0
  21. package/dist/{chunk-FS2HJVVY.js → chunk-XAKC2QY7.js} +1204 -5
  22. package/dist/chunk-XAKC2QY7.js.map +1 -0
  23. package/dist/css/components/calendar.css +159 -0
  24. package/dist/css/components/chat-background.css +2 -2
  25. package/dist/css/components/chat.css +1 -1
  26. package/dist/css/components/cursor.css +3 -3
  27. package/dist/css/components/dialog.css +3 -3
  28. package/dist/css/components/hover-card.css +14 -0
  29. package/dist/css/components/input-otp.css +10 -0
  30. package/dist/css/components/island.css +1 -1
  31. package/dist/css/components/menu.css +1 -1
  32. package/dist/css/components/nav.css +1 -5
  33. package/dist/css/components/pagination.css +11 -0
  34. package/dist/css/components/quantity.css +16 -0
  35. package/dist/css/components/slot-picker.css +126 -0
  36. package/dist/css/components/steps.css +2 -2
  37. package/dist/css/components/studio-dock.css +35 -0
  38. package/dist/css/components/theme-picker.css +1 -1
  39. package/dist/css/components/toast.css +1 -1
  40. package/dist/css/components/trailer.css +37 -0
  41. package/dist/css/controls.css +78 -13
  42. package/dist/css/effects.css +8 -8
  43. package/dist/css/fonts.css +1 -1
  44. package/dist/css/themes/black-green.css +1 -1
  45. package/dist/css/themes/black.css +1 -1
  46. package/dist/css/themes/blue.css +1 -1
  47. package/dist/css/themes/brown.css +1 -1
  48. package/dist/css/themes/cold-blue.css +1 -1
  49. package/dist/css/themes/funny.css +1 -1
  50. package/dist/css/themes/gray.css +1 -1
  51. package/dist/css/themes/green.css +1 -1
  52. package/dist/css/themes/modern-1.css +1 -1
  53. package/dist/css/themes/neogreen.css +1 -1
  54. package/dist/css/themes/new.css +1 -1
  55. package/dist/css/themes/new2.css +1 -1
  56. package/dist/css/themes/oled.css +1 -1
  57. package/dist/css/themes/orange.css +1 -1
  58. package/dist/css/themes/pastel-pink.css +1 -1
  59. package/dist/css/themes/pink.css +1 -1
  60. package/dist/css/themes/purple.css +1 -1
  61. package/dist/css/themes/red.css +1 -1
  62. package/dist/css/themes/super-blue.css +1 -1
  63. package/dist/css/themes.css +19 -19
  64. package/dist/css/tokens.css +9 -9
  65. package/dist/elements/index.d.ts +75 -7
  66. package/dist/elements/index.js +172 -6
  67. package/dist/elements/index.js.map +1 -1
  68. package/dist/{engine-CUr9QijM.d.ts → engine-GDGwQnJE.d.ts} +2 -2
  69. package/dist/gl/index.js +3 -3
  70. package/dist/index.css +1076 -63
  71. package/dist/index.d.ts +17 -17
  72. package/dist/index.js +49 -7
  73. package/dist/index.js.map +1 -1
  74. package/dist/react/index.d.ts +139 -40
  75. package/dist/react/index.js +355 -8
  76. package/dist/react/index.js.map +1 -1
  77. package/dist/{reduced-BM4Ls0g5.d.ts → reduced-BmBzKH94.d.ts} +215 -18
  78. package/dist/{reveal-WFUWDrei.d.ts → reveal-jsX-uwUz.d.ts} +233 -24
  79. package/dist/{store-Bqy_obnD.d.ts → store-DYb3tTsZ.d.ts} +5 -5
  80. package/dist/theme/generate.d.ts +1 -1
  81. package/dist/theme/generate.js +2 -2
  82. package/dist/theme/generate.js.map +1 -1
  83. package/dist/theme/index.d.ts +2 -2
  84. package/dist/theme/index.js +2 -2
  85. package/dist/{theme-BFMoCzi6.d.ts → theme-Bjf9qIIy.d.ts} +1 -1
  86. package/package.json +2 -2
  87. package/registry.json +230 -156
  88. package/skills/motion-kit/SKILL.md +209 -189
  89. package/skills/motion-kit/reference/components.md +5 -5
  90. package/skills/motion-kit/reference/design.md +14 -14
  91. package/skills/motion-kit/reference/recipes.md +48 -49
  92. package/src/auto/auto-init.ts +38 -0
  93. package/src/components/calendar/README.md +82 -0
  94. package/src/components/calendar/calendar.css +158 -0
  95. package/src/components/calendar/element.ts +27 -0
  96. package/src/components/calendar/engine.ts +412 -0
  97. package/src/components/calendar/react.tsx +69 -0
  98. package/src/components/chat/README.md +1 -3
  99. package/src/components/chat/chat.css +1 -1
  100. package/src/components/chat/engine.ts +1 -1
  101. package/src/components/chat/react.tsx +1 -1
  102. package/src/components/chat-background/README.md +1 -3
  103. package/src/components/chat-background/audio.ts +1 -1
  104. package/src/components/chat-background/chat-background.css +2 -2
  105. package/src/components/chat-background/engine.ts +1 -1
  106. package/src/components/chat-background/react.tsx +1 -1
  107. package/src/components/cursor/README.md +3 -5
  108. package/src/components/cursor/cursor.css +3 -3
  109. package/src/components/cursor/element.ts +2 -2
  110. package/src/components/cursor/engine.ts +5 -5
  111. package/src/components/cursor/react.tsx +1 -1
  112. package/src/components/dialog/README.md +4 -8
  113. package/src/components/dialog/dialog.css +3 -3
  114. package/src/components/dialog/engine.ts +2 -2
  115. package/src/components/dialog/react.tsx +1 -1
  116. package/src/components/hover-card/README.md +16 -0
  117. package/src/components/hover-card/element.ts +8 -0
  118. package/src/components/hover-card/engine.ts +69 -0
  119. package/src/components/hover-card/hover-card.css +13 -0
  120. package/src/components/hover-card/react.tsx +19 -0
  121. package/src/components/input-otp/README.md +18 -0
  122. package/src/components/input-otp/element.ts +14 -0
  123. package/src/components/input-otp/engine.ts +106 -0
  124. package/src/components/input-otp/input-otp.css +9 -0
  125. package/src/components/input-otp/react.tsx +21 -0
  126. package/src/components/island/README.md +7 -7
  127. package/src/components/island/engine.ts +3 -3
  128. package/src/components/island/island.css +1 -1
  129. package/src/components/island/react.tsx +3 -3
  130. package/src/components/menu/README.md +2 -4
  131. package/src/components/menu/engine.ts +1 -1
  132. package/src/components/menu/menu.css +1 -1
  133. package/src/components/menu/react.tsx +1 -1
  134. package/src/components/nav/README.md +12 -14
  135. package/src/components/nav/engine.ts +5 -5
  136. package/src/components/nav/nav.css +1 -5
  137. package/src/components/nav/react.tsx +3 -3
  138. package/src/components/pagination/README.md +17 -0
  139. package/src/components/pagination/element.ts +14 -0
  140. package/src/components/pagination/engine.ts +102 -0
  141. package/src/components/pagination/pagination.css +10 -0
  142. package/src/components/pagination/react.tsx +22 -0
  143. package/src/components/quantity/README.md +20 -0
  144. package/src/components/quantity/element.ts +14 -0
  145. package/src/components/quantity/engine.ts +85 -0
  146. package/src/components/quantity/quantity.css +15 -0
  147. package/src/components/quantity/react.tsx +21 -0
  148. package/src/components/slot-picker/README.md +68 -0
  149. package/src/components/slot-picker/element.ts +27 -0
  150. package/src/components/slot-picker/engine.ts +412 -0
  151. package/src/components/slot-picker/react.tsx +76 -0
  152. package/src/components/slot-picker/slot-picker.css +125 -0
  153. package/src/components/steps/README.md +1 -3
  154. package/src/components/steps/engine.ts +3 -3
  155. package/src/components/steps/react.tsx +2 -2
  156. package/src/components/steps/steps.css +2 -2
  157. package/src/components/studio-dock/README.md +44 -0
  158. package/src/components/studio-dock/element.ts +11 -0
  159. package/src/components/studio-dock/engine.ts +212 -0
  160. package/src/components/studio-dock/react.tsx +23 -0
  161. package/src/components/studio-dock/studio-dock.css +34 -0
  162. package/src/components/theme-picker/README.md +3 -5
  163. package/src/components/theme-picker/engine.ts +2 -2
  164. package/src/components/theme-picker/react.tsx +1 -1
  165. package/src/components/theme-picker/theme-picker.css +1 -1
  166. package/src/components/toast/README.md +1 -3
  167. package/src/components/toast/engine.ts +2 -2
  168. package/src/components/toast/react.tsx +1 -1
  169. package/src/components/toast/store.ts +1 -2
  170. package/src/components/toast/toast.css +1 -1
  171. package/src/components/trailer/README.md +28 -0
  172. package/src/components/trailer/element.ts +13 -0
  173. package/src/components/trailer/engine.ts +189 -0
  174. package/src/components/trailer/react.tsx +16 -0
  175. package/src/components/trailer/trailer.css +36 -0
  176. package/src/core/channels.ts +3 -3
  177. package/src/core/curves.ts +3 -3
  178. package/src/core/dates.ts +98 -0
  179. package/src/core/gsap.ts +2 -2
  180. package/src/core/loop.ts +1 -1
  181. package/src/core/math.ts +1 -1
  182. package/src/core/physics-spring.ts +2 -2
  183. package/src/core/scroll-track.ts +2 -4
  184. package/src/core/spring.ts +1 -1
  185. package/src/core/timing.ts +21 -22
  186. package/src/css/controls/README.md +24 -20
  187. package/src/css/controls/burger.css +1 -1
  188. package/src/css/controls/button.css +4 -4
  189. package/src/css/controls/checkbox.css +1 -1
  190. package/src/css/controls/chip.css +1 -1
  191. package/src/css/controls/field.css +2 -2
  192. package/src/css/controls/glass.css +4 -4
  193. package/src/css/controls/status.css +63 -0
  194. package/src/css/default-theme.css +1 -1
  195. package/src/css/effects/README.md +19 -21
  196. package/src/css/effects/anim.css +1 -1
  197. package/src/css/effects/fade.css +1 -1
  198. package/src/css/effects/float.css +1 -1
  199. package/src/css/effects/frost.css +1 -1
  200. package/src/css/effects/grain.css +1 -1
  201. package/src/css/effects/loading.css +1 -1
  202. package/src/css/effects/success.css +1 -1
  203. package/src/css/effects/text.css +1 -1
  204. package/src/css/fonts.css +1 -1
  205. package/src/css/states.css +1 -1
  206. package/src/css/themes/black-green.css +1 -1
  207. package/src/css/themes/black.css +1 -1
  208. package/src/css/themes/blue.css +1 -1
  209. package/src/css/themes/brown.css +1 -1
  210. package/src/css/themes/cold-blue.css +1 -1
  211. package/src/css/themes/funny.css +1 -1
  212. package/src/css/themes/gray.css +1 -1
  213. package/src/css/themes/green.css +1 -1
  214. package/src/css/themes/modern-1.css +1 -1
  215. package/src/css/themes/neogreen.css +1 -1
  216. package/src/css/themes/new.css +1 -1
  217. package/src/css/themes/new2.css +1 -1
  218. package/src/css/themes/oled.css +1 -1
  219. package/src/css/themes/orange.css +1 -1
  220. package/src/css/themes/pastel-pink.css +1 -1
  221. package/src/css/themes/pink.css +1 -1
  222. package/src/css/themes/purple.css +1 -1
  223. package/src/css/themes/red.css +1 -1
  224. package/src/css/themes/super-blue.css +1 -1
  225. package/src/css/tokens.css +8 -8
  226. package/src/elements/index.ts +24 -0
  227. package/src/gl/liquid-blend/README.md +3 -3
  228. package/src/gl/matcap/README.md +3 -3
  229. package/src/gl/particle-hero/README.md +1 -1
  230. package/src/gl/particle-hero/create-particle-hero.ts +3 -5
  231. package/src/gl/particle-hero/shaders.ts +1 -1
  232. package/src/gl/shader-blur/README.md +3 -3
  233. package/src/gl/shader-grain/README.md +3 -3
  234. package/src/index.ts +25 -0
  235. package/src/motion/chat.ts +2 -2
  236. package/src/motion/confetti.ts +1 -1
  237. package/src/motion/count.ts +1 -1
  238. package/src/motion/details.ts +1 -1
  239. package/src/motion/drag-dismiss.ts +1 -1
  240. package/src/motion/effects.ts +1 -1
  241. package/src/motion/entrances.ts +1 -1
  242. package/src/motion/floating.ts +1 -1
  243. package/src/motion/flow.ts +2 -2
  244. package/src/motion/indicator.ts +1 -1
  245. package/src/motion/layout.ts +1 -1
  246. package/src/motion/liquid.ts +1 -1
  247. package/src/motion/morph.ts +2 -2
  248. package/src/motion/reveal.ts +1 -1
  249. package/src/motion/sheet.ts +1 -1
  250. package/src/motion/surface.ts +1 -1
  251. package/src/react/animated-number.tsx +2 -2
  252. package/src/react/flip-group.tsx +1 -1
  253. package/src/react/frost.tsx +1 -1
  254. package/src/react/index.ts +21 -0
  255. package/src/react/slot-text.tsx +1 -1
  256. package/src/react/use-action-button.ts +6 -0
  257. package/src/react/use-card-dock.ts +1 -1
  258. package/src/react/use-cascade.ts +1 -1
  259. package/src/react/use-contrast.ts +1 -1
  260. package/src/react/use-depth-grid.ts +1 -1
  261. package/src/react/use-editorial-reveal.ts +7 -0
  262. package/src/react/use-fill-text.ts +1 -1
  263. package/src/react/use-film-hero.ts +6 -0
  264. package/src/react/use-font-cycle.ts +7 -0
  265. package/src/react/use-frame-zoom.ts +1 -1
  266. package/src/react/use-image-zoom.ts +5 -0
  267. package/src/react/use-joined-cta.ts +5 -0
  268. package/src/react/use-letters.ts +1 -1
  269. package/src/react/use-local-clock.ts +5 -0
  270. package/src/react/use-marquee.ts +1 -1
  271. package/src/react/use-mask-text.ts +1 -1
  272. package/src/react/use-parallax.ts +1 -1
  273. package/src/react/use-particle-hero.ts +1 -1
  274. package/src/react/use-scroll-dock.ts +5 -0
  275. package/src/react/use-snap-strip.ts +1 -1
  276. package/src/react/use-sticker.ts +5 -0
  277. package/src/react/use-studio-dock.ts +8 -0
  278. package/src/react/use-text-roll.ts +1 -1
  279. package/src/react/use-thumbnail-nav.ts +7 -0
  280. package/src/react/use-trailer.ts +6 -0
  281. package/src/react/use-weight-hover.ts +5 -0
  282. package/src/recipes/action-button/README.md +31 -0
  283. package/src/recipes/action-button/action-button.css +113 -0
  284. package/src/recipes/action-button/create-action-button.ts +56 -0
  285. package/src/recipes/card-dock/README.md +1 -1
  286. package/src/recipes/card-dock/card-dock.css +1 -1
  287. package/src/recipes/card-dock/create-card-dock.ts +2 -2
  288. package/src/recipes/cascade/README.md +1 -3
  289. package/src/recipes/cascade/create-cascade.ts +3 -3
  290. package/src/recipes/contrast/README.md +0 -2
  291. package/src/recipes/contrast/create-contrast.ts +1 -1
  292. package/src/recipes/counter/README.md +0 -2
  293. package/src/recipes/counter/create-counter.ts +1 -1
  294. package/src/recipes/curved-carousel/README.md +0 -2
  295. package/src/recipes/curved-carousel/create-curved-carousel.ts +2 -2
  296. package/src/recipes/curved-carousel/curved-carousel.css +1 -1
  297. package/src/recipes/depth-grid/README.md +0 -2
  298. package/src/recipes/depth-grid/create-depth-grid.ts +3 -4
  299. package/src/recipes/details/README.md +0 -2
  300. package/src/recipes/details/create-details.ts +1 -1
  301. package/src/recipes/dock-pill/README.md +3 -3
  302. package/src/recipes/drag-ruler/README.md +3 -3
  303. package/src/recipes/editorial-reveal/README.md +35 -0
  304. package/src/recipes/editorial-reveal/create-editorial-reveal.ts +91 -0
  305. package/src/recipes/editorial-reveal/editorial-reveal.css +17 -0
  306. package/src/recipes/fill-text/README.md +1 -1
  307. package/src/recipes/fill-text/create-fill-text.ts +1 -1
  308. package/src/recipes/film-hero/README.md +23 -0
  309. package/src/recipes/film-hero/create-film-hero.ts +65 -0
  310. package/src/recipes/film-hero/film-hero.css +29 -0
  311. package/src/recipes/fit-text/README.md +0 -2
  312. package/src/recipes/flow/README.md +0 -2
  313. package/src/recipes/flow/create-flow.ts +1 -1
  314. package/src/recipes/font-cycle/README.md +11 -0
  315. package/src/recipes/font-cycle/create-font-cycle.ts +126 -0
  316. package/src/recipes/font-cycle/font-cycle.css +36 -0
  317. package/src/recipes/frame-zoom/README.md +1 -3
  318. package/src/recipes/frame-zoom/create-frame-zoom.ts +2 -2
  319. package/src/recipes/image-zoom/README.md +16 -0
  320. package/src/recipes/image-zoom/create-image-zoom.ts +27 -0
  321. package/src/recipes/image-zoom/image-zoom.css +7 -0
  322. package/src/recipes/infinite-reel/README.md +3 -3
  323. package/src/recipes/joined-cta/README.md +13 -0
  324. package/src/recipes/joined-cta/create-joined-cta.ts +22 -0
  325. package/src/recipes/joined-cta/joined-cta.css +17 -0
  326. package/src/recipes/letters/README.md +2 -4
  327. package/src/recipes/letters/create-letters.ts +3 -3
  328. package/src/recipes/local-clock/README.md +31 -0
  329. package/src/recipes/local-clock/create-local-clock.ts +61 -0
  330. package/src/recipes/marquee/README.md +0 -2
  331. package/src/recipes/marquee/create-marquee.ts +3 -3
  332. package/src/recipes/marquee/marquee.css +1 -1
  333. package/src/recipes/mask-text/README.md +3 -5
  334. package/src/recipes/mask-text/create-mask-text.ts +4 -4
  335. package/src/recipes/parallax/README.md +1 -3
  336. package/src/recipes/parallax/create-parallax.ts +1 -1
  337. package/src/recipes/pile/README.md +0 -2
  338. package/src/recipes/pile/create-pile.ts +1 -1
  339. package/src/recipes/pile/pile-math.ts +1 -1
  340. package/src/recipes/pile/pile.css +1 -1
  341. package/src/recipes/pill/README.md +0 -2
  342. package/src/recipes/pill/pill.css +2 -2
  343. package/src/recipes/pop/README.md +0 -2
  344. package/src/recipes/pop/create-pop.ts +1 -1
  345. package/src/recipes/radial-menu/README.md +0 -2
  346. package/src/recipes/radial-menu/create-radial-menu.ts +1 -1
  347. package/src/recipes/radial-menu/radial-menu.css +1 -1
  348. package/src/recipes/reflow/README.md +3 -5
  349. package/src/recipes/reflow/create-reflow.ts +1 -1
  350. package/src/recipes/repel-bento/README.md +3 -3
  351. package/src/recipes/reveal/README.md +0 -2
  352. package/src/recipes/reveal/create-reveal.ts +1 -1
  353. package/src/recipes/rotator/README.md +3 -3
  354. package/src/recipes/scroll-dock/README.md +46 -0
  355. package/src/recipes/scroll-dock/create-scroll-dock.ts +72 -0
  356. package/src/recipes/scroll-dock/scroll-dock.css +70 -0
  357. package/src/recipes/slot-text/README.md +0 -2
  358. package/src/recipes/slot-text/create-slot-text.ts +1 -1
  359. package/src/recipes/snap-strip/README.md +1 -1
  360. package/src/recipes/snap-strip/create-snap-strip.ts +5 -6
  361. package/src/recipes/snap-strip/snap-strip.css +1 -1
  362. package/src/recipes/sortable/README.md +3 -5
  363. package/src/recipes/sortable/create-sortable.ts +4 -4
  364. package/src/recipes/sortable/sortable.css +1 -1
  365. package/src/recipes/spring-cards/README.md +0 -2
  366. package/src/recipes/spring-cards/create-spring-cards.ts +1 -1
  367. package/src/recipes/spring-cards/spring-cards.css +1 -1
  368. package/src/recipes/sticker/README.md +59 -0
  369. package/src/recipes/sticker/create-sticker.ts +490 -0
  370. package/src/recipes/sticker/sticker.css +112 -0
  371. package/src/recipes/stretch-footer/README.md +0 -2
  372. package/src/recipes/stretch-footer/create-stretch-footer.ts +1 -1
  373. package/src/recipes/stretch-footer/stretch-footer.css +1 -1
  374. package/src/recipes/tabs/README.md +0 -2
  375. package/src/recipes/tabs/create-tabs.ts +1 -1
  376. package/src/recipes/text-roll/README.md +1 -3
  377. package/src/recipes/text-roll/create-text-roll.ts +3 -3
  378. package/src/recipes/text-roll/text-roll.css +1 -1
  379. package/src/recipes/thumbnail-nav/README.md +16 -0
  380. package/src/recipes/thumbnail-nav/create-thumbnail-nav.ts +165 -0
  381. package/src/recipes/thumbnail-nav/thumbnail-nav.css +108 -0
  382. package/src/recipes/ticket-menu/README.md +3 -3
  383. package/src/recipes/weight-hover/README.md +37 -0
  384. package/src/recipes/weight-hover/create-weight-hover.ts +136 -0
  385. package/src/recipes/weight-hover/weight-hover.css +7 -0
  386. package/src/recipes/words/README.md +0 -2
  387. package/src/recipes/words/create-words.ts +1 -1
  388. package/src/theme/generate.ts +1 -1
  389. package/src/theme/presets.ts +2 -2
  390. package/src/theme/theme.ts +1 -2
  391. package/dist/chunk-2BQGFOA7.js.map +0 -1
  392. package/dist/chunk-34XYOXHG.js.map +0 -1
  393. package/dist/chunk-6OJAP6ZL.js.map +0 -1
  394. package/dist/chunk-FS2HJVVY.js.map +0 -1
  395. package/dist/chunk-J27KH2A2.js.map +0 -1
  396. package/dist/chunk-KLY5BRRI.js.map +0 -1
  397. package/dist/chunk-Q4LVFNLN.js.map +0 -1
  398. package/dist/chunk-RFDIZYFX.js.map +0 -1
  399. /package/dist/{chunk-ABNRDDO7.js.map → chunk-I3DXF2KQ.js.map} +0 -0
@@ -5,7 +5,7 @@ import { inert, warnOnce, type Instance } from '../../core/types';
5
5
  import { shows, thresholds } from '../../core/visible';
6
6
 
7
7
  /*
8
- * Siena's slot title: every letter is a little reel — its face and a copy
8
+ * slot title: every letter is a little reel — its face and a copy
9
9
  * under it scroll up one height per turn, 8–14 turns picked at random, in a
10
10
  * paused timeline. play() pushes all those timelines from 0 to 1 in 2 s
11
11
  * (expo.out, 40 ms apart in random order), so the letters spin fast and settle
@@ -16,12 +16,12 @@ import { shows, thresholds } from '../../core/visible';
16
16
  export interface TextRollOptions {
17
17
  duration?: number;
18
18
  stagger?: number;
19
- /** [min, max] repeats of each letter's turn (Siena: 8–14). */
19
+ /** [min, max] repeats of each letter's turn (default: 8–14). */
20
20
  turns?: readonly [number, number];
21
21
  /** 'load' (default) after `delay`; 'scroll' when it shows; 'hover'; 'manual'. */
22
22
  trigger?: 'load' | 'scroll' | 'hover' | 'manual';
23
23
  delay?: number;
24
- /** Replay every n ms while visible and not scrolling (Siena: 6000). 0: off. */
24
+ /** Replay every n ms while visible and not scrolling (default: 6000). 0: off. */
25
25
  interval?: number;
26
26
  }
27
27
 
@@ -1,4 +1,4 @@
1
- /* text-roll — Siena's slot title: each letter is a reel (its face and a copy below), clipped to one line. */
1
+ /* text-roll — slot title: each letter is a reel (its face and a copy below), clipped to one line. */
2
2
  .mk-roll-word {
3
3
  white-space: nowrap;
4
4
  }
@@ -0,0 +1,16 @@
1
+ # thumbnail-nav
2
+
3
+ Selector de categorías inspirado en la cápsula inferior de Ottografie: tres previews apilados, expansión 222×53→289×68 y panel accesible de enlaces. Es una adaptación contenida; no reproduce su transición de pantalla completa.
4
+
5
+ ```html
6
+ <nav data-mk="thumbnail-nav" aria-label="Colecciones">
7
+ <a data-mk-thumbnail-item aria-current="page" href="/campaigns"><img src="/campaigns.jpg" alt="" />Campañas</a>
8
+ <a data-mk-thumbnail-item href="/editorial"><img src="/editorial.jpg" alt="" />Editorial</a>
9
+ </nav>
10
+ ```
11
+
12
+ `createThumbnailNav(root, { index, label: 'Categoría', duration: .5, onSelect })` devuelve `open()`, `close()`, `toggle()`, `select(index)`, `index`, `expanded`, `destroy()`. Duración en segundos; panel GSAP `power3.out`, cierre .3 s por defecto. Emitimos `mk:select` cancelable con `{ index, item }`; cancelar evita también la navegación de ese click. Los enlaces conservan su destino. Usa botones si la selección cambia estado local. `data-mk-label` personaliza el nombre de una opción.
13
+
14
+ React: `useThumbnailNav(ref, options, deps)`. La receta administra los hijos mientras está montada; no cambies la lista durante ese intervalo. Flechas y Home/End mueven foco, Escape devuelve foco al disparador; Tab y click exterior cierran sin atrapar el foco. Las opciones `disabled` / `aria-disabled="true"` se omiten. Touch usa el mismo botón de apertura. Sin JS los enlaces siguen disponibles. Con movimiento reducido no hay animación. `destroy()` devuelve nodos/atributos originales y cancela listeners y tweens.
15
+
16
+ La cápsula se contiene en su padre por defecto. Añade `mk-thumbnail-nav--fixed` para fijarla abajo y controla `--mk-thumbnail-nav-surface`, `--mk-thumbnail-nav-color`. No contiene imágenes del sitio de referencia.
@@ -0,0 +1,165 @@
1
+ import { gsap, registerMotion } from '../../core/gsap';
2
+ import { reduced } from '../../core/reduced';
3
+ import { createScope } from '../../core/scope';
4
+ import { wrap } from '../../core/math';
5
+ import { inert, type Instance } from '../../core/types';
6
+
7
+ export interface ThumbnailNavOptions {
8
+ index?: number;
9
+ label?: string;
10
+ /** Seconds for expansion and panel entry. */
11
+ duration?: number;
12
+ onSelect?: (index: number, item: HTMLElement) => void;
13
+ }
14
+
15
+ export interface ThumbnailNavInstance extends Instance {
16
+ open(): void;
17
+ close(): void;
18
+ toggle(): void;
19
+ select(index: number): void;
20
+ readonly index: number;
21
+ readonly expanded: boolean;
22
+ }
23
+
24
+ let panelSequence = 0;
25
+
26
+ /** A category disclosure with stacked previews, real links and complete keyboard navigation. */
27
+ export function createThumbnailNav(root: HTMLElement | null, options: ThumbnailNavOptions = {}): ThumbnailNavInstance {
28
+ const items = root ? [...root.querySelectorAll<HTMLElement>('[data-mk-thumbnail-item]')] : [];
29
+ if (!root || !items.length || typeof document === 'undefined') return inert({ open() {}, close() {}, toggle() {}, select() {}, index: 0, expanded: false });
30
+ registerMotion();
31
+ const scope = createScope();
32
+ const original = [...root.childNodes];
33
+ const duration = Math.max(0, options.duration ?? 0.5);
34
+ const disabled = (item: HTMLElement) => item.matches(':disabled, [aria-disabled="true"]');
35
+ const name = (item: HTMLElement) => item.dataset.mkLabel ?? item.textContent?.trim() ?? '';
36
+ let index = 0;
37
+ let expanded = false;
38
+ let animation: gsap.core.Tween | undefined;
39
+ const button = document.createElement('button');
40
+ button.type = 'button';
41
+ button.className = 'mk-thumbnail-nav__toggle';
42
+ button.setAttribute('aria-expanded', 'false');
43
+ const preview = document.createElement('span');
44
+ preview.className = 'mk-thumbnail-nav__preview';
45
+ preview.setAttribute('aria-hidden', 'true');
46
+ const copy = document.createElement('span');
47
+ copy.className = 'mk-thumbnail-nav__copy';
48
+ const kicker = document.createElement('span');
49
+ kicker.className = 'mk-thumbnail-nav__kicker';
50
+ kicker.textContent = options.label ?? root.dataset.mkLabel ?? 'Categoría';
51
+ const label = document.createElement('span');
52
+ label.className = 'mk-thumbnail-nav__label';
53
+ copy.append(kicker, label);
54
+ const arrow = document.createElement('span');
55
+ arrow.className = 'mk-thumbnail-nav__chevron';
56
+ arrow.setAttribute('aria-hidden', 'true');
57
+ arrow.textContent = '⌄';
58
+ button.append(preview, copy, arrow);
59
+ const panel = document.createElement('div');
60
+ panel.className = 'mk-thumbnail-nav__panel';
61
+ panel.id = `mk-thumbnail-panel-${++panelSequence}`;
62
+ while (document.getElementById(panel.id)) panel.id = `mk-thumbnail-panel-${++panelSequence}`;
63
+ panel.hidden = true;
64
+ panel.inert = true;
65
+ button.setAttribute('aria-controls', panel.id);
66
+ panel.append(...original);
67
+ root.replaceChildren(button, panel);
68
+ scope.add(() => root.replaceChildren(...original));
69
+ scope.attr(root, 'class', `${root.className} mk-thumbnail-nav`.trim());
70
+ scope.attr(root, 'style', root.getAttribute('style'));
71
+ root.style.setProperty('--mk-thumbnail-nav-duration', `${duration}s`);
72
+ scope.attr(root, 'data-mk-ready', '');
73
+
74
+ const updatePreview = () => {
75
+ label.textContent = name(items[index]!);
76
+ button.setAttribute('aria-label', `${kicker.textContent}: ${label.textContent}`);
77
+ preview.replaceChildren();
78
+ for (let offset = 0; offset < Math.min(3, items.length); offset++) {
79
+ const item = items[wrap(index + offset, items.length)]!;
80
+ const thumb = document.createElement('span');
81
+ thumb.className = 'mk-thumbnail-nav__thumb';
82
+ thumb.style.setProperty('--mk-thumbnail-order', String(offset));
83
+ const source = item.querySelector('img');
84
+ if (source) {
85
+ const image = source.cloneNode(false) as HTMLImageElement;
86
+ image.removeAttribute('id');
87
+ image.alt = '';
88
+ thumb.append(image);
89
+ } else thumb.textContent = name(item).slice(0, 1);
90
+ preview.append(thumb);
91
+ }
92
+ };
93
+ const focusItem = (candidate: number, direction = 1) => {
94
+ for (let attempt = 0; attempt < items.length; attempt++) {
95
+ const current = items[wrap(candidate, items.length)]!;
96
+ if (!disabled(current)) { current.focus(); return; }
97
+ candidate += direction;
98
+ }
99
+ };
100
+ const close = (restoreFocus = panel.contains(document.activeElement)) => {
101
+ if (scope.disposed || !expanded) return;
102
+ expanded = false;
103
+ button.setAttribute('aria-expanded', 'false');
104
+ scope.attr(root, 'data-mk-expanded', null);
105
+ panel.inert = true;
106
+ animation?.kill();
107
+ if (restoreFocus) button.focus();
108
+ if (reduced() || duration === 0) panel.hidden = true;
109
+ else animation = gsap.to(panel, { autoAlpha: 0, y: 10, scale: 0.98, duration: duration * 0.6, ease: 'power3.out', overwrite: true, onComplete: () => { panel.hidden = true; } });
110
+ };
111
+ const open = () => {
112
+ if (scope.disposed || expanded) return;
113
+ expanded = true;
114
+ panel.hidden = false;
115
+ panel.inert = false;
116
+ button.setAttribute('aria-expanded', 'true');
117
+ scope.attr(root, 'data-mk-expanded', '');
118
+ animation?.kill();
119
+ if (reduced() || duration === 0) gsap.set(panel, { autoAlpha: 1, y: 0, scale: 1 });
120
+ else animation = gsap.fromTo(panel, { autoAlpha: 0, y: 12, scale: 0.96 }, { autoAlpha: 1, y: 0, scale: 1, duration, ease: 'power3.out', overwrite: true });
121
+ };
122
+ const select = (next: number, notify = true) => {
123
+ if (scope.disposed || !Number.isFinite(next)) return false;
124
+ const candidate = wrap(Math.round(next), items.length);
125
+ const item = items[candidate]!;
126
+ if (disabled(item)) return false;
127
+ if (notify && !root.dispatchEvent(new CustomEvent('mk:select', { bubbles: true, cancelable: true, detail: { index: candidate, item } }))) return false;
128
+ index = candidate;
129
+ items.forEach((entry, position) => scope.attr(entry, 'aria-current', position === index ? 'page' : null));
130
+ updatePreview();
131
+ if (notify) options.onSelect?.(index, item);
132
+ return true;
133
+ };
134
+ scope.on(button, 'click', () => { if (expanded) close(); else open(); });
135
+ scope.on(root, 'click', (event) => {
136
+ const item = (event.target as Element).closest<HTMLElement>('[data-mk-thumbnail-item]');
137
+ if (!item || !items.includes(item)) return;
138
+ if (!select(items.indexOf(item))) { event.preventDefault(); return; }
139
+ close(true);
140
+ });
141
+ scope.on(root, 'keydown', (event) => {
142
+ if (event.key === 'Escape' && expanded) { event.preventDefault(); event.stopPropagation(); close(true); return; }
143
+ const current = items.indexOf(document.activeElement as HTMLElement);
144
+ const onTrigger = document.activeElement === button;
145
+ const direction = event.key === 'ArrowDown' || event.key === 'ArrowRight' ? 1 : event.key === 'ArrowUp' || event.key === 'ArrowLeft' ? -1 : 0;
146
+ if ((!direction && event.key !== 'Home' && event.key !== 'End') || (!onTrigger && current < 0)) return;
147
+ event.preventDefault();
148
+ open();
149
+ const candidate = event.key === 'Home' ? 0 : event.key === 'End' ? items.length - 1 : onTrigger ? index : current + direction;
150
+ focusItem(candidate, event.key === 'End' ? -1 : direction || 1);
151
+ });
152
+ scope.on(root, 'focusout', (event) => {
153
+ if (event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;
154
+ scope.timeout(() => { if (!scope.disposed && !root.contains(document.activeElement)) close(false); }, 0);
155
+ });
156
+ scope.on(document, 'pointerdown', (event) => { if (event.target instanceof Node && !root.contains(event.target)) close(false); });
157
+ scope.add(() => animation?.kill());
158
+ const initial = options.index ?? Math.max(0, items.findIndex((item) => item.getAttribute('aria-current') === 'page'));
159
+ const enabled = items.findIndex((item) => !disabled(item));
160
+ if (!select(initial, false)) {
161
+ if (enabled >= 0) select(enabled, false);
162
+ else { updatePreview(); button.disabled = true; }
163
+ }
164
+ return { open, close: () => close(), toggle: () => { if (expanded) close(); else open(); }, select: (next) => { select(next); }, get index() { return index; }, get expanded() { return expanded; }, destroy: () => scope.dispose() };
165
+ }
@@ -0,0 +1,108 @@
1
+ .mk-thumbnail-nav {
2
+ --mk-thumbnail-nav-surface: #181716e6;
3
+ --mk-thumbnail-nav-color: #fff;
4
+ --mk-thumbnail-nav-duration: .5s;
5
+ --mk-thumbnail-nav-ease: cubic-bezier(.165,.84,.44,1);
6
+ position: relative;
7
+ display: flex;
8
+ align-items: end;
9
+ justify-content: center;
10
+ inline-size: min(100%, 18.0625rem);
11
+ min-block-size: 4.25rem;
12
+ margin-inline: auto;
13
+ color: var(--mk-thumbnail-nav-color);
14
+ font-family: var(--mk-font, sans-serif);
15
+ isolation: isolate;
16
+ }
17
+ .mk-thumbnail-nav--fixed { position: fixed; z-index: 20; inset: auto auto max(1.25rem, env(safe-area-inset-bottom)) 50%; transform: translateX(-50%); }
18
+ .mk-thumbnail-nav__toggle {
19
+ position: relative;
20
+ display: flex;
21
+ align-items: center;
22
+ flex: none;
23
+ gap: .5rem;
24
+ inline-size: 13.875rem;
25
+ max-inline-size: 100%;
26
+ min-block-size: 3.3125rem;
27
+ padding: .40625rem .75rem;
28
+ border: 0;
29
+ border-radius: 3.3125rem;
30
+ background: var(--mk-thumbnail-nav-surface);
31
+ color: inherit;
32
+ font: inherit;
33
+ cursor: pointer;
34
+ -webkit-backdrop-filter: blur(8px);
35
+ backdrop-filter: blur(8px);
36
+ transition: inline-size var(--mk-thumbnail-nav-duration) var(--mk-thumbnail-nav-ease), min-block-size var(--mk-thumbnail-nav-duration) var(--mk-thumbnail-nav-ease), border-radius var(--mk-thumbnail-nav-duration) var(--mk-thumbnail-nav-ease);
37
+ }
38
+ .mk-thumbnail-nav[data-mk-expanded] .mk-thumbnail-nav__toggle { inline-size: 18.0625rem; min-block-size: 4.25rem; border-radius: 4.25rem; }
39
+ .mk-thumbnail-nav__preview { position: relative; display: block; flex: 0 0 4.25rem; block-size: 2.5rem; }
40
+ .mk-thumbnail-nav__thumb {
41
+ position: absolute;
42
+ inset: 0 auto auto 0;
43
+ display: grid;
44
+ place-items: center;
45
+ inline-size: 2.5rem;
46
+ block-size: 2.5rem;
47
+ overflow: hidden;
48
+ border-radius: 100%;
49
+ background: var(--mk-accent, #dda063);
50
+ color: var(--mk-on-accent, #181716);
51
+ font-size: .75rem;
52
+ font-weight: 600;
53
+ z-index: calc(3 - var(--mk-thumbnail-order));
54
+ transform-origin: left center;
55
+ transform: translateX(calc((2 - var(--mk-thumbnail-order)) * .625rem)) scale(.725);
56
+ transition: transform var(--mk-thumbnail-nav-duration) var(--mk-thumbnail-nav-ease);
57
+ }
58
+ .mk-thumbnail-nav[data-mk-expanded] .mk-thumbnail-nav__thumb { transform: translateX(calc((2 - var(--mk-thumbnail-order)) * .5rem)) scale(1); }
59
+ .mk-thumbnail-nav__thumb img { inline-size: 100%; block-size: 100%; object-fit: cover; }
60
+ .mk-thumbnail-nav__copy { display: grid; flex: 1; min-inline-size: 0; gap: .125rem; text-align: start; }
61
+ .mk-thumbnail-nav__kicker { display: none; font-size: .6875rem; opacity: .6; line-height: 1.1; }
62
+ .mk-thumbnail-nav[data-mk-expanded] .mk-thumbnail-nav__kicker { display: block; }
63
+ .mk-thumbnail-nav__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .875rem; font-weight: 600; line-height: 1.3; }
64
+ .mk-thumbnail-nav__chevron { display: grid; flex: 0 0 1.8125rem; place-items: center; block-size: 1.8125rem; border-radius: 50%; background: #ffffff1a; line-height: 1; transition: transform var(--mk-thumbnail-nav-duration) var(--mk-thumbnail-nav-ease); }
65
+ .mk-thumbnail-nav[data-mk-expanded] .mk-thumbnail-nav__chevron { transform: rotate(180deg); }
66
+ .mk-thumbnail-nav__panel {
67
+ position: absolute;
68
+ z-index: 2;
69
+ inset: auto 0 calc(100% + .625rem);
70
+ display: grid;
71
+ gap: .25rem;
72
+ max-block-size: min(20rem, 55vh);
73
+ overflow: auto;
74
+ padding: .5rem;
75
+ border: 1px solid #ffffff1a;
76
+ border-radius: 1.125rem;
77
+ background: var(--mk-thumbnail-nav-surface);
78
+ color: inherit;
79
+ box-shadow: 0 1rem 3rem #0003;
80
+ -webkit-backdrop-filter: blur(8px);
81
+ backdrop-filter: blur(8px);
82
+ transform-origin: center bottom;
83
+ }
84
+ .mk-thumbnail-nav__panel[hidden] { display: none; }
85
+ .mk-thumbnail-nav__panel :is(ul, ol) { margin: 0; padding: 0; list-style: none; }
86
+ .mk-thumbnail-nav__panel [data-mk-thumbnail-item] {
87
+ box-sizing: border-box;
88
+ display: flex;
89
+ align-items: center;
90
+ gap: .75rem;
91
+ inline-size: 100%;
92
+ min-block-size: 3rem;
93
+ padding: .5rem .625rem;
94
+ border: 0;
95
+ border-radius: .75rem;
96
+ background: transparent;
97
+ color: inherit;
98
+ font: inherit;
99
+ text-decoration: none;
100
+ cursor: pointer;
101
+ }
102
+ .mk-thumbnail-nav__panel [data-mk-thumbnail-item]:is(:hover, [aria-current='page']) { background: #ffffff1a; }
103
+ .mk-thumbnail-nav__panel [data-mk-thumbnail-item]:is(:disabled, [aria-disabled='true']) { opacity: .4; cursor: default; }
104
+ .mk-thumbnail-nav__panel [data-mk-thumbnail-item] img { inline-size: 2rem; block-size: 2rem; object-fit: cover; border-radius: 50%; }
105
+ .mk-thumbnail-nav__toggle:focus-visible, .mk-thumbnail-nav__panel [data-mk-thumbnail-item]:focus-visible { outline: 2px solid var(--mk-accent, #dda063); outline-offset: 2px; }
106
+ @media (prefers-reduced-motion: reduce) {
107
+ .mk-thumbnail-nav *, .mk-thumbnail-nav *::before, .mk-thumbnail-nav *::after { transition: none !important; }
108
+ }
@@ -1,6 +1,6 @@
1
1
  # ticket-menu
2
2
 
3
- Menú boleto de Siena sobre menu: recorte, filas escalonadas, teclado y foco.
3
+ Menú boleto sobre menu: recorte, filas escalonadas, teclado y foco.
4
4
 
5
5
  ```html
6
6
  <button data-mk-open="sections">Secciones</button>
@@ -17,8 +17,8 @@ const instance = createTicketMenu(root);
17
17
  instance.destroy();
18
18
  ```
19
19
 
20
- `createTicketMenu(dialog, MenuOptions)` reutiliza el motor de menú. También `<mk-menu ticket>` y `<Menu.Root ticket>`. Apertura por clip-path .9 s; filas desde yPercent 150, 1.1 s, stagger .02. `open(origin?)`, `close()`, `toggle()`, `items`, `isOpen`. Origen: Siena `siena.menu.expand` y `siena.menu.details`; adaptación de sus grid rows a un diálogo local.
20
+ `createTicketMenu(dialog, MenuOptions)` reutiliza el motor de menú. También `<mk-menu ticket>` y `<Menu.Root ticket>`. Apertura por clip-path .9 s; filas desde yPercent 150, 1.1 s, stagger .02. `open(origin?)`, `close()`, `toggle()`, `items`, `isOpen`.
21
21
 
22
22
  Vanilla declarativo: `autoInit(root)`. React: `useTicketMenu(ref, options, deps)` devuelve una ref a la instancia; se destruye al desmontar. Con movimiento reducido no se ejecuta movimiento decorativo. `destroy()` restaura atributos y estilos y quita listeners, observers y nodos generados.
23
23
 
24
- Desde 0.8.0. Parámetros investigados en `REA - Dousan OFFFICE Siena/`; implementación propia, sin código ni assets de esos sitios.
24
+ Desde 0.8.0.
@@ -0,0 +1,37 @@
1
+ # weight-hover
2
+
3
+ Cada letra adelgaza al acercarse el puntero, como el titular y el «SAY HELLO» de DOVV Studio. El valor del eje sale de `reposo + (cerca − reposo) · smoothstep(1 − distancia / radio)`. Cada frame recorre un 10 % de lo que falta, y el bucle se detiene cuando ninguna letra se mueve más de media unidad. Valores de la referencia: reposo 900, cerca 200 y radio 400 px (380 en el contacto).
4
+
5
+ Necesita una fuente variable con el eje (por defecto `wght`), por ejemplo Archivo, Inter o Roboto Flex; con una fuente estática no hay efecto. Funciona con cualquier texto y `<br>`: las letras visibles son `aria-hidden` y una copia `.mk-sr-only` conserva el texto completo para lectores de pantalla.
6
+
7
+ ## Vanilla / Astro
8
+
9
+ ```html
10
+ <h2 class="mk-weight-hover" data-mk="weight-hover" data-mk-rest="900" data-mk-near="200" data-mk-radius="400"
11
+ style="font-family: 'Archivo', sans-serif">WE BUILD SITES<br>THAT MOVE PEOPLE</h2>
12
+ ```
13
+
14
+ Con `autoInit()` las opciones van como `data-mk-<opción>`. Sin `autoInit`: `createWeightHover(el, opciones)`, que devuelve `pointAt({x, y} | null)` y `destroy()`.
15
+
16
+ ## React
17
+
18
+ ```tsx
19
+ const ref = useRef<HTMLHeadingElement>(null);
20
+ useWeightHover(ref, { rest: 900, near: 200, radius: 400 });
21
+ return <h2 ref={ref} className="mk-weight-hover">SAY HELLO</h2>;
22
+ ```
23
+
24
+ ## Opciones
25
+
26
+ | Opción | Por defecto | Qué hace |
27
+ |---|---|---|
28
+ | `rest` | `900` | Valor del eje lejos del puntero |
29
+ | `near` | `200` | Valor bajo el puntero |
30
+ | `radius` | `400` | Alcance en px |
31
+ | `axis` | `'wght'` | Eje variable (4 letras) |
32
+ | `lerp` | `0.1` | Fracción del camino por frame |
33
+ | `anyPointer` | `false` | Reaccionar también a táctil y lápiz; la referencia sólo usa ratón |
34
+
35
+ ## Accesibilidad
36
+
37
+ El texto no cambia, sólo su grosor. Con movimiento reducido no parte el texto ni anima. `destroy()` restaura los nodos originales.
@@ -0,0 +1,136 @@
1
+ import { reduced } from '../../core/reduced';
2
+ import { createScope } from '../../core/scope';
3
+ import { inert, type Instance } from '../../core/types';
4
+
5
+ /*
6
+ * Weight hover (DOVV Studio hero and "SAY HELLO"): every letter drops its
7
+ * variable-font weight as the pointer gets close. Target = rest + (near − rest)
8
+ * · smoothstep(1 − distance / radius); each frame moves 10 % of the way and the
9
+ * loop stops once no letter moves more than half a unit. Mouse only, off with
10
+ * reduced motion. Needs a font with the axis (default "wght").
11
+ */
12
+
13
+ export interface WeightHoverOptions {
14
+ /** Axis value far from the pointer. */
15
+ rest?: number;
16
+ /** Axis value right under the pointer. */
17
+ near?: number;
18
+ /** Reach of the effect, px. */
19
+ radius?: number;
20
+ /** Variable-font axis tag. */
21
+ axis?: string;
22
+ /** Share of the remaining distance covered per frame (0–1). */
23
+ lerp?: number;
24
+ /** React to touch and pen too (the reference reacts to the mouse only). */
25
+ anyPointer?: boolean;
26
+ }
27
+
28
+ export interface WeightHoverInstance extends Instance {
29
+ /** Point the effect at viewport coordinates (null releases it). */
30
+ pointAt(point: { x: number; y: number } | null): void;
31
+ }
32
+
33
+ interface Letter { span: HTMLElement; value: number; x: number; y: number }
34
+
35
+ const FAR = -1e5;
36
+
37
+ export function createWeightHover(root: HTMLElement | null, options: WeightHoverOptions = {}): WeightHoverInstance {
38
+ if (!root) return inert({ pointAt() {} });
39
+ const scope = createScope();
40
+ scope.attr(root, 'data-mk-ready', '');
41
+ if (reduced()) return { pointAt() {}, destroy: () => scope.dispose() };
42
+
43
+ const num = (value: number | undefined, fallback: number) => (Number.isFinite(value) ? value! : fallback);
44
+ const rest = num(options.rest, 900);
45
+ const near = num(options.near, 200);
46
+ const radius = Math.max(1, num(options.radius, 400));
47
+ const lerp = Math.min(1, Math.max(0.01, num(options.lerp, 0.1)));
48
+ const axis = /^[A-Za-z]{4}$/.test(options.axis ?? '') ? options.axis! : 'wght';
49
+ const setting = (value: number) => `"${axis}" ${value.toFixed(1)}`;
50
+
51
+ // Visual letters are aria-hidden; one screen-reader copy keeps the text whole.
52
+ const original = [...root.childNodes];
53
+ const copy = document.createElement('span');
54
+ copy.className = 'mk-sr-only';
55
+ const spoken = root.cloneNode(true) as HTMLElement;
56
+ spoken.querySelectorAll('br').forEach((br) => br.replaceWith(' '));
57
+ copy.textContent = spoken.textContent?.replace(/\s+/g, ' ').trim() ?? '';
58
+ const visual = document.createElement('span');
59
+ visual.className = 'mk-weight-hover__visual';
60
+ visual.setAttribute('aria-hidden', 'true');
61
+ visual.append(...original.map((node) => node.cloneNode(true)));
62
+ const letters: Letter[] = [];
63
+ const walker = document.createTreeWalker(visual, NodeFilter.SHOW_TEXT);
64
+ const texts: Text[] = [];
65
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) texts.push(node as Text);
66
+ for (const text of texts) {
67
+ const fragment = document.createDocumentFragment();
68
+ for (const char of text.data) {
69
+ if (/\s/.test(char)) {
70
+ fragment.append(char);
71
+ continue;
72
+ }
73
+ const span = document.createElement('span');
74
+ span.dataset.mkWeightChar = '';
75
+ span.textContent = char;
76
+ span.style.fontVariationSettings = setting(rest);
77
+ fragment.append(span);
78
+ letters.push({ span, value: rest, x: 0, y: 0 });
79
+ }
80
+ text.replaceWith(fragment);
81
+ }
82
+ root.replaceChildren(copy, visual);
83
+ scope.add(() => root.replaceChildren(...original));
84
+
85
+ // Letter centres relative to the root, re-measured when layout or fonts change.
86
+ const measure = () => {
87
+ const box = root.getBoundingClientRect();
88
+ for (const letter of letters) {
89
+ const r = letter.span.getBoundingClientRect();
90
+ letter.x = r.left + r.width / 2 - box.left;
91
+ letter.y = r.top + r.height / 2 - box.top;
92
+ }
93
+ };
94
+ measure();
95
+ if (typeof ResizeObserver === 'function') scope.observe(new ResizeObserver(measure)).observe(root);
96
+ document.fonts?.ready.then(() => { if (!scope.disposed) measure(); });
97
+
98
+ const pointer = { x: FAR, y: FAR };
99
+ let frame = 0;
100
+ const tick = () => {
101
+ frame = 0;
102
+ if (scope.disposed) return;
103
+ const box = root.getBoundingClientRect();
104
+ let moving = false;
105
+ for (const letter of letters) {
106
+ const distance = Math.hypot(pointer.x - (box.left + letter.x), pointer.y - (box.top + letter.y));
107
+ const d = Math.max(0, 1 - distance / radius);
108
+ const target = rest + (near - rest) * d * d * (3 - 2 * d);
109
+ letter.value += (target - letter.value) * lerp;
110
+ if (Math.abs(target - letter.value) > 0.5) moving = true;
111
+ else letter.value = target;
112
+ letter.span.style.fontVariationSettings = setting(letter.value);
113
+ }
114
+ if (moving) frame = requestAnimationFrame(tick);
115
+ };
116
+ const wake = () => { if (!frame && !scope.disposed) frame = requestAnimationFrame(tick); };
117
+ scope.add(() => cancelAnimationFrame(frame));
118
+ scope.on(window, 'pointermove', (event) => {
119
+ const e = event as PointerEvent;
120
+ if (!options.anyPointer && e.pointerType && e.pointerType !== 'mouse') return;
121
+ pointer.x = e.clientX;
122
+ pointer.y = e.clientY;
123
+ wake();
124
+ }, { passive: true });
125
+ scope.on(document, 'pointerleave', () => { pointer.x = pointer.y = FAR; wake(); });
126
+
127
+ return {
128
+ pointAt(point) {
129
+ if (scope.disposed) return;
130
+ pointer.x = point ? point.x : FAR;
131
+ pointer.y = point ? point.y : FAR;
132
+ wake();
133
+ },
134
+ destroy: () => scope.dispose(),
135
+ };
136
+ }
@@ -0,0 +1,7 @@
1
+ /* weight-hover — each letter carries its own axis value; the font must be variable. */
2
+ .mk-weight-hover {
3
+ font-kerning: none;
4
+ }
5
+ .mk-weight-hover__visual [data-mk-weight-char] {
6
+ will-change: font-variation-settings;
7
+ }
@@ -2,8 +2,6 @@
2
2
 
3
3
  Cada palabra (o letra) crece desde la base con rebote: `scaleY 0 → 1`, 100 ms entre palabras (60 ms entre letras).
4
4
 
5
- Origen: spencergabor.com — titulares del Hero.
6
-
7
5
  ## Vanilla / Astro
8
6
 
9
7
  ```html
@@ -3,7 +3,7 @@ import { entrances } from '../../core/reduced';
3
3
  import { inert, warnOnce, type Instance } from '../../core/types';
4
4
 
5
5
  /*
6
- * spencergabor.com headlines: every word (or letter) grows from its baseline,
6
+ * words: every word (or letter) grows from its baseline,
7
7
  * scaleY 0 → 1 and 25 % up → 0, elastic, 100 ms apart (60 ms for letters).
8
8
  */
9
9
 
@@ -16,7 +16,7 @@ import { themeCss, type Palette, type ThemePalettes } from './css-format';
16
16
  import { registerTheme, setTheme } from './theme';
17
17
 
18
18
  /*
19
- * Material 3 palettes from any colour, in the same 37 roles as Stepbro's themes.
19
+ * Material 3 palettes from any colour, with the 37 theme color roles.
20
20
  * Needs the optional peer @material/material-color-utilities ^0.3.0
21
21
  * (0.4.0 publishes ESM imports without extensions that Node cannot load).
22
22
  */
@@ -1,4 +1,4 @@
1
- // Generated by scripts/import-themes.mjs from the Stepbro replication kit; do not edit.
1
+ // Theme preset metadata; keep in sync with the CSS palettes.
2
2
 
3
3
  /** The 37 Material 3 colour roles every theme defines (`--md-sys-color-<role>`). */
4
4
  export const ROLES = [
@@ -45,7 +45,7 @@ export type Role = (typeof ROLES)[number];
45
45
  export interface ThemePreset {
46
46
  readonly id: string;
47
47
  readonly name: string;
48
- /** Hidden from Stepbro's own picker (kept in its code). */
48
+ /** Hidden from the picker unless showHidden is enabled. */
49
49
  readonly hidden: boolean;
50
50
  /** Modes with their own palette. OLED is dark-only and uses Black in light mode. */
51
51
  readonly modes: readonly ('light' | 'dark')[];
@@ -3,8 +3,7 @@ import { warnOnce } from '../core/types';
3
3
 
4
4
  /*
5
5
  * Theme and mode on <html data-mk-theme="neogreen" data-mk-mode="system|light|dark">,
6
- * remembered in localStorage (Stepbro stores the palette per app and follows the
7
- * system for light/dark unless told otherwise). Nothing runs at import time.
6
+ * remembered in localStorage; the mode can follow the system or an explicit choice. Nothing runs at import time.
8
7
  */
9
8
 
10
9
  export type ThemeMode = 'light' | 'dark' | 'system';
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/motion/effects.ts","../src/motion/confetti.ts"],"sourcesContent":["import { reduced } from '../core/reduced';\nimport { TIMING } from '../core/timing';\nimport { warnOnce } from '../core/types';\n\n/*\n * Play Stepbro's utility animations (css/effects/anim.css) from code: animate()\n * restarts one even when it is already playing and resolves when it ends;\n * leave() plays an exit and then removes (or hides) the element.\n */\n\nexport const ANIMATIONS = [\n 'out',\n 'out-left',\n 'out-bottom',\n 'collapse',\n 'highlight',\n 'shake',\n 'shake-strong',\n 'fade',\n 'wipe',\n 'progress-in',\n] as const;\nexport type AnimationName = (typeof ANIMATIONS)[number];\nexport type ExitName = 'out' | 'out-left' | 'out-bottom' | 'collapse';\n\nconst EXITS = new Set<AnimationName>(['out', 'out-left', 'out-bottom', 'collapse']);\n\nexport interface AnimateOptions {\n signal?: AbortSignal;\n}\n\nexport function animate(el: Element, name: AnimationName, { signal }: AnimateOptions = {}): Promise<void> {\n const className = `mk-anim-${name}`;\n if (!ANIMATIONS.includes(name)) {\n warnOnce(`animate: unknown animation \"${String(name)}\" (use ${ANIMATIONS.join(', ')})`);\n return Promise.resolve();\n }\n if (reduced()) return Promise.resolve();\n const element = el as HTMLElement;\n if (name === 'collapse') element.style.setProperty('--mk-collapse-from', `${element.offsetHeight}px`);\n // Off, reflow, on: the browser starts the animation over.\n element.classList.remove(className);\n void element.offsetWidth;\n element.classList.add(className);\n return new Promise((resolve) => {\n const finish = (keep: boolean) => {\n element.removeEventListener('animationend', onEnd);\n signal?.removeEventListener('abort', onAbort);\n clearTimeout(timer);\n if (!keep) element.classList.remove(className);\n resolve();\n };\n const onEnd = (event: Event) => {\n if ((event as AnimationEvent).animationName === `mk-${name}`) finish(EXITS.has(name));\n };\n const onAbort = () => finish(false);\n element.addEventListener('animationend', onEnd);\n signal?.addEventListener('abort', onAbort, { once: true });\n const timer = setTimeout(() => finish(EXITS.has(name)), TIMING.effects[name] * 1000 + 100);\n if (signal?.aborted) onAbort();\n });\n}\n\nexport interface LeaveOptions {\n /** Remove the element (default) or hide it. */\n remove?: boolean;\n}\n\nexport async function leave(el: Element, name: ExitName, { remove = true }: LeaveOptions = {}): Promise<void> {\n const focused = el.contains(document.activeElement) ? (document.activeElement as HTMLElement) : null;\n await animate(el, name);\n focused?.blur();\n if (remove) {\n el.remove();\n return;\n }\n (el as HTMLElement).hidden = true;\n el.classList.remove(`mk-anim-${name}`);\n (el as HTMLElement).style.removeProperty('--mk-collapse-from');\n if (el.getAttribute('style') === '') el.removeAttribute('style');\n}\n","import { reduced } from '../core/reduced';\nimport { TIMING } from '../core/timing';\n\n/*\n * Dousan's success confetti: 45 paper pieces burst from a point (2–8 px a\n * frame, a little upward), fall with gravity .15, slow by .98 a frame, spin and\n * fade (.015–.03 a frame). Physics per 60 fps frame, scaled by the real frame\n * time. The canvas is decorative and removes itself when the last piece fades.\n */\n\nexport interface ConfettiOptions {\n count?: number;\n /** Default: the theme's primary, tertiary, secondary and containers. */\n colors?: string[];\n /** Draw inside this (positioned) element instead of over the whole window. */\n container?: HTMLElement | null;\n}\n\nexport interface ConfettiHandle {\n stop(): void;\n finished: Promise<void>;\n}\n\nconst FALLBACK = ['#3DDC84', '#A3E8C1', '#E8D0A3', '#1A73E8', '#8AB4F8'];\nconst ROLES = ['primary', 'tertiary', 'secondary', 'primary-container', 'tertiary-container'];\nconst between = ([min, max]: readonly [number, number]) => min + Math.random() * (max - min);\n\nfunction themeColors(from: Element) {\n const style = getComputedStyle(from);\n const colors = ROLES.map((role) => style.getPropertyValue(`--md-sys-color-${role}`).trim()).filter(Boolean);\n return colors.length ? colors : FALLBACK;\n}\n\nexport function confetti(origin?: Element | { x: number; y: number } | null, options: ConfettiOptions = {}): ConfettiHandle {\n const idle: ConfettiHandle = { stop() {}, finished: Promise.resolve() };\n if (typeof document === 'undefined' || reduced()) return idle;\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (!context) return idle;\n const config = TIMING.confetti;\n const container = options.container ?? null;\n const box = container ? container.getBoundingClientRect() : { left: 0, top: 0, width: innerWidth, height: innerHeight };\n const ratio = window.devicePixelRatio || 1;\n canvas.width = Math.max(1, Math.round(box.width * ratio));\n canvas.height = Math.max(1, Math.round(box.height * ratio));\n canvas.setAttribute('aria-hidden', 'true');\n canvas.dataset.mkConfetti = '';\n Object.assign(canvas.style, {\n position: container ? 'absolute' : 'fixed',\n inset: '0',\n width: container ? '100%' : '100vw',\n height: container ? '100%' : '100vh',\n zIndex: '2147483647',\n pointerEvents: 'none',\n });\n context.scale(ratio, ratio);\n\n let x = box.width / 2;\n let y = box.height / 2;\n if (origin instanceof Element) {\n const rect = origin.getBoundingClientRect();\n x = rect.left + rect.width / 2 - box.left;\n y = rect.top + rect.height / 2 - box.top;\n } else if (origin) {\n x = origin.x - box.left;\n y = origin.y - box.top;\n }\n const colors = options.colors?.length\n ? options.colors\n : themeColors(origin instanceof Element ? origin : (container ?? document.documentElement));\n const pieces = Array.from({ length: options.count ?? config.count }, () => {\n const angle = Math.random() * Math.PI * 2;\n const speed = between(config.speed);\n return {\n x,\n y,\n vx: Math.cos(angle) * speed,\n vy: Math.sin(angle) * speed - config.lift,\n radius: between(config.radius),\n color: colors[Math.floor(Math.random() * colors.length)]!,\n alpha: 1,\n decay: between(config.decay),\n rotation: Math.random() * 360,\n spin: (Math.random() * 2 - 1) * config.spin,\n };\n });\n\n let frame = 0;\n let last = 0;\n let stopped = false;\n let resolve!: () => void;\n const finished = new Promise<void>((done) => (resolve = done));\n const stop = () => {\n if (stopped) return;\n stopped = true;\n cancelAnimationFrame(frame);\n canvas.remove();\n resolve();\n };\n const draw = (now: number) => {\n const f = last ? Math.min((now - last) / (1000 / 60), 3) : 1;\n last = now;\n context.clearRect(0, 0, box.width, box.height);\n let alive = false;\n const drag = Math.pow(config.drag, f);\n for (const piece of pieces) {\n if (piece.alpha <= 0) continue;\n alive = true;\n piece.x += piece.vx * f;\n piece.y += piece.vy * f;\n piece.vy += config.gravity * f;\n piece.vx *= drag;\n piece.vy *= drag;\n piece.alpha -= piece.decay * f;\n piece.rotation += piece.spin * f;\n context.save();\n context.translate(piece.x, piece.y);\n context.rotate((piece.rotation * Math.PI) / 180);\n context.globalAlpha = Math.max(0, piece.alpha);\n context.fillStyle = piece.color;\n context.fillRect(-piece.radius, -piece.radius / 2, piece.radius * 2, piece.radius);\n context.restore();\n }\n if (alive && !stopped) frame = requestAnimationFrame(draw);\n else stop();\n };\n (container ?? document.body).append(canvas);\n frame = requestAnimationFrame(draw);\n return { stop, finished };\n}\n"],"mappings":"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","names":[]}