@justanarthur/modo-ui 0.6.0-canary.9 → 0.6.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 (490) hide show
  1. package/README.md +66 -164
  2. package/dist/base.css +4 -141
  3. package/dist/blocks/sidebar-app/index.js +26 -40
  4. package/dist/components/AI/input-message/index.js +16 -23
  5. package/dist/components/AI/thinking-indicator/index.js +6 -9
  6. package/dist/components/accordion/index.d.ts +11 -39
  7. package/dist/components/accordion/index.js +13 -18
  8. package/dist/components/badge/index.d.ts +11 -18
  9. package/dist/components/badge/index.js +7 -12
  10. package/dist/components/button/index.d.ts +17 -53
  11. package/dist/components/button/index.js +6 -11
  12. package/dist/components/card/index.d.ts +9 -32
  13. package/dist/components/card/index.js +11 -17
  14. package/dist/components/copy/index.d.ts +8 -41
  15. package/dist/components/copy/index.js +9 -15
  16. package/dist/components/inputs/checkbox/index.d.ts +9 -28
  17. package/dist/components/inputs/checkbox/index.js +13 -18
  18. package/dist/components/inputs/color-picker/index.d.ts +14 -22
  19. package/dist/components/inputs/color-picker/index.js +18 -26
  20. package/dist/components/inputs/combobox/index.d.ts +20 -85
  21. package/dist/components/inputs/combobox/index.js +18 -27
  22. package/dist/components/inputs/date-time/calendar/index.d.ts +18 -24
  23. package/dist/components/inputs/date-time/calendar/index.js +11 -15
  24. package/dist/components/inputs/date-time/date-picker/index.d.ts +16 -105
  25. package/dist/components/inputs/date-time/date-picker/index.js +25 -35
  26. package/dist/components/inputs/date-time/date-range-picker/index.d.ts +12 -106
  27. package/dist/components/inputs/date-time/date-range-picker/index.js +24 -35
  28. package/dist/components/inputs/date-time/duration-picker/index.d.ts +13 -66
  29. package/dist/components/inputs/date-time/duration-picker/index.js +24 -33
  30. package/dist/components/inputs/date-time/month-picker/index.d.ts +10 -60
  31. package/dist/components/inputs/date-time/month-picker/index.js +23 -32
  32. package/dist/components/inputs/date-time/time-picker/index.d.ts +10 -58
  33. package/dist/components/inputs/date-time/time-picker/index.js +24 -33
  34. package/dist/components/inputs/input/index.d.ts +13 -95
  35. package/dist/components/inputs/input/index.js +9 -18
  36. package/dist/components/inputs/radio/index.d.ts +5 -20
  37. package/dist/components/inputs/radio/index.js +12 -16
  38. package/dist/components/inputs/search/index.d.ts +3 -3
  39. package/dist/components/inputs/search/index.js +18 -26
  40. package/dist/components/inputs/select/index.d.ts +23 -99
  41. package/dist/components/inputs/select/index.js +14 -29
  42. package/dist/components/inputs/slider/index.d.ts +0 -9
  43. package/dist/components/inputs/slider/index.js +5 -8
  44. package/dist/components/inputs/switch/index.d.ts +2 -8
  45. package/dist/components/inputs/switch/index.js +12 -15
  46. package/dist/components/inputs/textarea/index.d.ts +1 -20
  47. package/dist/components/inputs/textarea/index.js +6 -9
  48. package/dist/components/navigation/sidebar/index.d.ts +21 -44
  49. package/dist/components/navigation/sidebar/index.js +31 -42
  50. package/dist/components/navigation/tabs/index.d.ts +2 -8
  51. package/dist/components/navigation/tabs/index.js +10 -15
  52. package/dist/components/navigation/tabs-subtle/index.js +10 -13
  53. package/dist/components/overlays/alert-dialog/index.d.ts +16 -135
  54. package/dist/components/overlays/alert-dialog/index.js +13 -23
  55. package/dist/components/overlays/command/index.d.ts +20 -25
  56. package/dist/components/overlays/command/index.js +17 -25
  57. package/dist/components/overlays/context-menu/index.d.ts +17 -39
  58. package/dist/components/overlays/context-menu/index.js +18 -29
  59. package/dist/components/overlays/dialog/index.d.ts +16 -42
  60. package/dist/components/overlays/dialog/index.js +12 -20
  61. package/dist/components/overlays/dropdown/index.d.ts +34 -304
  62. package/dist/components/overlays/dropdown/index.js +17 -44
  63. package/dist/components/overlays/popover/index.d.ts +12 -38
  64. package/dist/components/overlays/popover/index.js +9 -13
  65. package/dist/components/overlays/preview-card/index.js +8 -12
  66. package/dist/components/overlays/sheet/index.d.ts +8 -25
  67. package/dist/components/overlays/sheet/index.js +13 -19
  68. package/dist/components/overlays/toast/index.d.ts +50 -209
  69. package/dist/components/overlays/toast/index.js +10 -23
  70. package/dist/components/overlays/tooltip/index.d.ts +21 -123
  71. package/dist/components/overlays/tooltip/index.js +6 -16
  72. package/dist/components/table/index.d.ts +8 -63
  73. package/dist/components/table/index.js +9 -20
  74. package/dist/index.d.ts +1048 -2999
  75. package/dist/index.js +82 -231
  76. package/dist/preset.d.ts +0 -22
  77. package/dist/preset.js +2 -24
  78. package/dist/primitives/code/index.js +5 -8
  79. package/dist/primitives/craft/index.js +6 -10
  80. package/dist/primitives/fluid-hover/index.js +8 -10
  81. package/dist/primitives/morph/index.js +10 -16
  82. package/dist/primitives/motion/index.js +8 -11
  83. package/dist/primitives/scroll-area/index.js +3 -5
  84. package/dist/primitives/sizes/index.js +4 -6
  85. package/dist/primitives/surface/index.js +4 -6
  86. package/dist/shared/{components-c7rwx0ap.js → components-0q0sqfh4.js} +49 -27
  87. package/dist/shared/components-0sn7ypjw.js +61 -0
  88. package/dist/shared/{components-vc864c51.js → components-11da9ajg.js} +5 -8
  89. package/dist/shared/components-1434p1mh.js +123 -0
  90. package/dist/shared/{components-c74gf16d.js → components-1z6tdadk.js} +24 -40
  91. package/dist/shared/{components-bae8sbjx.js → components-27nnvrwe.js} +14 -15
  92. package/dist/shared/{components-qh1cvj6p.js → components-2dxkv6zm.js} +7 -8
  93. package/dist/shared/{components-y0kfy2g4.js → components-2ecg8svx.js} +152 -234
  94. package/dist/shared/{components-c5w5zk0s.js → components-2ph6t61s.js} +34 -37
  95. package/dist/shared/{components-4j3t39dd.js → components-39jq9va1.js} +24 -29
  96. package/dist/shared/components-3ky44908.js +9 -0
  97. package/dist/shared/{components-syynwm1r.js → components-3mkepka5.js} +25 -28
  98. package/dist/shared/{components-wgtjt2af.js → components-4700p9kz.js} +22 -24
  99. package/dist/shared/{components-yhgq0qxx.js → components-59efxjd9.js} +12 -13
  100. package/dist/shared/{components-91zw3d9f.js → components-5vgpnxgy.js} +27 -51
  101. package/dist/shared/{components-kz5q11pp.js → components-5xc2j8jz.js} +18 -19
  102. package/dist/shared/{components-zkk3fmw8.js → components-6p8tfcd6.js} +29 -57
  103. package/dist/shared/{components-4nck8acq.js → components-7r3etddy.js} +54 -19
  104. package/dist/shared/{components-svkt577j.js → components-8snj490h.js} +32 -78
  105. package/dist/shared/{components-ctqmge8t.js → components-b0eac83j.js} +30 -9
  106. package/dist/shared/{components-9sttq78t.js → components-b2wyq2cq.js} +59 -41
  107. package/dist/shared/{components-pv1dyrtn.js → components-b3c5et5b.js} +38 -49
  108. package/dist/shared/{components-rrbpmfx4.js → components-c6s6b43g.js} +2 -3
  109. package/dist/shared/{components-pwayrkw9.js → components-chacnk61.js} +19 -19
  110. package/dist/shared/{components-71nbt03m.js → components-cv16mryv.js} +7 -26
  111. package/dist/shared/{components-hgv6tygd.js → components-d6xtjht3.js} +30 -35
  112. package/dist/shared/{components-z8fedth5.js → components-dk0gjr5a.js} +18 -19
  113. package/dist/shared/{components-cet3jpa0.js → components-fzrq6tr3.js} +34 -37
  114. package/dist/shared/{components-cnead5qm.js → components-gc5vtq2f.js} +30 -66
  115. package/dist/shared/components-gf5s6ap9.js +42 -0
  116. package/dist/shared/{components-0h78g3b5.js → components-gz8zf3ec.js} +55 -56
  117. package/dist/shared/{components-mattt5jp.js → components-h2ajmhwn.js} +35 -107
  118. package/dist/shared/{components-m06yq8wk.js → components-hqeq3w9q.js} +23 -38
  119. package/dist/shared/{components-7qx17na9.js → components-hw9yk32z.js} +24 -36
  120. package/dist/shared/components-hyq23phv.js +310 -0
  121. package/dist/shared/{components-9jmjnkqd.js → components-jbbmdr74.js} +22 -41
  122. package/dist/shared/{components-dcc02hs2.js → components-jjw69c4c.js} +4 -7
  123. package/dist/shared/{components-303jf96s.js → components-jthe1pwb.js} +420 -139
  124. package/dist/shared/{components-dtca4s6b.js → components-jwst3vtj.js} +76 -224
  125. package/dist/shared/{components-dc5q749x.js → components-kvr7f9hy.js} +10 -19
  126. package/dist/shared/{components-s8yqk2dc.js → components-kwa6mrf9.js} +11 -46
  127. package/dist/shared/{components-de99ykt7.js → components-men63rwj.js} +2 -3
  128. package/dist/shared/{components-qx27excf.js → components-mjthm1kf.js} +9 -13
  129. package/dist/shared/{components-1e64t8aj.js → components-n42c79bz.js} +22 -23
  130. package/dist/shared/{components-tz1fv35x.js → components-n7mjr357.js} +2 -3
  131. package/dist/shared/components-ng72k97e.js +128 -0
  132. package/dist/shared/{components-w2jqa6hx.js → components-pbmqctfa.js} +65 -169
  133. package/dist/shared/{components-px2zm7dc.js → components-q5xj5c16.js} +8 -9
  134. package/dist/shared/components-raknhppb.js +64 -0
  135. package/dist/shared/{components-2a7hcand.js → components-rr7a6ja9.js} +27 -59
  136. package/dist/shared/{components-005jt2nz.js → components-s083f38z.js} +34 -164
  137. package/dist/shared/components-s5ngg32c.js +267 -0
  138. package/dist/shared/components-sn1rxx1d.js +143 -0
  139. package/dist/shared/{components-r8eqs7gz.js → components-ta7shw28.js} +4 -4
  140. package/dist/shared/{components-9as3rf68.js → components-vc7vq5nz.js} +3 -6
  141. package/dist/shared/{components-ft3zdc79.js → components-vfbhxf5r.js} +5 -6
  142. package/dist/shared/components-vynjtrk6.js +19 -0
  143. package/dist/shared/{components-p0fm0bab.js → components-w1r67ag9.js} +149 -294
  144. package/dist/shared/{components-xd3emmy4.js → components-w90endve.js} +72 -71
  145. package/dist/shared/{components-t5pk5nmc.js → components-wvp1m52g.js} +102 -149
  146. package/dist/shared/components-wy9jpqny.js +146 -0
  147. package/dist/shared/{components-yxmxn2jp.js → components-y9y0g8er.js} +28 -52
  148. package/dist/shared/components-ypp01w3p.js +986 -0
  149. package/dist/shared/{components-czdvj1a8.js → components-zfnmn5d4.js} +36 -82
  150. package/dist/shared/{components-jqpefjsn.js → components-zrkzcayz.js} +6 -54
  151. package/dist/shared/{components-vf45j12c.js → components-zx6b6yby.js} +18 -4
  152. package/dist/styles.css +32 -320
  153. package/dist/tailwind.css +0 -16
  154. package/dist/tokens.css +0 -67
  155. package/package.json +4 -227
  156. package/DESIGN.md +0 -623
  157. package/blocks/sidebar-app/examples.mdx +0 -33
  158. package/blocks/sidebar-app/index.tsx +0 -218
  159. package/blocks/sidebar-app/inset-topbar.tsx +0 -25
  160. package/blocks/sidebar-app/menu-grid.ts +0 -26
  161. package/blocks/sidebar-app/nav-data.ts +0 -36
  162. package/blocks/sidebar-app/search-field.tsx +0 -48
  163. package/blocks/sidebar-app/user-footer.tsx +0 -67
  164. package/blocks/sidebar-app/workspace-header.tsx +0 -129
  165. package/components/AI/input-message/examples.mdx +0 -130
  166. package/components/AI/input-message/file-thumbnail.tsx +0 -173
  167. package/components/AI/input-message/index.tsx +0 -1385
  168. package/components/AI/thinking-indicator/examples.mdx +0 -27
  169. package/components/AI/thinking-indicator/index.tsx +0 -210
  170. package/components/accordion/examples.mdx +0 -233
  171. package/components/accordion/index.tsx +0 -1071
  172. package/components/avatar/examples.mdx +0 -95
  173. package/components/avatar/index.tsx +0 -129
  174. package/components/badge/change-demo.tsx +0 -29
  175. package/components/badge/examples.mdx +0 -124
  176. package/components/badge/index.tsx +0 -225
  177. package/components/badge/variants.ts +0 -73
  178. package/components/button/examples.mdx +0 -168
  179. package/components/button/index.tsx +0 -424
  180. package/components/button/loading-demo.tsx +0 -25
  181. package/components/button/variants.ts +0 -168
  182. package/components/card/examples.mdx +0 -251
  183. package/components/card/index.tsx +0 -1070
  184. package/components/copy/examples.mdx +0 -125
  185. package/components/copy/index.tsx +0 -662
  186. package/components/inputs/checkbox/examples.mdx +0 -87
  187. package/components/inputs/checkbox/index.tsx +0 -611
  188. package/components/inputs/checkbox/select-all-demo.tsx +0 -48
  189. package/components/inputs/color-picker/examples.mdx +0 -111
  190. package/components/inputs/color-picker/index.tsx +0 -1981
  191. package/components/inputs/color-picker/variants.ts +0 -401
  192. package/components/inputs/combobox/examples.mdx +0 -366
  193. package/components/inputs/combobox/index.tsx +0 -1527
  194. package/components/inputs/combobox/remote-demo.tsx +0 -105
  195. package/components/inputs/date-time/calendar/examples.mdx +0 -45
  196. package/components/inputs/date-time/calendar/index.tsx +0 -370
  197. package/components/inputs/date-time/calendar/use-month-window.ts +0 -173
  198. package/components/inputs/date-time/date-picker/examples.mdx +0 -243
  199. package/components/inputs/date-time/date-picker/index.tsx +0 -476
  200. package/components/inputs/date-time/date-picker/masked-date-input.tsx +0 -348
  201. package/components/inputs/date-time/date-range-picker/examples.mdx +0 -93
  202. package/components/inputs/date-time/date-range-picker/index.tsx +0 -525
  203. package/components/inputs/date-time/duration-picker/examples.mdx +0 -40
  204. package/components/inputs/date-time/duration-picker/index.tsx +0 -253
  205. package/components/inputs/date-time/month-picker/examples.mdx +0 -45
  206. package/components/inputs/date-time/month-picker/index.tsx +0 -254
  207. package/components/inputs/date-time/picker-shell.tsx +0 -581
  208. package/components/inputs/date-time/time-columns.tsx +0 -94
  209. package/components/inputs/date-time/time-picker/examples.mdx +0 -57
  210. package/components/inputs/date-time/time-picker/index.tsx +0 -227
  211. package/components/inputs/field/examples.mdx +0 -117
  212. package/components/inputs/field/index.tsx +0 -268
  213. package/components/inputs/file-dropzone/examples.mdx +0 -83
  214. package/components/inputs/file-dropzone/file-row.tsx +0 -201
  215. package/components/inputs/file-dropzone/index.tsx +0 -316
  216. package/components/inputs/file-dropzone/upload-demo.tsx +0 -67
  217. package/components/inputs/file-dropzone/variants.ts +0 -94
  218. package/components/inputs/input/examples.mdx +0 -166
  219. package/components/inputs/input/index.tsx +0 -494
  220. package/components/inputs/input/password-demo.tsx +0 -30
  221. package/components/inputs/label/examples.mdx +0 -67
  222. package/components/inputs/label/index.tsx +0 -85
  223. package/components/inputs/number-input/examples.mdx +0 -69
  224. package/components/inputs/number-input/index.tsx +0 -221
  225. package/components/inputs/radio/examples.mdx +0 -77
  226. package/components/inputs/radio/index.tsx +0 -636
  227. package/components/inputs/search/examples.mdx +0 -74
  228. package/components/inputs/search/index.tsx +0 -333
  229. package/components/inputs/select/examples.mdx +0 -296
  230. package/components/inputs/select/index.tsx +0 -1044
  231. package/components/inputs/select/variants.ts +0 -32
  232. package/components/inputs/slider/examples.mdx +0 -153
  233. package/components/inputs/slider/index.tsx +0 -1867
  234. package/components/inputs/switch/examples.mdx +0 -96
  235. package/components/inputs/switch/index.tsx +0 -504
  236. package/components/inputs/textarea/examples.mdx +0 -126
  237. package/components/inputs/textarea/index.tsx +0 -207
  238. package/components/navigation/navigation-menu/examples.mdx +0 -136
  239. package/components/navigation/navigation-menu/index.tsx +0 -704
  240. package/components/navigation/pagination/examples.mdx +0 -74
  241. package/components/navigation/pagination/index.tsx +0 -401
  242. package/components/navigation/sidebar/examples.mdx +0 -325
  243. package/components/navigation/sidebar/index.tsx +0 -453
  244. package/components/navigation/sidebar/sidebar-core.tsx +0 -1408
  245. package/components/navigation/sidebar/sidebar-menu.tsx +0 -1495
  246. package/components/navigation/sidebar/sidebar.css +0 -74
  247. package/components/navigation/sidebar/variants.ts +0 -55
  248. package/components/navigation/tabs/examples.mdx +0 -89
  249. package/components/navigation/tabs/index.tsx +0 -494
  250. package/components/navigation/tabs-subtle/examples.mdx +0 -49
  251. package/components/navigation/tabs-subtle/index.tsx +0 -512
  252. package/components/overlays/alert-dialog/examples.mdx +0 -123
  253. package/components/overlays/alert-dialog/index.tsx +0 -276
  254. package/components/overlays/alert-dialog/pending-demo.tsx +0 -36
  255. package/components/overlays/command/examples.mdx +0 -113
  256. package/components/overlays/command/index.tsx +0 -425
  257. package/components/overlays/context-menu/examples.mdx +0 -56
  258. package/components/overlays/context-menu/index.tsx +0 -279
  259. package/components/overlays/dialog/examples.mdx +0 -165
  260. package/components/overlays/dialog/index.tsx +0 -451
  261. package/components/overlays/dropdown/dropdown-search.tsx +0 -395
  262. package/components/overlays/dropdown/examples.mdx +0 -444
  263. package/components/overlays/dropdown/index.tsx +0 -1241
  264. package/components/overlays/dropdown/menu-item.tsx +0 -510
  265. package/components/overlays/dropdown/menu-parts.tsx +0 -725
  266. package/components/overlays/popover/anchored-demo.tsx +0 -59
  267. package/components/overlays/popover/examples.mdx +0 -203
  268. package/components/overlays/popover/index.tsx +0 -317
  269. package/components/overlays/preview-card/examples.mdx +0 -61
  270. package/components/overlays/preview-card/index.tsx +0 -204
  271. package/components/overlays/sheet/examples.mdx +0 -101
  272. package/components/overlays/sheet/index.tsx +0 -307
  273. package/components/overlays/toast/examples.mdx +0 -85
  274. package/components/overlays/toast/global-demo.tsx +0 -72
  275. package/components/overlays/toast/index.tsx +0 -544
  276. package/components/overlays/toast/manager.ts +0 -193
  277. package/components/overlays/tooltip/examples.mdx +0 -106
  278. package/components/overlays/tooltip/index.tsx +0 -539
  279. package/components/progress/examples.mdx +0 -59
  280. package/components/progress/index.tsx +0 -174
  281. package/components/progress/progress.css +0 -49
  282. package/components/progress/step-demo.tsx +0 -27
  283. package/components/table/examples.mdx +0 -188
  284. package/components/table/index.tsx +0 -535
  285. package/components/table/sort-demo.tsx +0 -61
  286. package/dist/components/avatar/index.d.ts +0 -46
  287. package/dist/components/avatar/index.js +0 -19
  288. package/dist/components/badge/variants.d.ts +0 -33
  289. package/dist/components/badge/variants.js +0 -10
  290. package/dist/components/button/variants.d.ts +0 -39
  291. package/dist/components/button/variants.js +0 -8
  292. package/dist/components/inputs/color-picker/variants.d.ts +0 -61
  293. package/dist/components/inputs/color-picker/variants.js +0 -28
  294. package/dist/components/inputs/field/index.d.ts +0 -116
  295. package/dist/components/inputs/field/index.js +0 -29
  296. package/dist/components/inputs/file-dropzone/index.d.ts +0 -167
  297. package/dist/components/inputs/file-dropzone/index.js +0 -33
  298. package/dist/components/inputs/file-dropzone/variants.d.ts +0 -35
  299. package/dist/components/inputs/file-dropzone/variants.js +0 -12
  300. package/dist/components/inputs/label/index.d.ts +0 -32
  301. package/dist/components/inputs/label/index.js +0 -11
  302. package/dist/components/inputs/number-input/index.d.ts +0 -87
  303. package/dist/components/inputs/number-input/index.js +0 -21
  304. package/dist/components/inputs/select/variants.d.ts +0 -5
  305. package/dist/components/inputs/select/variants.js +0 -6
  306. package/dist/components/navigation/navigation-menu/index.d.ts +0 -126
  307. package/dist/components/navigation/navigation-menu/index.js +0 -41
  308. package/dist/components/navigation/pagination/index.d.ts +0 -75
  309. package/dist/components/navigation/pagination/index.js +0 -41
  310. package/dist/components/navigation/sidebar/variants.d.ts +0 -25
  311. package/dist/components/navigation/sidebar/variants.js +0 -28
  312. package/dist/components/progress/index.d.ts +0 -56
  313. package/dist/components/progress/index.js +0 -15
  314. package/dist/docs/index.d.ts +0 -83
  315. package/dist/docs/index.js +0 -288
  316. package/dist/docs/vite.d.ts +0 -31
  317. package/dist/docs/vite.js +0 -45
  318. package/dist/lib/date-locale.d.ts +0 -26
  319. package/dist/lib/date-locale.js +0 -24
  320. package/dist/lib/field-classes.d.ts +0 -149
  321. package/dist/lib/field-classes.js +0 -41
  322. package/dist/lib/field-variants.d.ts +0 -47
  323. package/dist/lib/field-variants.js +0 -26
  324. package/dist/lib/fluid-hover-highlight.d.ts +0 -112
  325. package/dist/lib/fluid-hover-highlight.js +0 -17
  326. package/dist/lib/focus-ring.d.ts +0 -18
  327. package/dist/lib/focus-ring.js +0 -11
  328. package/dist/lib/goo-indicator.d.ts +0 -46
  329. package/dist/lib/goo-indicator.js +0 -14
  330. package/dist/lib/icon-context.d.ts +0 -30
  331. package/dist/lib/icon-context.js +0 -12
  332. package/dist/lib/inert.d.ts +0 -9
  333. package/dist/lib/inert.js +0 -8
  334. package/dist/lib/morph-layers.d.ts +0 -93
  335. package/dist/lib/morph-layers.js +0 -13
  336. package/dist/lib/morph-part.d.ts +0 -21
  337. package/dist/lib/morph-part.js +0 -12
  338. package/dist/lib/popup.d.ts +0 -20
  339. package/dist/lib/popup.js +0 -12
  340. package/dist/lib/reduced-motion.d.ts +0 -9
  341. package/dist/lib/reduced-motion.js +0 -6
  342. package/dist/lib/row-index.d.ts +0 -10
  343. package/dist/lib/row-index.js +0 -8
  344. package/dist/lib/row-order.d.ts +0 -28
  345. package/dist/lib/row-order.js +0 -10
  346. package/dist/lib/scroll-column.d.ts +0 -31
  347. package/dist/lib/scroll-column.js +0 -19
  348. package/dist/lib/shape-context.d.ts +0 -30
  349. package/dist/lib/shape-context.js +0 -13
  350. package/dist/lib/shape-radius.d.ts +0 -19
  351. package/dist/lib/shape-radius.js +0 -6
  352. package/dist/lib/size-context.d.ts +0 -94
  353. package/dist/lib/size-context.js +0 -20
  354. package/dist/lib/size-scale.d.ts +0 -79
  355. package/dist/lib/size-scale.js +0 -8
  356. package/dist/lib/slot.d.ts +0 -39
  357. package/dist/lib/slot.js +0 -14
  358. package/dist/lib/springs.d.ts +0 -44
  359. package/dist/lib/springs.js +0 -8
  360. package/dist/lib/surface-classes.d.ts +0 -19
  361. package/dist/lib/surface-classes.js +0 -10
  362. package/dist/lib/surface-context.d.ts +0 -9
  363. package/dist/lib/surface-context.js +0 -8
  364. package/dist/lib/use-controllable-state.d.ts +0 -2
  365. package/dist/lib/use-controllable-state.js +0 -6
  366. package/dist/lib/use-fluid-hover.d.ts +0 -138
  367. package/dist/lib/use-fluid-hover.js +0 -14
  368. package/dist/lib/use-is-mobile.d.ts +0 -2
  369. package/dist/lib/use-is-mobile.js +0 -6
  370. package/dist/lib/use-keyboard-nav-gate.d.ts +0 -20
  371. package/dist/lib/use-keyboard-nav-gate.js +0 -7
  372. package/dist/lib/use-merge-split.d.ts +0 -47
  373. package/dist/lib/use-merge-split.js +0 -15
  374. package/dist/lib/use-morph.d.ts +0 -101
  375. package/dist/lib/use-morph.js +0 -20
  376. package/dist/lib/use-touch-primary.d.ts +0 -2
  377. package/dist/lib/use-touch-primary.js +0 -6
  378. package/dist/lib/utils.d.ts +0 -3
  379. package/dist/lib/utils.js +0 -6
  380. package/dist/primitives/separator/index.d.ts +0 -25
  381. package/dist/primitives/separator/index.js +0 -9
  382. package/dist/primitives/skeleton/index.d.ts +0 -35
  383. package/dist/primitives/skeleton/index.js +0 -13
  384. package/dist/shared/components-01w9qcfa.js +0 -277
  385. package/dist/shared/components-124epb7f.js +0 -53
  386. package/dist/shared/components-26snnbs9.js +0 -322
  387. package/dist/shared/components-2krsc300.js +0 -254
  388. package/dist/shared/components-2qbqqpgq.js +0 -1545
  389. package/dist/shared/components-34txk9rj.js +0 -35
  390. package/dist/shared/components-3h92d94m.js +0 -23
  391. package/dist/shared/components-4vfm04wq.js +0 -82
  392. package/dist/shared/components-54nq988h.js +0 -123
  393. package/dist/shared/components-7s7pykk5.js +0 -281
  394. package/dist/shared/components-868v7scv.js +0 -399
  395. package/dist/shared/components-8jrk5eye.js +0 -448
  396. package/dist/shared/components-adkarj8n.js +0 -35
  397. package/dist/shared/components-b1e239g3.js +0 -28
  398. package/dist/shared/components-b94t6qtj.js +0 -143
  399. package/dist/shared/components-bsqynhaj.js +0 -179
  400. package/dist/shared/components-c9yr4ed1.js +0 -44
  401. package/dist/shared/components-d3shbwqq.js +0 -95
  402. package/dist/shared/components-dhgk6mbt.js +0 -16
  403. package/dist/shared/components-drxrtnp2.js +0 -25
  404. package/dist/shared/components-f7h57m6t.js +0 -59
  405. package/dist/shared/components-g3eafs70.js +0 -337
  406. package/dist/shared/components-gx4h5rbb.js +0 -40
  407. package/dist/shared/components-gyt7my41.js +0 -328
  408. package/dist/shared/components-h6p86be3.js +0 -19
  409. package/dist/shared/components-h83az0bz.js +0 -33
  410. package/dist/shared/components-hdzrqqpx.js +0 -55
  411. package/dist/shared/components-krtx3m5b.js +0 -84
  412. package/dist/shared/components-kswxnm3h.js +0 -24
  413. package/dist/shared/components-mhza2wdn.js +0 -482
  414. package/dist/shared/components-mrbaxt8q.js +0 -62
  415. package/dist/shared/components-pt5ftm4p.js +0 -38
  416. package/dist/shared/components-rtadhd9j.js +0 -64
  417. package/dist/shared/components-tv7b8tet.js +0 -271
  418. package/dist/shared/components-tz4gaf1v.js +0 -23
  419. package/dist/shared/components-wbbckbw8.js +0 -34
  420. package/dist/shared/components-wknae3m4.js +0 -298
  421. package/dist/shared/components-xdqmt6ed.js +0 -20
  422. package/dist/shared/components-xjkdvnbg.js +0 -107
  423. package/dist/shared/components-z3v0n0de.js +0 -11
  424. package/dist/tokens/colors.css +0 -230
  425. package/dist/tokens/motion.css +0 -40
  426. package/dist/tokens/radius.css +0 -10
  427. package/dist/tokens/spacing.css +0 -9
  428. package/dist/tokens/typography.css +0 -119
  429. package/docs/chrome.css +0 -120
  430. package/docs/chrome.tsx +0 -283
  431. package/docs/index.ts +0 -9
  432. package/docs/vite.ts +0 -85
  433. package/index.ts +0 -60
  434. package/lib/date-locale.ts +0 -195
  435. package/lib/field-classes.ts +0 -121
  436. package/lib/field-variants.ts +0 -117
  437. package/lib/fluid-hover-highlight.tsx +0 -139
  438. package/lib/focus-ring.tsx +0 -46
  439. package/lib/goo-indicator.tsx +0 -261
  440. package/lib/icon-context.tsx +0 -310
  441. package/lib/inert.ts +0 -22
  442. package/lib/morph-layers.tsx +0 -102
  443. package/lib/morph-part.tsx +0 -49
  444. package/lib/popup.ts +0 -52
  445. package/lib/reduced-motion.ts +0 -19
  446. package/lib/row-index.tsx +0 -29
  447. package/lib/row-order.tsx +0 -126
  448. package/lib/scroll-column.tsx +0 -273
  449. package/lib/shape-context.tsx +0 -138
  450. package/lib/shape-radius.ts +0 -67
  451. package/lib/size-context.tsx +0 -66
  452. package/lib/size-scale.ts +0 -115
  453. package/lib/slot.ts +0 -115
  454. package/lib/springs.ts +0 -62
  455. package/lib/surface-classes.ts +0 -58
  456. package/lib/surface-context.tsx +0 -21
  457. package/lib/use-controllable-state.ts +0 -44
  458. package/lib/use-fluid-hover.ts +0 -541
  459. package/lib/use-is-mobile.ts +0 -25
  460. package/lib/use-keyboard-nav-gate.ts +0 -45
  461. package/lib/use-merge-split.tsx +0 -209
  462. package/lib/use-morph.ts +0 -575
  463. package/lib/use-touch-primary.tsx +0 -43
  464. package/lib/utils.ts +0 -37
  465. package/preset.ts +0 -199
  466. package/primitives/code/examples.mdx +0 -27
  467. package/primitives/code/index.tsx +0 -80
  468. package/primitives/craft/demos.tsx +0 -173
  469. package/primitives/craft/examples.mdx +0 -142
  470. package/primitives/craft/index.tsx +0 -76
  471. package/primitives/fluid-hover/examples.mdx +0 -84
  472. package/primitives/fluid-hover/index.tsx +0 -316
  473. package/primitives/morph/examples.mdx +0 -139
  474. package/primitives/morph/index.tsx +0 -192
  475. package/primitives/motion/examples.mdx +0 -100
  476. package/primitives/motion/index.tsx +0 -160
  477. package/primitives/scroll-area/examples.mdx +0 -58
  478. package/primitives/scroll-area/index.tsx +0 -204
  479. package/primitives/separator/examples.mdx +0 -30
  480. package/primitives/separator/index.tsx +0 -53
  481. package/primitives/sizes/examples.mdx +0 -167
  482. package/primitives/sizes/index.tsx +0 -89
  483. package/primitives/skeleton/examples.mdx +0 -51
  484. package/primitives/skeleton/index.tsx +0 -72
  485. package/primitives/skeleton/skeleton.css +0 -15
  486. package/primitives/surface/examples.mdx +0 -173
  487. package/primitives/surface/index.tsx +0 -109
  488. package/primitives/surface/surface.css +0 -176
  489. /package/dist/shared/{components-3ey0hzpb.js → components-1v7rxytx.js} +0 -0
  490. /package/dist/shared/{components-jcnjay5g.js → components-ezxhrmer.js} +0 -0
