@yewdiel21/motion-kit 0.9.1 → 0.14.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 (458) hide show
  1. package/README.md +72 -0
  2. package/dist/auto/index.d.ts +72 -3
  3. package/dist/auto/index.js +139 -3
  4. package/dist/auto/index.js.map +1 -1
  5. package/dist/{chunk-BFC27F2G.js → chunk-6P4P7NU6.js} +2 -218
  6. package/dist/chunk-6P4P7NU6.js.map +1 -0
  7. package/dist/chunk-CU6SXW3E.js +20334 -0
  8. package/dist/chunk-CU6SXW3E.js.map +1 -0
  9. package/dist/{chunk-XDWBVFHQ.js → chunk-EY4BJKC4.js} +1203 -29
  10. package/dist/chunk-EY4BJKC4.js.map +1 -0
  11. package/dist/chunk-QP4QJOEN.js +72 -0
  12. package/dist/chunk-QP4QJOEN.js.map +1 -0
  13. package/dist/css/components/activity-feed.css +240 -0
  14. package/dist/css/components/attachment.css +266 -0
  15. package/dist/css/components/breadcrumb.css +110 -0
  16. package/dist/css/components/calendar.css +159 -0
  17. package/dist/css/components/chart-tooltip.css +168 -0
  18. package/dist/css/components/chart.css +296 -0
  19. package/dist/css/components/code-block.css +264 -0
  20. package/dist/css/components/color-area.css +82 -0
  21. package/dist/css/components/color-field.css +67 -0
  22. package/dist/css/components/color-picker.css +131 -0
  23. package/dist/css/components/color-slider.css +113 -0
  24. package/dist/css/components/command-search.css +252 -0
  25. package/dist/css/components/context-menu.css +23 -0
  26. package/dist/css/components/credit-card.css +288 -0
  27. package/dist/css/components/data-table.css +591 -0
  28. package/dist/css/components/date-range-picker.css +168 -0
  29. package/dist/css/components/date-time-picker.css +178 -0
  30. package/dist/css/components/drawer.css +58 -0
  31. package/dist/css/components/editable-table.css +201 -0
  32. package/dist/css/components/empty-state.css +61 -0
  33. package/dist/css/components/file-explorer.css +633 -0
  34. package/dist/css/components/filter-builder.css +232 -0
  35. package/dist/css/components/hold-confirm.css +87 -0
  36. package/dist/css/components/hover-card.css +20 -0
  37. package/dist/css/components/image-preview.css +195 -0
  38. package/dist/css/components/input-otp.css +10 -0
  39. package/dist/css/components/json-viewer.css +272 -0
  40. package/dist/css/components/like-button.css +78 -0
  41. package/dist/css/components/link-preview.css +275 -0
  42. package/dist/css/components/loading-button.css +77 -0
  43. package/dist/css/components/multi-select.css +267 -0
  44. package/dist/css/components/notification-bell.css +127 -0
  45. package/dist/css/components/pagination.css +11 -0
  46. package/dist/css/components/password-strength.css +183 -0
  47. package/dist/css/components/popover.css +25 -0
  48. package/dist/css/components/progress.css +140 -0
  49. package/dist/css/components/quantity.css +16 -0
  50. package/dist/css/components/questionnaire.css +217 -0
  51. package/dist/css/components/range-calendar.css +186 -0
  52. package/dist/css/components/range-slider.css +192 -0
  53. package/dist/css/components/resizable-panel.css +243 -0
  54. package/dist/css/components/rich-text-editor.css +284 -0
  55. package/dist/css/components/scratch-card.css +75 -0
  56. package/dist/css/components/skeleton.css +108 -0
  57. package/dist/css/components/slot-picker.css +126 -0
  58. package/dist/css/components/split-view.css +19 -0
  59. package/dist/css/components/star-rating.css +80 -0
  60. package/dist/css/components/stat-card.css +164 -0
  61. package/dist/css/components/studio-dock.css +35 -0
  62. package/dist/css/components/swipe-actions.css +105 -0
  63. package/dist/css/components/tag-input.css +244 -0
  64. package/dist/css/components/time-field.css +88 -0
  65. package/dist/css/components/timeline.css +219 -0
  66. package/dist/css/components/tooltip.css +79 -0
  67. package/dist/css/components/trailer.css +37 -0
  68. package/dist/css/components/tree-view.css +210 -0
  69. package/dist/css/components/virtual-list.css +195 -0
  70. package/dist/css/controls.css +65 -0
  71. package/dist/css/tokens.css +185 -181
  72. package/dist/elements/index.d.ts +742 -5
  73. package/dist/elements/index.js +1527 -8
  74. package/dist/elements/index.js.map +1 -1
  75. package/dist/{engine-GDGwQnJE.d.ts → engine-Cn6IhE3z.d.ts} +1 -1
  76. package/dist/engine-QECQnGz1.d.ts +33 -0
  77. package/dist/index.css +10113 -181
  78. package/dist/index.d.ts +89 -5
  79. package/dist/index.js +285 -9
  80. package/dist/index.js.map +1 -1
  81. package/dist/react/index.d.ts +678 -23
  82. package/dist/react/index.js +2389 -7
  83. package/dist/react/index.js.map +1 -1
  84. package/dist/{reduced-B98f8LPs.d.ts → reduced-BBUusld6.d.ts} +206 -2
  85. package/dist/reveal-DEvp6U6T.d.ts +2381 -0
  86. package/dist/{store-pHiUvxP8.d.ts → store-DjkTBX7f.d.ts} +2 -2
  87. package/package.json +1 -1
  88. package/registry.json +583 -104
  89. package/skills/motion-kit/SKILL.md +209 -189
  90. package/src/auto/auto-init.ts +136 -0
  91. package/src/components/activity-feed/README.md +56 -0
  92. package/src/components/activity-feed/activity-feed.css +239 -0
  93. package/src/components/activity-feed/element.ts +36 -0
  94. package/src/components/activity-feed/engine.ts +351 -0
  95. package/src/components/activity-feed/react.tsx +32 -0
  96. package/src/components/attachment/README.md +27 -0
  97. package/src/components/attachment/attachment.css +265 -0
  98. package/src/components/attachment/element.ts +50 -0
  99. package/src/components/attachment/engine.ts +469 -0
  100. package/src/components/attachment/react.tsx +65 -0
  101. package/src/components/breadcrumb/README.md +38 -0
  102. package/src/components/breadcrumb/breadcrumb.css +109 -0
  103. package/src/components/breadcrumb/element.ts +27 -0
  104. package/src/components/breadcrumb/engine.ts +249 -0
  105. package/src/components/breadcrumb/react.tsx +30 -0
  106. package/src/components/calendar/README.md +82 -0
  107. package/src/components/calendar/calendar.css +158 -0
  108. package/src/components/calendar/element.ts +27 -0
  109. package/src/components/calendar/engine.ts +412 -0
  110. package/src/components/calendar/react.tsx +69 -0
  111. package/src/components/chart/README.md +24 -0
  112. package/src/components/chart/chart.css +295 -0
  113. package/src/components/chart/element.ts +41 -0
  114. package/src/components/chart/engine.ts +811 -0
  115. package/src/components/chart/react.tsx +50 -0
  116. package/src/components/chart-tooltip/README.md +53 -0
  117. package/src/components/chart-tooltip/chart-tooltip.css +167 -0
  118. package/src/components/chart-tooltip/element.ts +37 -0
  119. package/src/components/chart-tooltip/engine.ts +442 -0
  120. package/src/components/chart-tooltip/react.tsx +47 -0
  121. package/src/components/code-block/README.md +19 -0
  122. package/src/components/code-block/code-block.css +263 -0
  123. package/src/components/code-block/element.ts +34 -0
  124. package/src/components/code-block/engine.ts +254 -0
  125. package/src/components/code-block/highlight.ts +244 -0
  126. package/src/components/code-block/react.tsx +94 -0
  127. package/src/components/color-area/README.md +50 -0
  128. package/src/components/color-area/color-area.css +81 -0
  129. package/src/components/color-area/element.ts +26 -0
  130. package/src/components/color-area/engine.ts +246 -0
  131. package/src/components/color-area/react.tsx +42 -0
  132. package/src/components/color-field/README.md +43 -0
  133. package/src/components/color-field/color-field.css +66 -0
  134. package/src/components/color-field/element.ts +26 -0
  135. package/src/components/color-field/engine.ts +174 -0
  136. package/src/components/color-field/react.tsx +42 -0
  137. package/src/components/color-picker/README.md +45 -0
  138. package/src/components/color-picker/color-picker.css +130 -0
  139. package/src/components/color-picker/element.ts +45 -0
  140. package/src/components/color-picker/engine.ts +314 -0
  141. package/src/components/color-picker/react.tsx +47 -0
  142. package/src/components/color-slider/README.md +55 -0
  143. package/src/components/color-slider/color-slider.css +112 -0
  144. package/src/components/color-slider/element.ts +26 -0
  145. package/src/components/color-slider/engine.ts +234 -0
  146. package/src/components/color-slider/react.tsx +45 -0
  147. package/src/components/command-search/README.md +15 -0
  148. package/src/components/command-search/command-search.css +251 -0
  149. package/src/components/command-search/element.ts +36 -0
  150. package/src/components/command-search/engine.ts +356 -0
  151. package/src/components/command-search/match.ts +53 -0
  152. package/src/components/command-search/react.tsx +66 -0
  153. package/src/components/context-menu/README.md +31 -0
  154. package/src/components/context-menu/context-menu.css +22 -0
  155. package/src/components/context-menu/element.ts +40 -0
  156. package/src/components/context-menu/engine.ts +153 -0
  157. package/src/components/context-menu/react.tsx +57 -0
  158. package/src/components/credit-card/README.md +20 -0
  159. package/src/components/credit-card/card.ts +77 -0
  160. package/src/components/credit-card/credit-card.css +287 -0
  161. package/src/components/credit-card/element.ts +33 -0
  162. package/src/components/credit-card/engine.ts +410 -0
  163. package/src/components/credit-card/react.tsx +36 -0
  164. package/src/components/data-table/README.md +83 -0
  165. package/src/components/data-table/data-table.css +590 -0
  166. package/src/components/data-table/element.ts +54 -0
  167. package/src/components/data-table/engine.ts +1059 -0
  168. package/src/components/data-table/react.tsx +94 -0
  169. package/src/components/date-range-picker/README.md +51 -0
  170. package/src/components/date-range-picker/date-range-picker.css +167 -0
  171. package/src/components/date-range-picker/element.ts +52 -0
  172. package/src/components/date-range-picker/engine.ts +352 -0
  173. package/src/components/date-range-picker/react.tsx +57 -0
  174. package/src/components/date-time-picker/README.md +48 -0
  175. package/src/components/date-time-picker/date-time-picker.css +177 -0
  176. package/src/components/date-time-picker/element.ts +47 -0
  177. package/src/components/date-time-picker/engine.ts +350 -0
  178. package/src/components/date-time-picker/react.tsx +54 -0
  179. package/src/components/dialog/README.md +103 -102
  180. package/src/components/dialog/element.ts +123 -118
  181. package/src/components/dialog/engine.ts +449 -432
  182. package/src/components/drawer/README.md +28 -0
  183. package/src/components/drawer/drawer.css +57 -0
  184. package/src/components/drawer/element.ts +21 -0
  185. package/src/components/drawer/engine.ts +56 -0
  186. package/src/components/drawer/react.tsx +85 -0
  187. package/src/components/editable-table/README.md +67 -0
  188. package/src/components/editable-table/editable-table.css +200 -0
  189. package/src/components/editable-table/element.ts +45 -0
  190. package/src/components/editable-table/engine.ts +775 -0
  191. package/src/components/editable-table/react.tsx +79 -0
  192. package/src/components/empty-state/README.md +41 -0
  193. package/src/components/empty-state/element.ts +23 -0
  194. package/src/components/empty-state/empty-state.css +60 -0
  195. package/src/components/empty-state/engine.ts +232 -0
  196. package/src/components/empty-state/react.tsx +34 -0
  197. package/src/components/file-explorer/README.md +67 -0
  198. package/src/components/file-explorer/element.ts +39 -0
  199. package/src/components/file-explorer/engine.ts +1307 -0
  200. package/src/components/file-explorer/file-explorer.css +632 -0
  201. package/src/components/file-explorer/react.tsx +77 -0
  202. package/src/components/filter-builder/README.md +47 -0
  203. package/src/components/filter-builder/element.ts +34 -0
  204. package/src/components/filter-builder/engine.ts +365 -0
  205. package/src/components/filter-builder/filter-builder.css +231 -0
  206. package/src/components/filter-builder/react.tsx +36 -0
  207. package/src/components/hold-confirm/README.md +13 -0
  208. package/src/components/hold-confirm/element.ts +24 -0
  209. package/src/components/hold-confirm/engine.ts +216 -0
  210. package/src/components/hold-confirm/hold-confirm.css +86 -0
  211. package/src/components/hold-confirm/react.tsx +28 -0
  212. package/src/components/hover-card/README.md +20 -0
  213. package/src/components/hover-card/element.ts +8 -0
  214. package/src/components/hover-card/engine.ts +131 -0
  215. package/src/components/hover-card/hover-card.css +19 -0
  216. package/src/components/hover-card/react.tsx +19 -0
  217. package/src/components/image-preview/README.md +16 -0
  218. package/src/components/image-preview/element.ts +26 -0
  219. package/src/components/image-preview/engine.ts +325 -0
  220. package/src/components/image-preview/image-preview.css +194 -0
  221. package/src/components/image-preview/react.tsx +43 -0
  222. package/src/components/input-otp/README.md +18 -0
  223. package/src/components/input-otp/element.ts +14 -0
  224. package/src/components/input-otp/engine.ts +106 -0
  225. package/src/components/input-otp/input-otp.css +9 -0
  226. package/src/components/input-otp/react.tsx +21 -0
  227. package/src/components/json-viewer/README.md +56 -0
  228. package/src/components/json-viewer/element.ts +37 -0
  229. package/src/components/json-viewer/engine.ts +440 -0
  230. package/src/components/json-viewer/json-viewer.css +271 -0
  231. package/src/components/json-viewer/react.tsx +56 -0
  232. package/src/components/like-button/README.md +13 -0
  233. package/src/components/like-button/element.ts +47 -0
  234. package/src/components/like-button/engine.ts +145 -0
  235. package/src/components/like-button/like-button.css +77 -0
  236. package/src/components/like-button/react.tsx +39 -0
  237. package/src/components/link-preview/README.md +19 -0
  238. package/src/components/link-preview/element.ts +29 -0
  239. package/src/components/link-preview/engine.ts +192 -0
  240. package/src/components/link-preview/link-preview.css +274 -0
  241. package/src/components/link-preview/react.tsx +101 -0
  242. package/src/components/loading-button/README.md +45 -0
  243. package/src/components/loading-button/element.ts +34 -0
  244. package/src/components/loading-button/engine.ts +201 -0
  245. package/src/components/loading-button/loading-button.css +76 -0
  246. package/src/components/loading-button/react.tsx +43 -0
  247. package/src/components/multi-select/README.md +48 -0
  248. package/src/components/multi-select/element.ts +28 -0
  249. package/src/components/multi-select/engine.ts +294 -0
  250. package/src/components/multi-select/multi-select.css +266 -0
  251. package/src/components/multi-select/react.tsx +39 -0
  252. package/src/components/notification-bell/README.md +14 -0
  253. package/src/components/notification-bell/element.ts +48 -0
  254. package/src/components/notification-bell/engine.ts +202 -0
  255. package/src/components/notification-bell/notification-bell.css +126 -0
  256. package/src/components/notification-bell/react.tsx +57 -0
  257. package/src/components/pagination/README.md +17 -0
  258. package/src/components/pagination/element.ts +14 -0
  259. package/src/components/pagination/engine.ts +102 -0
  260. package/src/components/pagination/pagination.css +10 -0
  261. package/src/components/pagination/react.tsx +22 -0
  262. package/src/components/password-strength/README.md +17 -0
  263. package/src/components/password-strength/element.ts +22 -0
  264. package/src/components/password-strength/engine.ts +212 -0
  265. package/src/components/password-strength/password-strength.css +182 -0
  266. package/src/components/password-strength/react.tsx +36 -0
  267. package/src/components/password-strength/score.ts +62 -0
  268. package/src/components/popover/README.md +44 -0
  269. package/src/components/popover/element.ts +31 -0
  270. package/src/components/popover/engine.ts +114 -0
  271. package/src/components/popover/popover.css +24 -0
  272. package/src/components/popover/react.tsx +50 -0
  273. package/src/components/progress/README.md +17 -0
  274. package/src/components/progress/element.ts +30 -0
  275. package/src/components/progress/engine.ts +173 -0
  276. package/src/components/progress/progress.css +139 -0
  277. package/src/components/progress/react.tsx +35 -0
  278. package/src/components/quantity/README.md +20 -0
  279. package/src/components/quantity/element.ts +14 -0
  280. package/src/components/quantity/engine.ts +85 -0
  281. package/src/components/quantity/quantity.css +15 -0
  282. package/src/components/quantity/react.tsx +21 -0
  283. package/src/components/questionnaire/README.md +32 -0
  284. package/src/components/questionnaire/element.ts +118 -0
  285. package/src/components/questionnaire/engine.ts +255 -0
  286. package/src/components/questionnaire/questionnaire.css +216 -0
  287. package/src/components/questionnaire/react.tsx +140 -0
  288. package/src/components/range-calendar/README.md +58 -0
  289. package/src/components/range-calendar/element.ts +42 -0
  290. package/src/components/range-calendar/engine.ts +390 -0
  291. package/src/components/range-calendar/range-calendar.css +185 -0
  292. package/src/components/range-calendar/react.tsx +56 -0
  293. package/src/components/range-slider/README.md +23 -0
  294. package/src/components/range-slider/element.ts +53 -0
  295. package/src/components/range-slider/engine.ts +316 -0
  296. package/src/components/range-slider/range-slider.css +191 -0
  297. package/src/components/range-slider/react.tsx +66 -0
  298. package/src/components/resizable-panel/README.md +61 -0
  299. package/src/components/resizable-panel/element.ts +36 -0
  300. package/src/components/resizable-panel/engine.ts +551 -0
  301. package/src/components/resizable-panel/react.tsx +74 -0
  302. package/src/components/resizable-panel/resizable-panel.css +242 -0
  303. package/src/components/rich-text-editor/README.md +57 -0
  304. package/src/components/rich-text-editor/element.ts +34 -0
  305. package/src/components/rich-text-editor/engine.ts +673 -0
  306. package/src/components/rich-text-editor/react.tsx +60 -0
  307. package/src/components/rich-text-editor/rich-text-editor.css +283 -0
  308. package/src/components/scratch-card/README.md +15 -0
  309. package/src/components/scratch-card/element.ts +30 -0
  310. package/src/components/scratch-card/engine.ts +257 -0
  311. package/src/components/scratch-card/react.tsx +40 -0
  312. package/src/components/scratch-card/scratch-card.css +74 -0
  313. package/src/components/skeleton/README.md +15 -0
  314. package/src/components/skeleton/element.ts +32 -0
  315. package/src/components/skeleton/engine.ts +104 -0
  316. package/src/components/skeleton/react.tsx +41 -0
  317. package/src/components/skeleton/skeleton.css +107 -0
  318. package/src/components/slot-picker/README.md +68 -0
  319. package/src/components/slot-picker/element.ts +27 -0
  320. package/src/components/slot-picker/engine.ts +412 -0
  321. package/src/components/slot-picker/react.tsx +76 -0
  322. package/src/components/slot-picker/slot-picker.css +125 -0
  323. package/src/components/split-view/README.md +50 -0
  324. package/src/components/split-view/element.ts +34 -0
  325. package/src/components/split-view/engine.ts +114 -0
  326. package/src/components/split-view/react.tsx +63 -0
  327. package/src/components/split-view/split-view.css +18 -0
  328. package/src/components/star-rating/README.md +14 -0
  329. package/src/components/star-rating/element.ts +26 -0
  330. package/src/components/star-rating/engine.ts +217 -0
  331. package/src/components/star-rating/react.tsx +33 -0
  332. package/src/components/star-rating/star-rating.css +79 -0
  333. package/src/components/stat-card/README.md +42 -0
  334. package/src/components/stat-card/element.ts +40 -0
  335. package/src/components/stat-card/engine.ts +283 -0
  336. package/src/components/stat-card/react.tsx +36 -0
  337. package/src/components/stat-card/stat-card.css +163 -0
  338. package/src/components/studio-dock/README.md +44 -0
  339. package/src/components/studio-dock/element.ts +11 -0
  340. package/src/components/studio-dock/engine.ts +212 -0
  341. package/src/components/studio-dock/react.tsx +23 -0
  342. package/src/components/studio-dock/studio-dock.css +34 -0
  343. package/src/components/swipe-actions/README.md +19 -0
  344. package/src/components/swipe-actions/element.ts +26 -0
  345. package/src/components/swipe-actions/engine.ts +260 -0
  346. package/src/components/swipe-actions/react.tsx +80 -0
  347. package/src/components/swipe-actions/swipe-actions.css +104 -0
  348. package/src/components/tag-input/README.md +42 -0
  349. package/src/components/tag-input/element.ts +39 -0
  350. package/src/components/tag-input/engine.ts +313 -0
  351. package/src/components/tag-input/react.tsx +54 -0
  352. package/src/components/tag-input/tag-input.css +243 -0
  353. package/src/components/time-field/README.md +54 -0
  354. package/src/components/time-field/element.ts +38 -0
  355. package/src/components/time-field/engine.ts +110 -0
  356. package/src/components/time-field/react.tsx +45 -0
  357. package/src/components/time-field/time-field.css +87 -0
  358. package/src/components/timeline/README.md +57 -0
  359. package/src/components/timeline/element.ts +31 -0
  360. package/src/components/timeline/engine.ts +300 -0
  361. package/src/components/timeline/react.tsx +59 -0
  362. package/src/components/timeline/timeline.css +218 -0
  363. package/src/components/tooltip/README.md +46 -0
  364. package/src/components/tooltip/element.ts +22 -0
  365. package/src/components/tooltip/engine.ts +288 -0
  366. package/src/components/tooltip/react.tsx +30 -0
  367. package/src/components/tooltip/tooltip.css +78 -0
  368. package/src/components/trailer/README.md +28 -0
  369. package/src/components/trailer/element.ts +13 -0
  370. package/src/components/trailer/engine.ts +189 -0
  371. package/src/components/trailer/react.tsx +16 -0
  372. package/src/components/trailer/trailer.css +36 -0
  373. package/src/components/tree-view/README.md +61 -0
  374. package/src/components/tree-view/element.ts +32 -0
  375. package/src/components/tree-view/engine.ts +569 -0
  376. package/src/components/tree-view/react.tsx +78 -0
  377. package/src/components/tree-view/tree-view.css +209 -0
  378. package/src/components/virtual-list/README.md +81 -0
  379. package/src/components/virtual-list/element.ts +42 -0
  380. package/src/components/virtual-list/engine.ts +678 -0
  381. package/src/components/virtual-list/react.tsx +101 -0
  382. package/src/components/virtual-list/virtual-list.css +194 -0
  383. package/src/core/chips.ts +196 -0
  384. package/src/core/color.ts +304 -0
  385. package/src/core/date-range.ts +67 -0
  386. package/src/core/dates.ts +108 -0
  387. package/src/core/files.ts +107 -0
  388. package/src/core/filter.ts +229 -0
  389. package/src/core/gsap.ts +80 -65
  390. package/src/core/listbox.ts +332 -0
  391. package/src/core/mark.ts +29 -0
  392. package/src/core/position.ts +89 -0
  393. package/src/core/relative-time.ts +77 -0
  394. package/src/core/segment-field.ts +373 -0
  395. package/src/core/segments.ts +153 -0
  396. package/src/core/tree.ts +116 -0
  397. package/src/core/virtual.ts +79 -0
  398. package/src/css/controls/README.md +14 -0
  399. package/src/css/controls/status.css +63 -0
  400. package/src/css/tokens.css +186 -182
  401. package/src/elements/index.ts +171 -0
  402. package/src/index.ts +97 -0
  403. package/src/motion/micro.ts +248 -0
  404. package/src/motion/sheet.ts +88 -65
  405. package/src/motion/surface.ts +9 -0
  406. package/src/react/index.ts +78 -0
  407. package/src/react/use-action-button.ts +6 -0
  408. package/src/react/use-editorial-reveal.ts +7 -0
  409. package/src/react/use-film-hero.ts +6 -0
  410. package/src/react/use-font-cycle.ts +7 -0
  411. package/src/react/use-image-zoom.ts +5 -0
  412. package/src/react/use-joined-cta.ts +5 -0
  413. package/src/react/use-local-clock.ts +5 -0
  414. package/src/react/use-scroll-dock.ts +5 -0
  415. package/src/react/use-sticker.ts +5 -0
  416. package/src/react/use-studio-dock.ts +8 -0
  417. package/src/react/use-thumbnail-nav.ts +7 -0
  418. package/src/react/use-trailer.ts +6 -0
  419. package/src/react/use-weight-hover.ts +5 -0
  420. package/src/recipes/action-button/README.md +31 -0
  421. package/src/recipes/action-button/action-button.css +113 -0
  422. package/src/recipes/action-button/create-action-button.ts +56 -0
  423. package/src/recipes/editorial-reveal/README.md +35 -0
  424. package/src/recipes/editorial-reveal/create-editorial-reveal.ts +91 -0
  425. package/src/recipes/editorial-reveal/editorial-reveal.css +17 -0
  426. package/src/recipes/film-hero/README.md +23 -0
  427. package/src/recipes/film-hero/create-film-hero.ts +65 -0
  428. package/src/recipes/film-hero/film-hero.css +29 -0
  429. package/src/recipes/font-cycle/README.md +11 -0
  430. package/src/recipes/font-cycle/create-font-cycle.ts +126 -0
  431. package/src/recipes/font-cycle/font-cycle.css +36 -0
  432. package/src/recipes/image-zoom/README.md +16 -0
  433. package/src/recipes/image-zoom/create-image-zoom.ts +27 -0
  434. package/src/recipes/image-zoom/image-zoom.css +7 -0
  435. package/src/recipes/joined-cta/README.md +13 -0
  436. package/src/recipes/joined-cta/create-joined-cta.ts +22 -0
  437. package/src/recipes/joined-cta/joined-cta.css +17 -0
  438. package/src/recipes/local-clock/README.md +31 -0
  439. package/src/recipes/local-clock/create-local-clock.ts +61 -0
  440. package/src/recipes/scroll-dock/README.md +46 -0
  441. package/src/recipes/scroll-dock/create-scroll-dock.ts +72 -0
  442. package/src/recipes/scroll-dock/scroll-dock.css +70 -0
  443. package/src/recipes/sticker/README.md +59 -0
  444. package/src/recipes/sticker/create-sticker.ts +490 -0
  445. package/src/recipes/sticker/sticker.css +112 -0
  446. package/src/recipes/thumbnail-nav/README.md +16 -0
  447. package/src/recipes/thumbnail-nav/create-thumbnail-nav.ts +165 -0
  448. package/src/recipes/thumbnail-nav/thumbnail-nav.css +108 -0
  449. package/src/recipes/weight-hover/README.md +37 -0
  450. package/src/recipes/weight-hover/create-weight-hover.ts +136 -0
  451. package/src/recipes/weight-hover/weight-hover.css +7 -0
  452. package/dist/chunk-BFC27F2G.js.map +0 -1
  453. package/dist/chunk-J6OCFNGU.js +0 -177
  454. package/dist/chunk-J6OCFNGU.js.map +0 -1
  455. package/dist/chunk-JQMJQSXD.js +0 -2374
  456. package/dist/chunk-JQMJQSXD.js.map +0 -1
  457. package/dist/chunk-XDWBVFHQ.js.map +0 -1
  458. package/dist/reveal-C0hCDcB_.d.ts +0 -415
@@ -0,0 +1,40 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createStatCard, type StatCardInstance } from './engine';
3
+
4
+ /**
5
+ * <mk-stat-card class="mk-stat-card" label="Ingresos" value="128400" previous="118900"
6
+ * format='{"style":"currency","currency":"MXN","maximumFractionDigits":0}' series="90,98,104,101,118,128"></mk-stat-card>
7
+ * Changing value, previous, series or target updates it in place (the figure counts).
8
+ */
9
+ export class MkStatCardElement extends MkElement<StatCardInstance> {
10
+ static options: Record<string, OptionType> = {
11
+ label: 'string',
12
+ value: 'number',
13
+ previous: 'number',
14
+ format: 'json',
15
+ locale: 'string',
16
+ positive: 'string',
17
+ comparison: 'string',
18
+ series: 'list',
19
+ target: 'number',
20
+ countOnView: 'boolean',
21
+ };
22
+
23
+ protected create(options: Record<string, unknown>) {
24
+ return createStatCard(this, normalise(options));
25
+ }
26
+
27
+ attributeChangedCallback(name: string, previous: string | null, next: string | null) {
28
+ if (previous === next || !this.instance) return;
29
+ if (name === 'locale' || name === 'count-on-view') return super.attributeChangedCallback(name, previous, next);
30
+ this.instance.update(normalise(this.options));
31
+ }
32
+ }
33
+
34
+ /** The series attribute is a comma list: numbers. */
35
+ const normalise = (options: Record<string, unknown>) => ({
36
+ ...options,
37
+ series: Array.isArray(options.series) ? (options.series as string[]).map(Number).filter(Number.isFinite) : undefined,
38
+ });
39
+
40
+ export const defineStatCard = () => defineElement('mk-stat-card', MkStatCardElement);
@@ -0,0 +1,283 @@
1
+ import { gsap, EASE, registerMotion } from '../../core/gsap';
2
+ import { reduced } from '../../core/reduced';
3
+ import { createScope } from '../../core/scope';
4
+ import { inert, type Instance } from '../../core/types';
5
+ import { countTo } from '../../motion/count';
6
+ import { rollDigits } from '../../motion/micro';
7
+
8
+ /*
9
+ * Stat card: a KPI with its trend. The figure (Intl format of the page: money,
10
+ * percent, compact) counts up when the card comes into view (.9 s, flow); the
11
+ * change from `previous` sits in a badge (▲ / ▼ and the percentage, green when
12
+ * it moves the good way — `positive` says which — red when not, neutral when
13
+ * flat) that pops in after it while its arrow nudges; a sparkline of `series`
14
+ * draws itself (.9 s, wipe) with a soft area and an end dot that pops; a
15
+ * `target` fills a bar. update() counts from the shown figure to the new one,
16
+ * rolls the badge's digits, turns the arrow when the trend flips and morphs the
17
+ * sparkline to the new points. The visual figure is aria-hidden: a sentence
18
+ * with the final values speaks for it.
19
+ */
20
+
21
+ export interface StatCardOptions {
22
+ label?: string;
23
+ value?: number;
24
+ /** The value it is compared with (the previous period). */
25
+ previous?: number;
26
+ /** Intl.NumberFormat options (e.g. { style: 'currency', currency: 'MXN' }) or a formatter. */
27
+ format?: Intl.NumberFormatOptions | ((value: number) => string);
28
+ /** Locale for the numbers (default: the page's language). */
29
+ locale?: string;
30
+ /** Which way is good (default 'up'; 'down' for costs, churn, latency). */
31
+ positive?: 'up' | 'down';
32
+ /** What the change compares with (default 'vs. periodo anterior'). */
33
+ comparison?: string;
34
+ /** Values for the sparkline (two or more). */
35
+ series?: number[];
36
+ /** A goal: a bar shows how far the value is. */
37
+ target?: number;
38
+ /** Count when it comes into view (default true); false shows the figure at once. */
39
+ countOnView?: boolean;
40
+ }
41
+
42
+ export interface StatCardInstance extends Instance {
43
+ update(next: Omit<StatCardOptions, 'countOnView' | 'locale'>): void;
44
+ }
45
+
46
+ type Trend = 'up' | 'down' | 'flat';
47
+ const SVG = 'http://www.w3.org/2000/svg';
48
+ const ARROW = '<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M6 11l6-6 6 6"/></svg>';
49
+
50
+ /** Sparkline points in a 100 × 32 box (2 px of air top and bottom). */
51
+ const pointsOf = (series: number[]) => {
52
+ const lo = Math.min(...series);
53
+ const hi = Math.max(...series);
54
+ const span = hi - lo || 1;
55
+ return series.map((value, i) => [series.length === 1 ? 50 : (i / (series.length - 1)) * 100, 30 - ((value - lo) / span) * 28] as [number, number]);
56
+ };
57
+ const pathOf = (points: Array<[number, number]>) => `M${points.map(([x, y]) => `${x.toFixed(2)},${y.toFixed(2)}`).join('L')}`;
58
+
59
+ export function createStatCard(root: HTMLElement | null, initial: StatCardOptions = {}): StatCardInstance {
60
+ if (!root) return inert({ update() {} });
61
+ registerMotion();
62
+ const scope = createScope();
63
+ let options: StatCardOptions = { ...initial };
64
+ const effects = new Set<gsap.core.Animation>();
65
+ const keep = (animation: gsap.core.Animation | null) => {
66
+ if (!animation) return;
67
+ effects.add(animation);
68
+ // Finished ones leave as new ones arrive: the animation's own onComplete stays untouched.
69
+ for (const effect of effects) if (effect.totalProgress() === 1) effects.delete(effect);
70
+ };
71
+ const locale = options.locale ?? root.closest('[lang]')?.getAttribute('lang') ?? undefined;
72
+ const formatter = () => {
73
+ if (typeof options.format === 'function') return options.format;
74
+ let intl: Intl.NumberFormat;
75
+ try {
76
+ intl = new Intl.NumberFormat(locale, { maximumFractionDigits: 1, ...options.format });
77
+ } catch {
78
+ intl = new Intl.NumberFormat(undefined, { maximumFractionDigits: 1 });
79
+ }
80
+ return (value: number) => intl.format(value);
81
+ };
82
+ const percent = new Intl.NumberFormat(locale, { maximumFractionDigits: 1, minimumFractionDigits: 1 });
83
+
84
+ const el = <K extends keyof HTMLElementTagNameMap>(tag: K, attribute: string) => {
85
+ const element = document.createElement(tag);
86
+ element.setAttribute(attribute, '');
87
+ return element;
88
+ };
89
+ const labelEl = el('span', 'data-mk-stat-label');
90
+ const row = el('div', 'data-mk-stat-row');
91
+ const valueEl = el('strong', 'data-mk-stat-value');
92
+ valueEl.setAttribute('aria-hidden', 'true');
93
+ const badge = el('span', 'data-mk-stat-delta');
94
+ badge.setAttribute('aria-hidden', 'true');
95
+ const arrow = el('span', 'data-mk-stat-arrow');
96
+ arrow.innerHTML = ARROW;
97
+ const deltaText = el('span', 'data-mk-stat-delta-text');
98
+ badge.append(arrow, deltaText);
99
+ row.append(valueEl, badge);
100
+ const comparisonEl = el('p', 'data-mk-stat-comparison');
101
+ comparisonEl.setAttribute('aria-hidden', 'true');
102
+ const sparkHost = el('div', 'data-mk-stat-spark');
103
+ sparkHost.setAttribute('aria-hidden', 'true');
104
+ const svg = document.createElementNS(SVG, 'svg');
105
+ svg.setAttribute('viewBox', '0 0 100 32');
106
+ svg.setAttribute('preserveAspectRatio', 'none');
107
+ const area = document.createElementNS(SVG, 'path');
108
+ area.setAttribute('data-mk-stat-area', '');
109
+ const line = document.createElementNS(SVG, 'path');
110
+ line.setAttribute('data-mk-stat-line', '');
111
+ line.setAttribute('vector-effect', 'non-scaling-stroke');
112
+ line.setAttribute('pathLength', '1');
113
+ svg.append(area, line);
114
+ const dot = el('span', 'data-mk-stat-dot');
115
+ sparkHost.append(svg, dot);
116
+ const target = el('div', 'data-mk-stat-target');
117
+ const targetBar = el('span', 'data-mk-stat-target-bar');
118
+ const targetFill = el('span', 'data-mk-stat-target-fill');
119
+ targetBar.append(targetFill);
120
+ const targetText = el('span', 'data-mk-stat-target-text');
121
+ target.append(targetBar, targetText);
122
+ const summary = el('span', 'data-mk-stat-summary');
123
+ summary.className = 'mk-sr-only';
124
+ root.append(labelEl, row, comparisonEl, sparkHost, target, summary);
125
+ scope.add(() => {
126
+ for (const effect of effects) effect.kill();
127
+ effects.clear();
128
+ gsap.killTweensOf([valueEl, badge, arrow, line, area, dot, targetFill]);
129
+ for (const part of [labelEl, row, comparisonEl, sparkHost, target, summary]) part.remove();
130
+ });
131
+ scope.attr(root, 'role', 'group');
132
+ scope.attr(root, 'aria-label', options.label ?? null);
133
+ scope.attr(root, 'data-mk-trend', null);
134
+ scope.attr(root, 'data-mk-good', null);
135
+ scope.attr(root, 'data-mk-ready', '');
136
+
137
+ const trendOf = (): { trend: Trend; change: number | null } => {
138
+ const { value, previous } = options;
139
+ if (typeof value !== 'number' || typeof previous !== 'number' || !Number.isFinite(value) || !Number.isFinite(previous) || previous === 0) return { trend: 'flat', change: null };
140
+ const change = ((value - previous) / Math.abs(previous)) * 100;
141
+ return { trend: Math.abs(change) < 0.05 ? 'flat' : change > 0 ? 'up' : 'down', change };
142
+ };
143
+
144
+ let shownPoints: Array<[number, number]> | null = null;
145
+ const drawSpark = (points: Array<[number, number]>) => {
146
+ line.setAttribute('d', pathOf(points));
147
+ area.setAttribute('d', `${pathOf(points)}L100,32L0,32Z`);
148
+ const [x, y] = points[points.length - 1]!;
149
+ dot.style.left = `${x}%`;
150
+ dot.style.top = `${(y / 32) * 100}%`;
151
+ };
152
+
153
+ /** Write everything but the figure; `animate` runs the update motion. */
154
+ const paint = (animate: boolean, previousTrend: Trend | null) => {
155
+ const format = formatter();
156
+ labelEl.textContent = options.label ?? '';
157
+ labelEl.hidden = !options.label;
158
+ if (options.label) root.setAttribute('aria-label', options.label);
159
+ else root.removeAttribute('aria-label');
160
+ const { trend, change } = trendOf();
161
+ const good = trend === 'flat' ? 'neutral' : (trend === 'up') === ((options.positive ?? 'up') === 'up') ? 'good' : 'bad';
162
+ root.setAttribute('data-mk-trend', trend);
163
+ root.setAttribute('data-mk-good', good);
164
+ badge.hidden = change === null;
165
+ const text = change === null ? '' : `${trend === 'flat' ? '' : trend === 'up' ? '+' : '−'}${percent.format(Math.abs(change))} %`;
166
+ if (animate && !reduced() && deltaText.textContent && deltaText.textContent !== text) keep(rollDigits(deltaText, text, trend === 'down' ? -1 : 1, 0.3));
167
+ else deltaText.textContent = text;
168
+ if (animate && previousTrend && previousTrend !== trend && !reduced()) keep(gsap.fromTo(arrow, { scale: 0.6 }, { scale: 1, duration: 0.35, ease: EASE.click, clearProps: 'scale' }));
169
+ const comparison = options.comparison ?? 'vs. periodo anterior';
170
+ comparisonEl.textContent = typeof options.previous === 'number' ? `${comparison} (${format(options.previous)})` : '';
171
+ comparisonEl.hidden = typeof options.previous !== 'number';
172
+ // Sparkline: draw, or morph from the shown points.
173
+ const series = (options.series ?? []).filter((value) => Number.isFinite(value));
174
+ sparkHost.hidden = series.length < 2;
175
+ if (series.length >= 2) {
176
+ const next = pointsOf(series);
177
+ if (animate && shownPoints && shownPoints.length === next.length && !reduced()) {
178
+ const from = shownPoints.map((point) => [...point]) as Array<[number, number]>;
179
+ const state = { t: 0 };
180
+ keep(
181
+ gsap.to(state, {
182
+ t: 1,
183
+ duration: 0.6,
184
+ ease: EASE.swatch,
185
+ onUpdate: () => drawSpark(from.map(([x, y], i) => [x + (next[i]![0] - x) * state.t, y + (next[i]![1] - y) * state.t])),
186
+ }),
187
+ );
188
+ } else drawSpark(next);
189
+ shownPoints = next;
190
+ } else shownPoints = null;
191
+ // Target bar.
192
+ const goal = options.target;
193
+ const hasTarget = typeof goal === 'number' && goal > 0 && typeof options.value === 'number';
194
+ target.hidden = !hasTarget;
195
+ if (hasTarget) {
196
+ const ratio = Math.max(0, Math.min(1, options.value! / goal!));
197
+ targetText.textContent = `${percent.format(ratio * 100)} % de la meta (${format(goal!)})`;
198
+ if (animate && !reduced()) keep(gsap.to(targetFill, { scaleX: ratio, duration: 0.6, ease: EASE.wipe }));
199
+ else gsap.set(targetFill, { scaleX: ratio });
200
+ }
201
+ // The sentence that speaks for the figure.
202
+ const value = typeof options.value === 'number' ? format(options.value) : '—';
203
+ const said = change === null ? '' : trend === 'flat' ? ` Sin cambio ${comparison}.` : ` ${trend === 'up' ? 'Sube' : 'Baja'} ${percent.format(Math.abs(change))} % ${comparison}.`;
204
+ summary.textContent = `${options.label ? `${options.label}: ` : ''}${value}.${said}`;
205
+ };
206
+
207
+ /** The figure: counted (from `from`) or written. */
208
+ const figure = (from: number | null) => {
209
+ const value = options.value;
210
+ if (typeof value !== 'number') {
211
+ valueEl.textContent = '—';
212
+ return;
213
+ }
214
+ if (from === null || reduced()) {
215
+ countTo(valueEl, value, { from: value, format: formatter() });
216
+ return;
217
+ }
218
+ keep(countTo(valueEl, value, { from, duration: 0.9, format: formatter() }));
219
+ };
220
+
221
+ /** The first appearance: figure counts, badge pops, sparkline draws, target fills. */
222
+ const enter = () => {
223
+ paint(false, null);
224
+ if (reduced() || options.countOnView === false) {
225
+ figure(null);
226
+ return;
227
+ }
228
+ figure(0);
229
+ if (!badge.hidden) {
230
+ keep(gsap.fromTo(badge, { opacity: 0, scale: 0.6, filter: 'blur(4px)' }, { opacity: 1, scale: 1, filter: 'blur(0px)', duration: 0.35, delay: 0.45, ease: EASE.pop, clearProps: 'opacity,transform,filter' }));
231
+ keep(gsap.fromTo(arrow, { y: root.dataset.mkTrend === 'down' ? -4 : 4 }, { y: 0, duration: 0.45, delay: 0.6, ease: EASE.click, clearProps: 'transform' }));
232
+ }
233
+ if (!sparkHost.hidden) {
234
+ keep(gsap.fromTo(line, { strokeDasharray: 1, strokeDashoffset: 1 }, { strokeDashoffset: 0, duration: 0.9, ease: EASE.wipe, clearProps: 'strokeDasharray,strokeDashoffset' }));
235
+ keep(gsap.fromTo(area, { opacity: 0 }, { opacity: 1, duration: 0.5, delay: 0.4, ease: 'power1.out', clearProps: 'opacity' }));
236
+ keep(gsap.fromTo(dot, { scale: 0 }, { scale: 1, duration: 0.3, delay: 0.85, ease: EASE.pop, clearProps: 'scale' }));
237
+ }
238
+ if (!target.hidden) {
239
+ const ratio = gsap.getProperty(targetFill, 'scaleX') as number;
240
+ keep(gsap.fromTo(targetFill, { scaleX: 0 }, { scaleX: ratio, duration: 0.8, delay: 0.2, ease: EASE.wipe }));
241
+ }
242
+ };
243
+
244
+ let entered = false;
245
+ if (typeof IntersectionObserver === 'function' && options.countOnView !== false && !reduced()) {
246
+ paint(false, null);
247
+ valueEl.textContent = formatter()(0);
248
+ gsap.set([badge, dot], { opacity: 0 });
249
+ const observer = scope.observe(
250
+ new IntersectionObserver((entries) => {
251
+ if (entered || !entries.some((entry) => entry.isIntersecting)) return;
252
+ entered = true;
253
+ gsap.set([badge, dot], { clearProps: 'opacity' });
254
+ observer.disconnect();
255
+ enter();
256
+ }, { threshold: 0.4 }),
257
+ );
258
+ observer.observe(root);
259
+ } else {
260
+ entered = true;
261
+ paint(false, null);
262
+ figure(null);
263
+ }
264
+
265
+ return {
266
+ update(next) {
267
+ if (scope.disposed) return;
268
+ const previousTrend = (root.dataset.mkTrend as Trend | undefined) ?? null;
269
+ options = { ...options, ...next };
270
+ if (!entered) {
271
+ paint(false, null);
272
+ return;
273
+ }
274
+ paint(true, previousTrend);
275
+ const value = options.value;
276
+ if (typeof value !== 'number') valueEl.textContent = '—';
277
+ // countTo carries on from the figure it last wrote.
278
+ else if (reduced()) countTo(valueEl, value, { from: value, format: formatter() });
279
+ else keep(countTo(valueEl, value, { duration: 0.9, format: formatter() }));
280
+ },
281
+ destroy: () => scope.dispose(),
282
+ };
283
+ }
@@ -0,0 +1,36 @@
1
+ 'use client';
2
+ import { useRef, type HTMLAttributes } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createStatCard, type StatCardInstance, type StatCardOptions } from './engine';
5
+
6
+ export interface StatCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, StatCardOptions {}
7
+
8
+ /** <StatCard label="Ingresos" value={128400} previous={118900} format={{ style: 'currency', currency: 'MXN' }} series={week} />. */
9
+ export function StatCard({ label, value, previous, format, locale, positive, comparison, series, target, countOnView, className, ...props }: StatCardProps) {
10
+ const ref = useRef<HTMLDivElement>(null);
11
+ const instance = useRef<StatCardInstance | null>(null);
12
+ const formatter = useLatest(format);
13
+ const custom = typeof format === 'function';
14
+ const data = { label, value, previous, positive, comparison, series, target, format: custom ? undefined : format };
15
+ const key = JSON.stringify(data);
16
+ const options = () => ({ ...data, format: custom ? (n: number) => (formatter.current as (n: number) => string)(n) : data.format });
17
+ useIsomorphicLayoutEffect(() => {
18
+ const created = createStatCard(ref.current, { ...options(), locale, countOnView });
19
+ instance.current = created;
20
+ return () => {
21
+ created.destroy();
22
+ instance.current = null;
23
+ };
24
+ // eslint-disable-next-line react-hooks/exhaustive-deps
25
+ }, [locale, countOnView]);
26
+ const first = useRef(true);
27
+ useIsomorphicLayoutEffect(() => {
28
+ if (first.current) {
29
+ first.current = false;
30
+ return;
31
+ }
32
+ instance.current?.update(options());
33
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34
+ }, [key, custom]);
35
+ return <div {...props} ref={ref} className={['mk-stat-card', className].filter(Boolean).join(' ')} />;
36
+ }
@@ -0,0 +1,163 @@
1
+ /* stat-card — a 20 px card on surface-1: a muted label, a 2rem tabular figure, a pill badge with the change (green
2
+ when it moves the good way, red when not, neutral when flat), the comparison line, a 2 px sparkline over a soft
3
+ area with an end dot, and an optional goal bar. --mk-stat-good / --mk-stat-bad recolour the badge. */
4
+ .mk-stat-card,
5
+ mk-stat-card {
6
+ --mk-stat-good: #1f9d5b;
7
+ --mk-stat-bad: var(--mk-error, #c62828);
8
+ --mk-stat-line: var(--mk-accent);
9
+ display: grid;
10
+ gap: 0.5rem;
11
+ box-sizing: border-box;
12
+ min-width: 0;
13
+ padding: 1.1rem 1.2rem 1.2rem;
14
+ border: 1px solid var(--mk-outline-soft);
15
+ border-radius: 20px;
16
+ background: var(--mk-surface-1);
17
+ color: var(--mk-text);
18
+ }
19
+
20
+ [data-mk-stat-label] {
21
+ overflow: hidden;
22
+ color: var(--mk-muted);
23
+ font-size: 0.875rem;
24
+ font-weight: 500;
25
+ text-overflow: ellipsis;
26
+ white-space: nowrap;
27
+ }
28
+
29
+ [data-mk-stat-row] {
30
+ display: flex;
31
+ flex-wrap: wrap;
32
+ align-items: center;
33
+ gap: 0.35rem 0.6rem;
34
+ min-width: 0;
35
+ }
36
+
37
+ [data-mk-stat-value] {
38
+ min-width: 0;
39
+ overflow-wrap: anywhere;
40
+ font-size: clamp(1.5rem, 4vw, 2rem);
41
+ font-variant-numeric: tabular-nums;
42
+ font-weight: 600;
43
+ letter-spacing: -0.02em;
44
+ line-height: 1.1;
45
+ }
46
+
47
+ [data-mk-stat-delta] {
48
+ display: inline-flex;
49
+ align-items: center;
50
+ gap: 0.2rem;
51
+ height: 1.5rem;
52
+ padding: 0 0.5rem 0 0.4rem;
53
+ border-radius: 999px;
54
+ background: color-mix(in srgb, var(--mk-muted) 14%, transparent);
55
+ color: var(--mk-muted);
56
+ font-size: 0.8125rem;
57
+ font-variant-numeric: tabular-nums;
58
+ font-weight: 600;
59
+ white-space: nowrap;
60
+ transition: background-color 0.3s ease, color 0.3s ease;
61
+ }
62
+
63
+ :is(.mk-stat-card, mk-stat-card)[data-mk-good='good'] [data-mk-stat-delta] {
64
+ background: color-mix(in srgb, var(--mk-stat-good) 16%, transparent);
65
+ color: var(--mk-stat-good);
66
+ }
67
+
68
+ :is(.mk-stat-card, mk-stat-card)[data-mk-good='bad'] [data-mk-stat-delta] {
69
+ background: color-mix(in srgb, var(--mk-stat-bad) 14%, transparent);
70
+ color: var(--mk-stat-bad);
71
+ }
72
+
73
+ [data-mk-stat-arrow] {
74
+ display: inline-grid;
75
+ transition: rotate 0.35s cubic-bezier(0.38, 0.49, 0, 1.5);
76
+ }
77
+
78
+ :is(.mk-stat-card, mk-stat-card)[data-mk-trend='down'] [data-mk-stat-arrow] {
79
+ rotate: 180deg;
80
+ }
81
+
82
+ :is(.mk-stat-card, mk-stat-card)[data-mk-trend='flat'] [data-mk-stat-arrow] {
83
+ rotate: 90deg;
84
+ }
85
+
86
+ [data-mk-stat-comparison] {
87
+ margin: 0;
88
+ color: var(--mk-muted);
89
+ font-size: 0.8125rem;
90
+ }
91
+
92
+ [data-mk-stat-spark] {
93
+ position: relative;
94
+ height: 2.5rem;
95
+ margin-top: 0.25rem;
96
+ color: var(--mk-stat-line);
97
+ }
98
+
99
+ [data-mk-stat-spark] svg {
100
+ display: block;
101
+ width: 100%;
102
+ height: 100%;
103
+ overflow: visible;
104
+ }
105
+
106
+ [data-mk-stat-line] {
107
+ fill: none;
108
+ stroke: currentColor;
109
+ stroke-width: 2px;
110
+ stroke-linecap: round;
111
+ stroke-linejoin: round;
112
+ }
113
+
114
+ [data-mk-stat-area] {
115
+ fill: color-mix(in srgb, currentColor 14%, transparent);
116
+ }
117
+
118
+ [data-mk-stat-dot] {
119
+ position: absolute;
120
+ width: 8px;
121
+ height: 8px;
122
+ margin: -4px 0 0 -4px;
123
+ border-radius: 50%;
124
+ background: currentColor;
125
+ box-shadow: 0 0 0 2.5px var(--mk-surface-1);
126
+ }
127
+
128
+ [data-mk-stat-target] {
129
+ display: grid;
130
+ gap: 0.35rem;
131
+ }
132
+
133
+ [data-mk-stat-target-bar] {
134
+ display: block;
135
+ height: 6px;
136
+ overflow: hidden;
137
+ border-radius: 999px;
138
+ background: var(--mk-surface-3);
139
+ }
140
+
141
+ [data-mk-stat-target-fill] {
142
+ display: block;
143
+ height: 100%;
144
+ border-radius: inherit;
145
+ background: var(--mk-stat-line);
146
+ transform-origin: 0 50%;
147
+ }
148
+
149
+ [data-mk-stat-target-text] {
150
+ color: var(--mk-muted);
151
+ font-size: 0.75rem;
152
+ }
153
+
154
+ :is(.mk-stat-card, mk-stat-card) [hidden] {
155
+ display: none;
156
+ }
157
+
158
+ @media (prefers-reduced-motion: reduce) {
159
+ [data-mk-stat-delta],
160
+ [data-mk-stat-arrow] {
161
+ transition: none;
162
+ }
163
+ }
@@ -0,0 +1,44 @@
1
+ # studio-dock
2
+
3
+ Navegación de estudio que pasa de una píldora a un panel de tarjetas. Inspirada en el comportamiento público de Mubien; código y gráficos propios, sin sus fuentes, marcas o videos.
4
+
5
+ ```html
6
+ <div class="mk-studio-dock" data-mk="studio-dock">
7
+ <div class="mk-studio-dock__bar">
8
+ <a href="/" class="mk-studio-dock__brand">ST</a>
9
+ <span class="mk-studio-dock__label">Estudio</span>
10
+ <button type="button" class="mk-studio-dock__toggle" data-mk-studio-toggle aria-label="Abrir navegación"><span aria-hidden="true">+</span></button>
11
+ </div>
12
+ <section class="mk-studio-dock__panel" data-mk-studio-panel hidden>
13
+ <div data-mk-studio-view="menu">
14
+ <div class="mk-studio-dock__grid">
15
+ <a href="/work" class="mk-studio-dock__tile">Proyectos</a>
16
+ <a href="/about" class="mk-studio-dock__tile">Estudio</a>
17
+ </div>
18
+ <button type="button" data-mk-studio-slide>Contacto</button>
19
+ </div>
20
+ <div data-mk-studio-view="contact" hidden>
21
+ <h2>Conversemos</h2><a href="mailto:hello@example.com">hello@example.com</a>
22
+ <button type="button" data-mk-studio-to="menu">Volver</button>
23
+ </div>
24
+ </section>
25
+ </div>
26
+ ```
27
+
28
+ ```ts
29
+ import { createStudioDock } from '@yewdiel21/motion-kit';
30
+ import { useStudioDock, StudioDock } from '@yewdiel21/motion-kit/react';
31
+ import '@yewdiel21/motion-kit/css';
32
+ const dock = createStudioDock(root, { duration: 1.3, ease: 'back.out(0.52)' });
33
+ dock.open('contact'); dock.selectPage(1); dock.close(); dock.destroy();
34
+ ```
35
+
36
+ `StudioDock` es un shell React con los mismos slots y `instanceRef`; `useStudioDock(ref, options, deps)` permite usar tu markup. Web component: `defineStudioDock()` registra `<mk-studio-dock>`. El evento `mk:studio-dock` y `onChange` entregan `{ open, view, page }`.
37
+
38
+ Slots opcionales: `[data-mk-studio-to="menu|contact|reel"]`, `[data-mk-studio-close]`, `[data-mk-studio-pages]`, `[data-mk-studio-page]`, botones `[data-mk-studio-page-to="0"]` y `<time data-mk-studio-timezone="America/Mazatlan">`. Las páginas se ocultan con `hidden`; el paginado tiene flechas izquierda/derecha y botones numerados. Los relojes usan `Intl` y actualizan cada minuto; no consultan ningún servidor.
39
+
40
+ El botón de contacto funciona con clic, Enter y Espacio. Dentro de `.mk-studio-dock__slide-track` también acepta arrastre y abre al superar 70 % del recorrido. Las vistas reciben foco, Escape cierra, clic fuera cierra sin robar foco al destino y el cierre explícito devuelve el foco anterior. `trapFocus` y `focusOnOpen` son `true` por defecto y se pueden desactivar. El panel es un diálogo contenido, sin bloqueo del body ni cambios en otros componentes. `aria-expanded` refleja el estado y el panel cerrado queda fuera del árbol de accesibilidad.
41
+
42
+ El panel se revela/colapsa por `clip-path` y desplazamiento de 16 px; duración configurable de 1.3 s, rebote `.52`. Con movimiento reducido se salta el recorrido. `destroy()` es idempotente: revierte estilos/atributos, listeners, temporizadores y animaciones. Los videos aportados por el consumidor se pausan al salir de su vista. No se envía ningún formulario desde este motor.
43
+
44
+ Por defecto permanece dentro del layout. Usa `.mk-studio-dock--fixed` o `<StudioDock fixed>` para fijarlo abajo de la ventana. Al abrirse, el panel mide el espacio que queda en la ventana: su alto se limita a ese hueco (con scroll interno) y, si arriba no cabe y abajo hay más sitio, se abre bajo la barra (`data-mk-studio-below`). `--mk-studio-dock-offset` (76 px) es la distancia del panel al borde del dock. Variables de color: `--mk-surface`, `--mk-surface-2`, `--mk-accent`, `--mk-on-accent`, `--mk-outline-soft`.
@@ -0,0 +1,11 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createStudioDock, type StudioDockInstance, type StudioDockView } from './engine';
3
+ export class MkStudioDockElement extends MkElement<StudioDockInstance> {
4
+ static options: Record<string, OptionType> = { duration: 'number', ease: 'string', focusOnOpen: 'boolean', trapFocus: 'boolean' };
5
+ protected create(options: Record<string, unknown>) { return createStudioDock(this, options); }
6
+ open(view?: StudioDockView) { this.instance?.open(view); }
7
+ close() { this.instance?.close(); }
8
+ toggle() { this.instance?.toggle(); }
9
+ selectPage(index: number) { this.instance?.selectPage(index); }
10
+ }
11
+ export const defineStudioDock = () => defineElement('mk-studio-dock', MkStudioDockElement);