@@ -1,168 +0,0 @@
1
- import Button, { buttonVariants } from './index'
2
- import LoadingDemo from './loading-demo'
3
- import { ArrowRight, Check, ChevronDown, Loader, Plus, RotateCcw, Search, Settings, Trash2 } from 'lucide-react'
4
-
5
- # Variants
6
-
7
- 4 variants, inline or full width: add `className="w-full"` to stretch one
8
- to its container.
9
-
10
- <div className="flex w-fit max-w-full flex-col gap-8">
11
- <div className="flex items-center gap-2">
12
- <Button variant="primary">Primary</Button>
13
- <Button variant="secondary">Secondary</Button>
14
- <Button variant="tertiary">Tertiary</Button>
15
- <Button variant="ghost">Ghost</Button>
16
- </div>
17
- <div className="flex flex-col gap-2">
18
- <Button variant="primary" className="w-full">Primary</Button>
19
- <Button variant="secondary" className="w-full">Secondary</Button>
20
- <Button variant="tertiary" className="w-full">Tertiary</Button>
21
- <Button variant="ghost" className="w-full">Ghost</Button>
22
- </div>
23
- </div>
24
-
25
- ## Destructive and success
26
-
27
- A status as an action: its hue as the label on a wash of itself, deepening on
28
- hover and press. Never a solid fill, so the view keeps one primary.
29
-
30
- <div className="flex flex-wrap items-center gap-2">
31
- <Button variant="destructive" leadingIcon={Trash2}>Delete</Button>
32
- <Button variant="success" leadingIcon={Check}>Approve</Button>
33
- <Button variant="destructive" size="compact">Remove</Button>
34
- <Button variant="success" size="compact">Accept</Button>
35
- </div>
36
-
37
- ## Outline and link
38
-
39
- `outline` is `tertiary` under its shadcn name. `link` is text on the control
40
- height: its underline fades in on hover, so the label never moves, and it
41
- lines up with the buttons beside it.
42
-
43
- <div className="flex flex-wrap items-center gap-2">
44
- <Button variant="outline">Outline</Button>
45
- <Button variant="link">Forgot password?</Button>
46
- <Button variant="link" trailingIcon={ArrowRight}>View all</Button>
47
- </div>
48
-
49
- # Sizes
50
-
51
- The size ladder: `default` (36px) and `compact` (28px), plus square
52
- icon-only steps. There is no third height: `xs` and `sm` resolve to `compact`. Icon-only buttons take the icon as their child and need
53
- an `aria-label`.
54
-
55
- <div className="flex flex-col gap-3">
56
- <div className="flex flex-wrap items-center gap-2">
57
- <Button leadingIcon={Plus}>Default</Button>
58
- <Button variant="secondary" size="icon" aria-label="Add">
59
- <Plus />
60
- </Button>
61
- <Button variant="ghost" size="icon" aria-label="Settings">
62
- <Settings />
63
- </Button>
64
- </div>
65
- <div className="flex flex-wrap items-center gap-2">
66
- <Button size="compact" leadingIcon={Plus}>Compact</Button>
67
- <Button variant="secondary" size="icon-compact" aria-label="Add">
68
- <Plus />
69
- </Button>
70
- <Button variant="ghost" size="icon-compact" aria-label="Settings">
71
- <Settings />
72
- </Button>
73
- </div>
74
- </div>
75
-
76
- # States
77
-
78
- ## Loading & Disabled
79
-
80
- `loading` folds the surface into a circle around a spinner and disables
81
- the button; the hidden label keeps its width.
82
-
83
- <div className="flex flex-wrap items-center gap-2">
84
- <Button loading>Loading</Button>
85
- <Button variant="secondary" loading leadingIcon={Loader}>Saving</Button>
86
- <Button disabled>Disabled</Button>
87
- </div>
88
-
89
- ## Loading morph
90
-
91
- Press one. The surface folds into a circle around the spinner while the
92
- button keeps its box, so its neighbors never move, and springs back when
93
- loading ends. It keeps focus while it loads (`aria-disabled`, not
94
- `disabled`), so press one with Enter and focus is still there when it is
95
- done; presses in between are ignored. `LoadingDemo` is a docs stage: a
96
- Button whose `loading` holds for two seconds after a press.
97
-
98
- <div className="flex w-fit max-w-full flex-col gap-3">
99
- <div className="flex flex-wrap items-center gap-2">
100
- <LoadingDemo leadingIcon={Check}>Save</LoadingDemo>
101
- <LoadingDemo variant="secondary">Publish</LoadingDemo>
102
- <LoadingDemo variant="tertiary" trailingIcon={ArrowRight}>Next</LoadingDemo>
103
- <LoadingDemo variant="secondary" size="icon" aria-label="Refresh">
104
- <RotateCcw />
105
- </LoadingDemo>
106
- </div>
107
- <LoadingDemo className="w-full">Create account</LoadingDemo>
108
- </div>
109
-
110
- ## Active
111
-
112
- `active` forces the pressed visual — for a button whose dropdown or
113
- popover is currently open.
114
-
115
- <div className="flex flex-wrap items-center gap-2">
116
- <Button active>Primary</Button>
117
- <Button variant="secondary" active>Secondary</Button>
118
- <Button variant="tertiary" active trailingIcon={ChevronDown}>Tertiary</Button>
119
- <Button variant="ghost" active>Ghost</Button>
120
- </div>
121
-
122
- # Composition
123
-
124
- ## With Icons
125
-
126
- `leadingIcon` and `trailingIcon` take an icon component; it sits 4px
127
- closer to its edge than the label would.
128
-
129
- <div className="flex flex-wrap items-center gap-2">
130
- <Button leadingIcon={Plus}>Create</Button>
131
- <Button variant="secondary" trailingIcon={ArrowRight}>Next</Button>
132
- <Button variant="tertiary" leadingIcon={Search} trailingIcon={ArrowRight}>
133
- Search
134
- </Button>
135
- </div>
136
-
137
- ## As child
138
-
139
- `asChild` merges the button's styling onto its single child element —
140
- here a link — instead of rendering a `<button>`.
141
-
142
- <Button asChild variant="tertiary" trailingIcon={ArrowRight}>
143
- <a href="https://www.fluidfunctionalism.com" target="_blank" rel="noreferrer">
144
- Fluid Functionalism
145
- </a>
146
- </Button>
147
-
148
- ## Surface classes
149
-
150
- For an element you can't render through `asChild` (a router's link that
151
- builds its own anchor, markup from a server template), `buttonVariants`
152
- with `surface: true` draws the fill, ring, hover, press and focus ring on
153
- the element itself. Without `surface` it returns only the root classes, and
154
- the fill lives on the Button's inner layer. It is static: no press shrink,
155
- no loading morph, so prefer `<Button asChild>` where you can. Size aliases
156
- (`sm`, `lg`, `icon-sm`, …) resolve as on the Button; `aria-disabled` dims it.
157
-
158
- <div className="flex flex-wrap items-center gap-2">
159
- <a href="#surface-classes" className={buttonVariants({ surface: true })}>Primary</a>
160
- <a href="#surface-classes" className={buttonVariants({ variant: 'secondary', surface: true })}>Secondary</a>
161
- <a href="#surface-classes" className={buttonVariants({ variant: 'outline', surface: true })}>Outline</a>
162
- <a href="#surface-classes" className={buttonVariants({ variant: 'ghost', surface: true })}>Ghost</a>
163
- <a href="#surface-classes" className={buttonVariants({ variant: 'destructive', size: 'sm', surface: true })}>Remove</a>
164
- <a href="#surface-classes" className={buttonVariants({ variant: 'secondary', size: 'icon-sm', surface: true })} aria-label="Add">
165
- <Plus />
166
- </a>
167
- <a href="#surface-classes" aria-disabled="true" className={buttonVariants({ variant: 'secondary', surface: true })}>Disabled</a>
168
- </div>
@@ -1,424 +0,0 @@
1
- // biome-ignore-all lint: vendored upstream code keeps its own patterns (see the header)
2
- /*
3
- * Ported from Fluid Functionalism — github.com/mickadesign/fluid-functionalism
4
- * `registry/base/button.tsx` @ b3587bdbd83fc66c2a6aae3817ffb856cb09260b
5
- * MIT License © 2026 Micka Touillaud — fluidfunctionalism.com (notice: LICENSE.fluid-functionalism)
6
- * Local modifications:
7
- * - `"use client"` directive dropped (no RSC here).
8
- * - `@/lib/{icon-context,utils,shape-context,size-context}` imports rewritten to `../../lib/*`.
9
- * - `ButtonProps`: `variant`, `disabled` and `children` re-declared in the interface body (modo's
10
- * parser lists only members declared there); member docs replaced by the FF docs API-table text
11
- * (upstream's size-alias and `active` notes folded in).
12
- * - modo item: TSDoc (from the FF "Button" docs page, plus Sizes / Active / As child examples for
13
- * the API-table props the page has no section for) and a default export.
14
- * - Also the modo docs-chrome Button (`modo.config.ts` shell.Button): the chrome renders its
15
- * icon buttons as `<Button variant="ghost" size="icon-sm">`, served by upstream's legacy
16
- * `icon-sm` → `icon-compact` alias.
17
- * - Styling reads DS tokens (AGENTS.md styling): `text-[Npx]` →
18
- * `text-<role>[-compact]`; the hex focus-ring fallback →
19
- * `ring-focus-ring` / `border-focus-ring`; `duration-80|120|160` and
20
- * tier-length JS durations → `duration-<tier>` / `spring.*`.
21
- * - `loading` morphs (local, in the morph layer's language): the surface layer contracts into
22
- * a circle around the spinner on `spring.moderate` and springs back when loading ends, while
23
- * the button's own box keeps its size; the label and the spinner crossfade instead of
24
- * swapping. The spinner centres on the button, not on the label. `aria-busy` while loading.
25
- * The root's `transition-colors` → `transition-[color,opacity]`, so the disabled dim fades
26
- * in step instead of jumping (the root draws no background or border of its own).
27
- * A loading button stays focusable (Base UI's `focusableWhenDisabled`, so
28
- * `aria-disabled`, not `disabled`) and ignores presses; with `asChild` it
29
- * sets `aria-disabled` and swallows clicks itself.
30
- * - `buttonVariants` and the size aliases moved to `variants.ts` (server-callable, no
31
- * JSX or hooks) and re-exported from here; it resolves the size aliases itself and
32
- * takes `surface` (the fill as root classes, for an anchor host; see `variants.ts`).
33
- * - Variants added for hosts with a shadcn-style API (local, under DESIGN.md): `outline`
34
- * renders exactly as `tertiary` (both are the transparent 1px-ring button);
35
- * `destructive` and `success` are their hue as text (`--destructive-strong`,
36
- * `--success-strong`) on a translucent wash of it that deepens on hover and press,
37
- * never a solid fill; `link` is foreground text on the control height with no padding,
38
- * whose always-drawn underline fades in on hover (and shows while `active`). Size `xs`
39
- * resolves to `compact`: the ladder has two steps.
40
- * - The `primary` fill and label read the theme hooks `--primary`, `--primary-hover`,
41
- * `--primary-active`, `--primary-foreground` and `--shadow-button-primary`
42
- * (`tokens/colors.css`; `bg-primary` / `text-primary-foreground`) instead of the
43
- * ink and `text-background`, so a host can recolour it. Defaults are the old values.
44
- * The shadow hook is appended to the layer's own shadow (it sits on the fill layer:
45
- * a shadow on the root would be covered by it). `variants.ts` mirrors it on the root.
46
- */
47
-
48
- import { Button as ButtonPrimitive } from '@base-ui/react/button'
49
- import type { VariantProps } from 'class-variance-authority'
50
- import { AnimatePresence, animate, motion, useMotionValue } from 'motion/react'
51
- import {
52
- type ButtonHTMLAttributes,
53
- cloneElement,
54
- forwardRef,
55
- isValidElement,
56
- type ReactElement,
57
- type ReactNode,
58
- useLayoutEffect,
59
- useRef,
60
- } from 'react'
61
- import type { IconComponent } from '../../lib/icon-context'
62
- import { useReduceMotion } from '../../lib/reduced-motion'
63
- import { useShape } from '../../lib/shape-context'
64
- import { useSizeVariant } from '../../lib/size-context'
65
- import { spring } from '../../lib/springs'
66
- import { cn } from '../../lib/utils'
67
- import {
68
- type ButtonSize,
69
- type ButtonSizeCanonical,
70
- type ButtonVariant,
71
- buttonVariants,
72
- legacySizeAliases,
73
- } from './variants'
74
-
75
- interface ButtonProps
76
- extends ButtonHTMLAttributes<HTMLButtonElement>,
77
- Omit<VariantProps<typeof buttonVariants>, 'size' | 'surface'> {
78
- /** Visual style of the button. `outline` is `tertiary` under its shadcn name; `destructive` and `success` tint the label and a wash of their hue; `link` is underlined text on the control height. Defaults to `"primary"`. */
79
- variant?: 'primary' | 'secondary' | 'tertiary' | 'outline' | 'ghost' | 'destructive' | 'success' | 'link'
80
- /** Step on the size ladder (36px default, 28px compact — see Sizes): `"default"` | `"compact"` | `"icon"` | `"icon-compact"`. Omitted, the button follows the surrounding SizeProvider. Aliases resolve onto the ladder (xs, sm → compact; md/lg → default; icon-sm → icon-compact; icon-lg → icon). */
81
- size?: 'default' | 'compact' | 'icon' | 'icon-compact' | 'xs' | 'sm' | 'md' | 'lg' | 'icon-sm' | 'icon-lg'
82
- /** Merge props onto the child element instead of rendering a `<button>` — the single React-element child becomes the rendered element (slot-style). Defaults to `false`. */
83
- asChild?: boolean
84
- /** Folds the button into a circle around a spinner and blocks presses; the button keeps its box, so nothing around it moves, and keeps focus (`aria-disabled`, not `disabled`). Defaults to `false`. */
85
- loading?: boolean
86
- /** Icon displayed before the label. */
87
- leadingIcon?: IconComponent
88
- /** Icon displayed after the label. */
89
- trailingIcon?: IconComponent
90
- /** Forces the pressed/held visual — e.g. while a dropdown or popover the button opened is showing. Defaults to `false`. */
91
- active?: boolean
92
- /** Disables the button. Defaults to `false`. */
93
- disabled?: boolean
94
- /** The label. With `size="icon"` / `"icon-compact"`, the icon itself (give the button an `aria-label`). */
95
- children?: ReactNode
96
- }
97
-
98
- /* Press effect: the surface layer sits 1px inside the button and a
99
- same-color box-shadow spread fills it back out to the full bounds.
100
- Pressing collapses the spread, shrinking the surface by exactly 1px per
101
- side at any width — a scale would warp (2% of a 400px button is 8px
102
- sideways but under 1px vertically). Fill colors are opaque color-mix()es
103
- rather than alpha so the fill and its spread ring never seam. The primary
104
- fill reads the `--primary*` hooks; `--shadow-button-primary` joins the
105
- layer's shadow list after the ring, at rest and pressed. */
106
- const bgVariants: Record<string, string> = {
107
- primary:
108
- '[--btn-bg:var(--primary)] group-hover:[--btn-bg:var(--primary-hover)] group-active:[--btn-bg:var(--primary-active)] bg-[var(--btn-bg)] shadow-[0_0_0_1px_var(--btn-bg),var(--shadow-button-primary)] group-active:shadow-[0_0_0_0px_var(--btn-bg),var(--shadow-button-primary)]',
109
- secondary:
110
- '[--btn-bg:var(--accent)] group-hover:[--btn-bg:color-mix(in_oklab,var(--accent)_80%,var(--background))] group-active:[--btn-bg:var(--accent)] bg-[var(--btn-bg)] shadow-[0_0_0_1px_var(--btn-bg)] group-active:shadow-[0_0_0_0px_var(--btn-bg)]',
111
- // The border ring is an outer 1px shadow at rest that hands off to an
112
- // inset 1px shadow when pressed, so the ring moves inward with the
113
- // surface. The translucent fill only ever reaches the ring's inner edge
114
- // (exactly the surface box), so it needs no spread of its own.
115
- tertiary:
116
- 'bg-transparent shadow-[0_0_0_1px_var(--border),inset_0_0_0_0px_var(--border)] group-hover:bg-hover group-active:bg-active group-active:shadow-[0_0_0_0px_var(--border),inset_0_0_0_1px_var(--border)]',
117
- // Translucent fill + same-color spread never double up: outer shadows
118
- // render only outside the surface box.
119
- ghost:
120
- 'bg-transparent shadow-[0_0_0_1px_transparent] group-hover:bg-hover group-hover:shadow-[0_0_0_1px_var(--hover)] group-active:bg-active group-active:shadow-[0_0_0_0px_var(--active)]',
121
- // A status as an action: a translucent wash of its hue (the ghost recipe),
122
- // deepening on hover and press.
123
- // The fill reads the wash utilities; `--btn-bg` only feeds the spread.
124
- destructive:
125
- 'bg-destructive-wash group-hover:bg-destructive-wash-hover group-active:bg-destructive-wash-active [--btn-bg:var(--destructive-wash)] group-hover:[--btn-bg:var(--destructive-wash-hover)] group-active:[--btn-bg:var(--destructive-wash-active)] shadow-[0_0_0_1px_var(--btn-bg)] group-active:shadow-[0_0_0_0px_var(--btn-bg)]',
126
- success:
127
- 'bg-success-wash group-hover:bg-success-wash-hover group-active:bg-success-wash-active [--btn-bg:var(--success-wash)] group-hover:[--btn-bg:var(--success-wash-hover)] group-active:[--btn-bg:var(--success-wash-active)] shadow-[0_0_0_1px_var(--btn-bg)] group-active:shadow-[0_0_0_0px_var(--btn-bg)]',
128
- link: 'bg-transparent',
129
- }
130
-
131
- /* Forced-active (`active` prop): pressed colors at full size; the
132
- geometric press-collapse still reacts on top. */
133
- const activeBgVariants: Record<string, string> = {
134
- primary:
135
- '[--btn-bg:var(--primary-active)] bg-[var(--btn-bg)] shadow-[0_0_0_1px_var(--btn-bg),var(--shadow-button-primary)] group-active:shadow-[0_0_0_0px_var(--btn-bg),var(--shadow-button-primary)]',
136
- secondary:
137
- '[--btn-bg:var(--accent)] bg-[var(--btn-bg)] shadow-[0_0_0_1px_var(--btn-bg)] group-active:shadow-[0_0_0_0px_var(--btn-bg)]',
138
- tertiary:
139
- 'bg-active shadow-[0_0_0_1px_var(--border),inset_0_0_0_0px_var(--border)] group-active:shadow-[0_0_0_0px_var(--border),inset_0_0_0_1px_var(--border)]',
140
- ghost: 'bg-active shadow-[0_0_0_1px_var(--active)] group-active:shadow-[0_0_0_0px_var(--active)]',
141
- destructive:
142
- 'bg-destructive-wash-active [--btn-bg:var(--destructive-wash-active)] shadow-[0_0_0_1px_var(--btn-bg)] group-active:shadow-[0_0_0_0px_var(--btn-bg)]',
143
- success:
144
- 'bg-success-wash-active [--btn-bg:var(--success-wash-active)] shadow-[0_0_0_1px_var(--btn-bg)] group-active:shadow-[0_0_0_0px_var(--btn-bg)]',
145
- // A held link shows its underline.
146
- link: 'bg-transparent',
147
- }
148
-
149
- const { exit: _exit, ...enter } = spring.moderate
150
-
151
- /* Loading morph (local): the surface layer contracts from the button's box
152
- into a circle one control tall around the spinner, on one progress value
153
- like the morph layer (lib/use-morph.ts). The button's own box never
154
- changes, so nothing around it moves. Left and right mix a share of the
155
- button's width with pixels, so a stretched (w-full) button keeps the
156
- circle centred without measuring its width. At rest the inline geometry
157
- is dropped and the classes (inset-px, the inherited radius) take over. */
158
- function useLoadingMorph(loading: boolean) {
159
- const surface = useRef<HTMLSpanElement>(null)
160
- const progress = useMotionValue(loading ? 1 : 0)
161
- const reduced = useReduceMotion()
162
-
163
- useLayoutEffect(() => {
164
- const el = surface.current
165
- const root = el?.parentElement
166
- if (!el || !root || (!loading && progress.get() === 0)) return
167
- const height = root.offsetHeight
168
- // The surface sits 1px inside the button (its spread fills that pixel back out).
169
- const circle = height / 2 - 1
170
- const rest = Math.min(Number.parseFloat(getComputedStyle(root).borderTopLeftRadius) || 0, circle)
171
- const render = (p: number) => {
172
- const side = `calc(${50 * p}% + ${1 - (height / 2) * p}px)`
173
- el.style.left = side
174
- el.style.right = side
175
- el.style.borderRadius = `${rest + (circle - rest) * p}px`
176
- }
177
- const settle = () => el.removeAttribute('style')
178
- const target = loading ? 1 : 0
179
- if (reduced || progress.get() === target) {
180
- progress.jump(target)
181
- return loading ? render(1) : settle()
182
- }
183
- const controls = animate(progress, target, {
184
- ...enter,
185
- onUpdate: render,
186
- onComplete: loading ? undefined : settle,
187
- })
188
- return () => controls.stop()
189
- }, [loading, reduced])
190
-
191
- return surface
192
- }
193
-
194
- /**
195
- * Versatile button with variants, sizes, loading state, and icon support.
196
- *
197
- * Four variants (primary, secondary, tertiary, ghost) on the two-step size
198
- * ladder: 36px by default, 28px compact — an explicit `size` wins, otherwise
199
- * the button follows the surrounding SizeProvider. For hosts with a
200
- * shadcn-style API there are four more: `outline` (tertiary by its other
201
- * name), `destructive` and `success` (the hue as the label on a wash of
202
- * itself, never a solid fill, so a view keeps one primary) and `link`
203
- * (underlined text on the control height). The press effect shrinks
204
- * the fill by exactly 1px per side at any width, icons thicken their stroke
205
- * on hover, and `loading` folds the surface into a circle around a spinner
206
- * (and back when it ends) while the button keeps its box, so nothing around
207
- * it moves. Built on Base UI's Button; `asChild` renders your own
208
- * element (e.g. a link) with the button's styling instead.
209
- *
210
- * Also exported from this item:
211
- * - `buttonVariants` — the class recipe (also from `button/variants`, safe in
212
- * server code): `variant`, `size` (aliases included), `iconLeft`,
213
- * `iconRight`, `surface`. On its own it gives the root classes, and the fill
214
- * lives on the Button's inner layer; `surface: true` puts the fill, ring,
215
- * hover, press and `aria-disabled` look on the element itself, for an
216
- * anchor you can't render through `asChild`. Prefer `<Button asChild>`
217
- * where you can: it keeps the press shrink, the icon stroke and `loading`.
218
- *
219
- * @example {@include ./examples.mdx}
220
- */
221
- const Button = forwardRef<HTMLButtonElement, ButtonProps>(
222
- (
223
- {
224
- className,
225
- variant,
226
- size,
227
- asChild = false,
228
- loading = false,
229
- leadingIcon: LeadingIcon,
230
- trailingIcon: TrailingIcon,
231
- active = false,
232
- disabled,
233
- children,
234
- style,
235
- ...props
236
- },
237
- ref,
238
- ) => {
239
- // asChild: the user's element becomes the root while the button's internal
240
- // structure (bg layer, content wrapper, spinner, icons) survives as its
241
- // children — the element's own children become the label. We clone the
242
- // element directly instead of routing through ButtonPrimitive's `render`:
243
- // Base UI would bolt button semantics (role="button", Space activation)
244
- // onto e.g. a link, where plain-link output is wanted.
245
- const asChildElement =
246
- asChild && isValidElement(children)
247
- ? (children as ReactElement<{
248
- children?: ReactNode
249
- className?: string
250
- style?: React.CSSProperties
251
- ref?: React.Ref<HTMLButtonElement>
252
- 'aria-busy'?: boolean
253
- 'aria-disabled'?: boolean | 'true' | 'false'
254
- onClick?: React.MouseEventHandler<HTMLButtonElement>
255
- }>)
256
- : null
257
- const label = asChildElement ? asChildElement.props.children : children
258
- // Resolve the size: explicit prop (legacy aliases mapped onto the
259
- // canonical ladder) > surrounding SizeProvider > default.
260
- const contextSize = useSizeVariant()
261
- const resolvedSize: ButtonSizeCanonical = size
262
- ? (legacySizeAliases[size] ?? (size as ButtonSizeCanonical))
263
- : contextSize === 'compact'
264
- ? 'compact'
265
- : 'default'
266
- const isIconOnly = resolvedSize === 'icon' || resolvedSize === 'icon-compact'
267
- const isCompact = resolvedSize === 'compact' || resolvedSize === 'icon-compact'
268
- const iconSize = isCompact ? 14 : 16
269
- // Spinner box tracks the button height so the loading glyph stays
270
- // proportionate across sizes.
271
- const spinnerSizeClass = isCompact ? 'h-7 w-7' : 'h-9 w-9'
272
- const shape = useShape()
273
- // `outline` is tertiary by its shadcn name.
274
- const surfaceVariant = variant === 'outline' ? 'tertiary' : (variant ?? 'primary')
275
- const bgClass = active ? activeBgVariants[surfaceVariant] : bgVariants[surfaceVariant]
276
-
277
- const surface = useLoadingMorph(loading)
278
-
279
- const internals = (
280
- <>
281
- <span
282
- ref={surface}
283
- aria-hidden
284
- className={cn(
285
- 'absolute inset-px rounded-[inherit] transition-[box-shadow,background-color] [transition-duration:180ms,80ms] [transition-timing-function:cubic-bezier(0.23,1,0.32,1),ease] group-active:[transition-duration:80ms,80ms]',
286
- bgClass,
287
- )}
288
- />
289
- {/* The label keeps its place while loading, so the button keeps its
290
- box. Whatever leaves goes on the fast exit, so the label and the
291
- spinner barely overlap while the surface folds or unfolds. */}
292
- <motion.span
293
- className="relative inline-flex items-center justify-center gap-[inherit]"
294
- initial={false}
295
- animate={{ opacity: loading ? 0 : 1 }}
296
- transition={loading ? spring.fast.exit : enter}
297
- >
298
- {isIconOnly ? (
299
- <span className="[&_svg]:stroke-[1.5] [&_svg]:transition-[stroke-width] [&_svg]:duration-fast group-hover:[&_svg]:stroke-[2]">
300
- {label}
301
- </span>
302
- ) : (
303
- <>
304
- {LeadingIcon && (
305
- <LeadingIcon
306
- size={iconSize}
307
- strokeWidth={1.5}
308
- className="transition-[stroke-width] duration-fast group-hover:stroke-[2]"
309
- />
310
- )}
311
- {/* text-box only applies to block containers, so the trim lives
312
- on the label span (a blockified flex item), not the flex root.
313
- The button's height is fixed (h-*), so this doesn't change
314
- layout — it just centers the cap-to-baseline box optically. */}
315
- <span className="[text-box:trim-both_cap_alphabetic]">{label}</span>
316
- {TrailingIcon && (
317
- <TrailingIcon
318
- size={iconSize}
319
- strokeWidth={1.5}
320
- className="transition-[stroke-width] duration-fast group-hover:stroke-[2]"
321
- />
322
- )}
323
- </>
324
- )}
325
- </motion.span>
326
- <AnimatePresence initial={false}>
327
- {loading && (
328
- <motion.span
329
- key="spinner"
330
- aria-hidden
331
- className="absolute inset-0 flex items-center justify-center"
332
- initial={{ opacity: 0, scale: 0.6 }}
333
- animate={{ opacity: 1, scale: 1 }}
334
- exit={{ opacity: 0, scale: 0.6, transition: spring.fast.exit }}
335
- transition={enter}
336
- >
337
- <svg className={spinnerSizeClass} viewBox="0 0 24 24" fill="none">
338
- <path
339
- d="M 12 12 C 14 8.5 19 8.5 19 12 C 19 15.5 14 15.5 12 12 C 10 8.5 5 8.5 5 12 C 5 15.5 10 15.5 12 12 Z"
340
- stroke="currentColor"
341
- strokeWidth="1.125"
342
- strokeLinecap="round"
343
- pathLength="100"
344
- style={{
345
- strokeDasharray: '15 85',
346
- animation: 'spinner-move 2s linear infinite, spinner-dash 4s ease-in-out infinite',
347
- }}
348
- />
349
- </svg>
350
- </motion.span>
351
- )}
352
- </AnimatePresence>
353
- </>
354
- )
355
-
356
- const rootClassName = cn(
357
- buttonVariants({
358
- variant,
359
- size: resolvedSize,
360
- iconLeft: !isIconOnly && !!LeadingIcon,
361
- iconRight: !isIconOnly && !!TrailingIcon,
362
- }),
363
- shape.button,
364
- // A loading button is disabled through aria-disabled (it keeps focus),
365
- // which the variants' `disabled:` styles don't see.
366
- 'aria-disabled:opacity-50 aria-disabled:pointer-events-none',
367
- active && variant === 'link' && 'decoration-current',
368
- className,
369
- )
370
-
371
- if (asChildElement) {
372
- const childProps = asChildElement.props
373
- const onClick = props.onClick ?? childProps.onClick
374
- return cloneElement(
375
- asChildElement,
376
- {
377
- ...props,
378
- ref,
379
- 'aria-busy': loading || undefined,
380
- 'aria-disabled': loading || props['aria-disabled'] || childProps['aria-disabled'],
381
- // A pending action blocks a second press (and a link's navigation).
382
- onClick: (event: React.MouseEvent<HTMLButtonElement>) => {
383
- if (loading) {
384
- event.preventDefault()
385
- event.stopPropagation()
386
- return
387
- }
388
- onClick?.(event)
389
- },
390
- className: cn(rootClassName, childProps.className),
391
- style: { ...style, ...childProps.style },
392
- },
393
- internals,
394
- )
395
- }
396
-
397
- return (
398
- <ButtonPrimitive
399
- // Base UI's `ButtonPrimitive` forwards to an HTMLButtonElement;
400
- // keep the public ref type narrow so consumers see the right type.
401
- ref={ref as React.Ref<HTMLButtonElement>}
402
- className={rootClassName}
403
- disabled={disabled || loading}
404
- // While loading, Base UI disables it with aria-disabled instead of
405
- // the attribute, so a keyboard user keeps focus on it; it still
406
- // ignores presses.
407
- focusableWhenDisabled={loading}
408
- aria-busy={loading || undefined}
409
- style={style}
410
- {...props}
411
- >
412
- {internals}
413
- </ButtonPrimitive>
414
- )
415
- },
416
- )
417
-
418
- Button.displayName = 'Button'
419
-
420
- export type { ButtonSizeCanonical, ButtonVariantsOptions } from './variants'
421
- export type { ButtonProps, ButtonSize, ButtonVariant }
422
- export { Button, buttonVariants }
423
-
424
- export default Button
@@ -1,25 +0,0 @@
1
- /*
2
- * Local addition (not part of Fluid Functionalism): the docs stage of the
3
- * Button page's loading morph. A Button that loads for a moment when pressed,
4
- * so the example stays hook-free.
5
- */
6
-
7
- import { useEffect, useState } from 'react'
8
- import Button, { type ButtonProps } from './index'
9
-
10
- interface LoadingDemoProps extends ButtonProps {
11
- /** How long a press keeps the button loading, in ms. */
12
- duration?: number
13
- }
14
-
15
- export default function LoadingDemo({ duration = 2000, ...props }: LoadingDemoProps) {
16
- const [loading, setLoading] = useState(false)
17
-
18
- useEffect(() => {
19
- if (!loading) return
20
- const timer = setTimeout(() => setLoading(false), duration)
21
- return () => clearTimeout(timer)
22
- }, [loading, duration])
23
-
24
- return <Button {...props} loading={loading} onClick={() => setLoading(true)} />
25
- }