@justanarthur/modo-ui 0.5.0 → 0.6.0-canary.1

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 (467) hide show
  1. package/DESIGN.md +608 -0
  2. package/README.md +66 -44
  3. package/blocks/sidebar-app/examples.mdx +33 -0
  4. package/blocks/sidebar-app/index.tsx +218 -0
  5. package/blocks/sidebar-app/inset-topbar.tsx +25 -0
  6. package/blocks/sidebar-app/menu-grid.ts +26 -0
  7. package/blocks/sidebar-app/nav-data.ts +36 -0
  8. package/blocks/sidebar-app/search-field.tsx +48 -0
  9. package/blocks/sidebar-app/user-footer.tsx +67 -0
  10. package/blocks/sidebar-app/workspace-header.tsx +129 -0
  11. package/components/AI/input-message/examples.mdx +124 -0
  12. package/components/AI/input-message/file-thumbnail.tsx +173 -0
  13. package/components/AI/input-message/index.tsx +1383 -0
  14. package/components/AI/thinking-indicator/examples.mdx +27 -0
  15. package/components/AI/thinking-indicator/index.tsx +210 -0
  16. package/components/accordion/examples.mdx +148 -0
  17. package/components/accordion/index.tsx +1034 -0
  18. package/components/avatar/examples.mdx +95 -0
  19. package/components/avatar/index.tsx +129 -0
  20. package/components/badge/change-demo.tsx +29 -0
  21. package/components/badge/examples.mdx +120 -0
  22. package/components/badge/index.tsx +225 -0
  23. package/components/badge/variants.ts +73 -0
  24. package/components/button/examples.mdx +164 -0
  25. package/components/button/index.tsx +416 -0
  26. package/components/button/loading-demo.tsx +25 -0
  27. package/components/button/variants.ts +166 -0
  28. package/components/card/examples.mdx +212 -0
  29. package/components/card/index.tsx +1037 -0
  30. package/components/copy/examples.mdx +87 -0
  31. package/components/copy/index.tsx +482 -0
  32. package/components/inputs/checkbox/examples.mdx +83 -0
  33. package/components/inputs/checkbox/index.tsx +611 -0
  34. package/components/inputs/checkbox/select-all-demo.tsx +48 -0
  35. package/components/inputs/color-picker/examples.mdx +72 -0
  36. package/components/inputs/color-picker/index.tsx +1955 -0
  37. package/components/inputs/color-picker/variants.ts +401 -0
  38. package/components/inputs/combobox/examples.mdx +273 -0
  39. package/components/inputs/combobox/index.tsx +1436 -0
  40. package/components/inputs/combobox/remote-demo.tsx +101 -0
  41. package/components/inputs/date-time/calendar/examples.mdx +34 -0
  42. package/components/inputs/date-time/calendar/index.tsx +351 -0
  43. package/components/inputs/date-time/calendar/use-month-window.ts +163 -0
  44. package/components/inputs/date-time/date-picker/examples.mdx +166 -0
  45. package/components/inputs/date-time/date-picker/index.tsx +385 -0
  46. package/components/inputs/date-time/date-picker/masked-date-input.tsx +336 -0
  47. package/components/inputs/date-time/date-range-picker/examples.mdx +29 -0
  48. package/components/inputs/date-time/date-range-picker/index.tsx +217 -0
  49. package/components/inputs/date-time/duration-picker/examples.mdx +16 -0
  50. package/components/inputs/date-time/duration-picker/index.tsx +189 -0
  51. package/components/inputs/date-time/month-picker/examples.mdx +16 -0
  52. package/components/inputs/date-time/month-picker/index.tsx +190 -0
  53. package/components/inputs/date-time/picker-shell.tsx +395 -0
  54. package/components/inputs/date-time/time-columns.tsx +91 -0
  55. package/components/inputs/date-time/time-picker/examples.mdx +26 -0
  56. package/components/inputs/date-time/time-picker/index.tsx +166 -0
  57. package/components/inputs/field/examples.mdx +113 -0
  58. package/components/inputs/field/index.tsx +267 -0
  59. package/components/inputs/file-dropzone/examples.mdx +73 -0
  60. package/components/inputs/file-dropzone/file-row.tsx +201 -0
  61. package/components/inputs/file-dropzone/index.tsx +296 -0
  62. package/components/inputs/file-dropzone/upload-demo.tsx +67 -0
  63. package/components/inputs/file-dropzone/variants.ts +94 -0
  64. package/components/inputs/input/examples.mdx +135 -0
  65. package/components/inputs/input/index.tsx +494 -0
  66. package/components/inputs/input/password-demo.tsx +30 -0
  67. package/components/inputs/label/examples.mdx +67 -0
  68. package/components/inputs/label/index.tsx +85 -0
  69. package/components/inputs/number-input/examples.mdx +56 -0
  70. package/components/inputs/number-input/index.tsx +214 -0
  71. package/components/inputs/radio/examples.mdx +77 -0
  72. package/components/inputs/radio/index.tsx +636 -0
  73. package/components/inputs/search/examples.mdx +74 -0
  74. package/components/inputs/search/index.tsx +333 -0
  75. package/components/inputs/select/examples.mdx +218 -0
  76. package/components/inputs/select/index.tsx +862 -0
  77. package/components/inputs/select/variants.ts +32 -0
  78. package/components/inputs/slider/examples.mdx +127 -0
  79. package/components/inputs/slider/index.tsx +1734 -0
  80. package/components/inputs/switch/examples.mdx +94 -0
  81. package/components/inputs/switch/index.tsx +504 -0
  82. package/components/inputs/textarea/examples.mdx +88 -0
  83. package/components/inputs/textarea/index.tsx +161 -0
  84. package/components/navigation/navigation-menu/examples.mdx +109 -0
  85. package/components/navigation/navigation-menu/index.tsx +613 -0
  86. package/components/navigation/pagination/examples.mdx +68 -0
  87. package/components/navigation/pagination/index.tsx +401 -0
  88. package/components/navigation/sidebar/examples.mdx +319 -0
  89. package/components/navigation/sidebar/index.tsx +453 -0
  90. package/components/navigation/sidebar/sidebar-core.tsx +1408 -0
  91. package/components/navigation/sidebar/sidebar-menu.tsx +1495 -0
  92. package/components/navigation/sidebar/sidebar.css +74 -0
  93. package/components/navigation/sidebar/variants.ts +55 -0
  94. package/components/navigation/tabs/examples.mdx +89 -0
  95. package/components/navigation/tabs/index.tsx +494 -0
  96. package/components/navigation/tabs-subtle/examples.mdx +49 -0
  97. package/components/navigation/tabs-subtle/index.tsx +512 -0
  98. package/components/overlays/alert-dialog/examples.mdx +123 -0
  99. package/components/overlays/alert-dialog/index.tsx +276 -0
  100. package/components/overlays/alert-dialog/pending-demo.tsx +36 -0
  101. package/components/overlays/command/examples.mdx +113 -0
  102. package/components/overlays/command/index.tsx +425 -0
  103. package/components/overlays/context-menu/examples.mdx +56 -0
  104. package/components/overlays/context-menu/index.tsx +279 -0
  105. package/components/overlays/dialog/examples.mdx +165 -0
  106. package/components/overlays/dialog/index.tsx +451 -0
  107. package/components/overlays/dropdown/dropdown-search.tsx +395 -0
  108. package/components/overlays/dropdown/examples.mdx +294 -0
  109. package/components/overlays/dropdown/index.tsx +1169 -0
  110. package/components/overlays/dropdown/menu-item.tsx +464 -0
  111. package/components/overlays/dropdown/menu-parts.tsx +660 -0
  112. package/components/overlays/popover/anchored-demo.tsx +59 -0
  113. package/components/overlays/popover/examples.mdx +197 -0
  114. package/components/overlays/popover/index.tsx +317 -0
  115. package/components/overlays/preview-card/examples.mdx +61 -0
  116. package/components/overlays/preview-card/index.tsx +204 -0
  117. package/components/overlays/sheet/examples.mdx +101 -0
  118. package/components/overlays/sheet/index.tsx +307 -0
  119. package/components/overlays/toast/examples.mdx +85 -0
  120. package/components/overlays/toast/global-demo.tsx +72 -0
  121. package/components/overlays/toast/index.tsx +544 -0
  122. package/components/overlays/toast/manager.ts +193 -0
  123. package/components/overlays/tooltip/examples.mdx +106 -0
  124. package/components/overlays/tooltip/index.tsx +539 -0
  125. package/components/progress/examples.mdx +59 -0
  126. package/components/progress/index.tsx +174 -0
  127. package/components/progress/progress.css +49 -0
  128. package/components/progress/step-demo.tsx +27 -0
  129. package/components/table/examples.mdx +184 -0
  130. package/components/table/index.tsx +525 -0
  131. package/components/table/sort-demo.tsx +61 -0
  132. package/dist/base.css +100 -0
  133. package/dist/blocks/sidebar-app/index.js +39 -26
  134. package/dist/components/AI/input-message/index.js +22 -16
  135. package/dist/components/AI/thinking-indicator/index.js +9 -6
  136. package/dist/components/accordion/index.js +16 -13
  137. package/dist/components/avatar/index.d.ts +46 -0
  138. package/dist/components/avatar/index.js +18 -0
  139. package/dist/components/badge/index.d.ts +18 -11
  140. package/dist/components/badge/index.js +11 -7
  141. package/dist/components/badge/variants.d.ts +33 -0
  142. package/dist/components/badge/variants.js +10 -0
  143. package/dist/components/button/index.d.ts +53 -17
  144. package/dist/components/button/index.js +10 -6
  145. package/dist/components/button/variants.d.ts +39 -0
  146. package/dist/components/button/variants.js +8 -0
  147. package/dist/components/card/index.d.ts +16 -3
  148. package/dist/components/card/index.js +16 -11
  149. package/dist/components/copy/index.js +13 -9
  150. package/dist/components/inputs/checkbox/index.d.ts +28 -9
  151. package/dist/components/inputs/checkbox/index.js +17 -13
  152. package/dist/components/inputs/color-picker/index.d.ts +13 -13
  153. package/dist/components/inputs/color-picker/index.js +25 -18
  154. package/dist/components/inputs/color-picker/variants.d.ts +61 -0
  155. package/dist/components/inputs/color-picker/variants.js +28 -0
  156. package/dist/components/inputs/combobox/index.d.ts +60 -17
  157. package/dist/components/inputs/combobox/index.js +26 -18
  158. package/dist/components/inputs/date-time/calendar/index.js +14 -11
  159. package/dist/components/inputs/date-time/date-picker/index.d.ts +53 -11
  160. package/dist/components/inputs/date-time/date-picker/index.js +33 -25
  161. package/dist/components/inputs/date-time/date-range-picker/index.d.ts +3 -3
  162. package/dist/components/inputs/date-time/date-range-picker/index.js +32 -24
  163. package/dist/components/inputs/date-time/duration-picker/index.d.ts +3 -3
  164. package/dist/components/inputs/date-time/duration-picker/index.js +32 -24
  165. package/dist/components/inputs/date-time/month-picker/index.d.ts +3 -3
  166. package/dist/components/inputs/date-time/month-picker/index.js +31 -23
  167. package/dist/components/inputs/date-time/time-picker/index.d.ts +3 -3
  168. package/dist/components/inputs/date-time/time-picker/index.js +32 -24
  169. package/dist/components/inputs/field/index.d.ts +116 -0
  170. package/dist/components/inputs/field/index.js +28 -0
  171. package/dist/components/inputs/file-dropzone/index.d.ts +165 -0
  172. package/dist/components/inputs/file-dropzone/index.js +32 -0
  173. package/dist/components/inputs/file-dropzone/variants.d.ts +35 -0
  174. package/dist/components/inputs/file-dropzone/variants.js +12 -0
  175. package/dist/components/inputs/input/index.d.ts +95 -13
  176. package/dist/components/inputs/input/index.js +17 -9
  177. package/dist/components/inputs/label/index.d.ts +32 -0
  178. package/dist/components/inputs/label/index.js +11 -0
  179. package/dist/components/inputs/number-input/index.d.ts +84 -0
  180. package/dist/components/inputs/number-input/index.js +20 -0
  181. package/dist/components/inputs/radio/index.d.ts +20 -5
  182. package/dist/components/inputs/radio/index.js +15 -12
  183. package/dist/components/inputs/search/index.d.ts +3 -3
  184. package/dist/components/inputs/search/index.js +25 -18
  185. package/dist/components/inputs/select/index.d.ts +41 -11
  186. package/dist/components/inputs/select/index.js +27 -14
  187. package/dist/components/inputs/select/variants.d.ts +5 -0
  188. package/dist/components/inputs/select/variants.js +6 -0
  189. package/dist/components/inputs/slider/index.d.ts +2 -0
  190. package/dist/components/inputs/slider/index.js +7 -5
  191. package/dist/components/inputs/switch/index.d.ts +8 -2
  192. package/dist/components/inputs/switch/index.js +14 -12
  193. package/dist/components/inputs/textarea/index.d.ts +4 -0
  194. package/dist/components/inputs/textarea/index.js +8 -6
  195. package/dist/components/navigation/navigation-menu/index.d.ts +114 -0
  196. package/dist/components/navigation/navigation-menu/index.js +40 -0
  197. package/dist/components/navigation/pagination/index.d.ts +75 -0
  198. package/dist/components/navigation/pagination/index.js +39 -0
  199. package/dist/components/navigation/sidebar/index.d.ts +44 -21
  200. package/dist/components/navigation/sidebar/index.js +41 -31
  201. package/dist/components/navigation/sidebar/variants.d.ts +25 -0
  202. package/dist/components/navigation/sidebar/variants.js +28 -0
  203. package/dist/components/navigation/tabs/index.d.ts +8 -2
  204. package/dist/components/navigation/tabs/index.js +14 -10
  205. package/dist/components/navigation/tabs-subtle/index.js +12 -10
  206. package/dist/components/overlays/alert-dialog/index.d.ts +135 -16
  207. package/dist/components/overlays/alert-dialog/index.js +22 -13
  208. package/dist/components/overlays/command/index.d.ts +25 -20
  209. package/dist/components/overlays/command/index.js +24 -17
  210. package/dist/components/overlays/context-menu/index.d.ts +33 -17
  211. package/dist/components/overlays/context-menu/index.js +27 -18
  212. package/dist/components/overlays/dialog/index.d.ts +42 -16
  213. package/dist/components/overlays/dialog/index.js +19 -12
  214. package/dist/components/overlays/dropdown/index.d.ts +256 -30
  215. package/dist/components/overlays/dropdown/index.js +42 -17
  216. package/dist/components/overlays/popover/index.d.ts +38 -12
  217. package/dist/components/overlays/popover/index.js +12 -9
  218. package/dist/components/overlays/preview-card/index.js +11 -8
  219. package/dist/components/overlays/sheet/index.d.ts +25 -8
  220. package/dist/components/overlays/sheet/index.js +18 -13
  221. package/dist/components/overlays/toast/index.d.ts +209 -50
  222. package/dist/components/overlays/toast/index.js +22 -10
  223. package/dist/components/overlays/tooltip/index.d.ts +123 -21
  224. package/dist/components/overlays/tooltip/index.js +15 -6
  225. package/dist/components/progress/index.d.ts +56 -0
  226. package/dist/components/progress/index.js +15 -0
  227. package/dist/components/table/index.d.ts +63 -8
  228. package/dist/components/table/index.js +20 -9
  229. package/dist/index.d.ts +2380 -831
  230. package/dist/index.js +229 -82
  231. package/dist/lib/date-locale.d.ts +14 -0
  232. package/dist/lib/date-locale.js +16 -0
  233. package/dist/lib/field-classes.d.ts +144 -0
  234. package/dist/lib/field-classes.js +40 -0
  235. package/dist/lib/field-variants.d.ts +47 -0
  236. package/dist/lib/field-variants.js +26 -0
  237. package/dist/lib/fluid-hover-highlight.d.ts +112 -0
  238. package/dist/lib/fluid-hover-highlight.js +17 -0
  239. package/dist/lib/focus-ring.d.ts +18 -0
  240. package/dist/lib/focus-ring.js +10 -0
  241. package/dist/lib/goo-indicator.d.ts +46 -0
  242. package/dist/lib/goo-indicator.js +14 -0
  243. package/dist/lib/icon-context.d.ts +30 -0
  244. package/dist/lib/icon-context.js +12 -0
  245. package/dist/lib/inert.d.ts +9 -0
  246. package/dist/lib/inert.js +8 -0
  247. package/dist/lib/morph-layers.d.ts +93 -0
  248. package/dist/lib/morph-layers.js +13 -0
  249. package/dist/lib/morph-part.d.ts +21 -0
  250. package/dist/lib/morph-part.js +12 -0
  251. package/dist/lib/popup.d.ts +20 -0
  252. package/dist/lib/popup.js +12 -0
  253. package/dist/lib/reduced-motion.d.ts +9 -0
  254. package/dist/lib/reduced-motion.js +6 -0
  255. package/dist/lib/row-index.d.ts +10 -0
  256. package/dist/lib/row-index.js +8 -0
  257. package/dist/lib/row-order.d.ts +28 -0
  258. package/dist/lib/row-order.js +10 -0
  259. package/dist/lib/scroll-column.d.ts +27 -0
  260. package/dist/lib/scroll-column.js +18 -0
  261. package/dist/lib/shape-context.d.ts +30 -0
  262. package/dist/lib/shape-context.js +12 -0
  263. package/dist/lib/size-context.d.ts +94 -0
  264. package/dist/lib/size-context.js +20 -0
  265. package/dist/lib/size-scale.d.ts +79 -0
  266. package/dist/lib/size-scale.js +8 -0
  267. package/dist/lib/slot.d.ts +19 -0
  268. package/dist/lib/slot.js +6 -0
  269. package/dist/lib/springs.d.ts +44 -0
  270. package/dist/lib/springs.js +8 -0
  271. package/dist/lib/surface-classes.d.ts +19 -0
  272. package/dist/lib/surface-classes.js +10 -0
  273. package/dist/lib/surface-context.d.ts +9 -0
  274. package/dist/lib/surface-context.js +8 -0
  275. package/dist/lib/use-controllable-state.d.ts +2 -0
  276. package/dist/lib/use-controllable-state.js +6 -0
  277. package/dist/lib/use-fluid-hover.d.ts +138 -0
  278. package/dist/lib/use-fluid-hover.js +14 -0
  279. package/dist/lib/use-is-mobile.d.ts +2 -0
  280. package/dist/lib/use-is-mobile.js +6 -0
  281. package/dist/lib/use-keyboard-nav-gate.d.ts +20 -0
  282. package/dist/lib/use-keyboard-nav-gate.js +7 -0
  283. package/dist/lib/use-merge-split.d.ts +47 -0
  284. package/dist/lib/use-merge-split.js +15 -0
  285. package/dist/lib/use-morph.d.ts +101 -0
  286. package/dist/lib/use-morph.js +20 -0
  287. package/dist/lib/use-touch-primary.d.ts +2 -0
  288. package/dist/lib/use-touch-primary.js +6 -0
  289. package/dist/lib/utils.d.ts +3 -0
  290. package/dist/lib/utils.js +6 -0
  291. package/dist/preset.d.ts +17 -0
  292. package/dist/preset.js +22 -2
  293. package/dist/primitives/code/index.js +7 -5
  294. package/dist/primitives/craft/index.js +9 -6
  295. package/dist/primitives/fluid-hover/index.js +9 -8
  296. package/dist/primitives/morph/index.js +15 -10
  297. package/dist/primitives/motion/index.js +10 -8
  298. package/dist/primitives/scroll-area/index.js +4 -3
  299. package/dist/primitives/separator/index.d.ts +25 -0
  300. package/dist/primitives/separator/index.js +9 -0
  301. package/dist/primitives/sizes/index.js +6 -4
  302. package/dist/primitives/skeleton/index.d.ts +35 -0
  303. package/dist/primitives/skeleton/index.js +12 -0
  304. package/dist/primitives/surface/index.js +6 -4
  305. package/dist/shared/{components-3mkepka5.js → components-0jmqhx2k.js} +28 -25
  306. package/dist/shared/{components-s5ngg32c.js → components-0khyhjn5.js} +121 -47
  307. package/dist/shared/{components-sn1rxx1d.js → components-0n95jaa2.js} +12 -11
  308. package/dist/shared/components-0x6mx1p5.js +252 -0
  309. package/dist/shared/components-124epb7f.js +53 -0
  310. package/dist/shared/{components-gz8zf3ec.js → components-1k0wv53d.js} +15 -43
  311. package/dist/shared/components-26snnbs9.js +322 -0
  312. package/dist/shared/{components-2dxkv6zm.js → components-2c0qcfx4.js} +8 -7
  313. package/dist/shared/{components-y9y0g8er.js → components-2gk4yywm.js} +23 -20
  314. package/dist/shared/{components-473g814w.js → components-2mt9te6d.js} +243 -137
  315. package/dist/shared/{components-hawvy2qz.js → components-2z48g4fp.js} +9 -30
  316. package/dist/shared/components-34txk9rj.js +35 -0
  317. package/dist/shared/components-3h92d94m.js +23 -0
  318. package/dist/shared/{components-s2bz1xpk.js → components-49bsynbw.js} +8 -5
  319. package/dist/shared/components-4bwsd46w.js +71 -0
  320. package/dist/shared/{components-0q0sqfh4.js → components-4y816hwp.js} +27 -49
  321. package/dist/shared/components-52ngsgca.js +399 -0
  322. package/dist/shared/components-559tf7yb.js +28 -0
  323. package/dist/shared/components-591m0a57.js +500 -0
  324. package/dist/shared/components-5mkga9hj.js +440 -0
  325. package/dist/shared/{components-zfnmn5d4.js → components-5nsn4w8c.js} +79 -35
  326. package/dist/shared/{components-kwa6mrf9.js → components-5s0nfqvw.js} +14 -9
  327. package/dist/shared/{components-39vmfq9s.js → components-5t2f1kq7.js} +24 -22
  328. package/dist/shared/{components-dk0gjr5a.js → components-60atskhp.js} +19 -18
  329. package/dist/shared/components-6zng858q.js +53 -0
  330. package/dist/shared/components-72w4ge68.js +64 -0
  331. package/dist/shared/{components-cv16mryv.js → components-78x40gee.js} +13 -7
  332. package/dist/shared/components-79q3xxwr.js +179 -0
  333. package/dist/shared/{components-b2wyq2cq.js → components-7d4r7hh1.js} +39 -57
  334. package/dist/shared/components-7ntvvegr.js +316 -0
  335. package/dist/shared/{components-5vgpnxgy.js → components-7xrqvk6g.js} +20 -17
  336. package/dist/shared/components-8nwsqjn8.js +34 -0
  337. package/dist/shared/{components-5xc2j8jz.js → components-8tt3xbb5.js} +19 -18
  338. package/dist/shared/{components-vc7vq5nz.js → components-9as3rf68.js} +6 -3
  339. package/dist/shared/components-adkarj8n.js +35 -0
  340. package/dist/shared/{components-b3c5et5b.js → components-ae9fncz4.js} +32 -31
  341. package/dist/shared/{components-jthe1pwb.js → components-ajhdkhjz.js} +101 -404
  342. package/dist/shared/{components-hqeq3w9q.js → components-awra5sff.js} +24 -16
  343. package/dist/shared/components-b1e239g3.js +28 -0
  344. package/dist/shared/{components-q5xj5c16.js → components-b1f2bjf1.js} +9 -8
  345. package/dist/shared/components-c9yr4ed1.js +44 -0
  346. package/dist/shared/components-d3shbwqq.js +95 -0
  347. package/dist/shared/{components-wvp1m52g.js → components-d6vf38m5.js} +22 -18
  348. package/dist/shared/{components-jjw69c4c.js → components-dcc02hs2.js} +7 -4
  349. package/dist/shared/{components-men63rwj.js → components-de99ykt7.js} +3 -2
  350. package/dist/shared/components-dhgk6mbt.js +16 -0
  351. package/dist/shared/components-drxrtnp2.js +25 -0
  352. package/dist/shared/{components-mjthm1kf.js → components-egy4tyjt.js} +11 -8
  353. package/dist/shared/components-f0xfpkhh.js +82 -0
  354. package/dist/shared/components-f7h57m6t.js +59 -0
  355. package/dist/shared/{components-frw34b65.js → components-faww1hy7.js} +114 -123
  356. package/dist/shared/components-gjd5v54y.js +271 -0
  357. package/dist/shared/{components-39jq9va1.js → components-gvbapfen.js} +29 -24
  358. package/dist/shared/components-h6p86be3.js +19 -0
  359. package/dist/shared/components-h83az0bz.js +33 -0
  360. package/dist/shared/{components-ar66gnt0.js → components-h9vrr1j2.js} +37 -34
  361. package/dist/shared/components-hbk69y68.js +298 -0
  362. package/dist/shared/{components-rr7a6ja9.js → components-jamcmdsd.js} +59 -27
  363. package/dist/shared/{components-zrkzcayz.js → components-jqpefjsn.js} +54 -6
  364. package/dist/shared/{components-xxvdw7k1.js → components-k1b0vhcq.js} +37 -34
  365. package/dist/shared/{components-1z6tdadk.js → components-k74j4hwh.js} +20 -17
  366. package/dist/shared/components-kswxnm3h.js +24 -0
  367. package/dist/shared/components-m1m46jdv.js +1444 -0
  368. package/dist/shared/{components-5pnw8183.js → components-mjnzv5vj.js} +23 -22
  369. package/dist/shared/components-mkezgkgk.js +10 -0
  370. package/dist/shared/{components-jbbmdr74.js → components-mkkbbgb7.js} +36 -17
  371. package/dist/shared/{components-pbmqctfa.js → components-mpw667rb.js} +169 -65
  372. package/dist/shared/{components-6p8tfcd6.js → components-my6hpnn6.js} +22 -19
  373. package/dist/shared/{components-s083f38z.js → components-nbvtmgme.js} +22 -21
  374. package/dist/shared/{components-jwst3vtj.js → components-ncdgtg6b.js} +224 -76
  375. package/dist/shared/{components-vfbhxf5r.js → components-pd42emw3.js} +6 -5
  376. package/dist/shared/{components-hw9yk32z.js → components-pmfszv7a.js} +35 -23
  377. package/dist/shared/{components-chacnk61.js → components-pw06rbxq.js} +19 -19
  378. package/dist/shared/{components-qmmjx1qw.js → components-q3pbax3p.js} +71 -72
  379. package/dist/shared/components-q5h7v6rr.js +104 -0
  380. package/dist/shared/{components-kvr7f9hy.js → components-qdm3yh26.js} +19 -10
  381. package/dist/shared/{components-27nnvrwe.js → components-qs4rkqsj.js} +15 -14
  382. package/dist/shared/{components-ta7shw28.js → components-r8eqs7gz.js} +4 -4
  383. package/dist/shared/{components-c6s6b43g.js → components-rrbpmfx4.js} +3 -2
  384. package/dist/shared/{components-d6xtjht3.js → components-tarz14gk.js} +33 -30
  385. package/dist/shared/{components-n7mjr357.js → components-tz1fv35x.js} +3 -2
  386. package/dist/shared/components-tz4gaf1v.js +23 -0
  387. package/dist/shared/{components-zx6b6yby.js → components-vf45j12c.js} +4 -18
  388. package/dist/shared/{components-gc5vtq2f.js → components-vqpzqwcg.js} +63 -29
  389. package/dist/shared/components-wktrrb4m.js +38 -0
  390. package/dist/shared/{components-8snj490h.js → components-wwd363me.js} +78 -32
  391. package/dist/shared/components-xdqmt6ed.js +20 -0
  392. package/dist/shared/{components-h2ajmhwn.js → components-y1w3a6wx.js} +107 -35
  393. package/dist/shared/{components-59efxjd9.js → components-yhgq0qxx.js} +13 -12
  394. package/dist/shared/components-z3v0n0de.js +11 -0
  395. package/dist/shared/{components-7r3etddy.js → components-z9n19fyk.js} +19 -54
  396. package/dist/shared/components-zbvjzh1p.js +141 -0
  397. package/dist/styles.css +241 -16
  398. package/dist/tailwind.css +13 -0
  399. package/dist/tokens.css +34 -0
  400. package/index.ts +60 -0
  401. package/lib/date-locale.ts +50 -0
  402. package/lib/field-classes.ts +115 -0
  403. package/lib/field-variants.ts +117 -0
  404. package/lib/fluid-hover-highlight.tsx +139 -0
  405. package/lib/focus-ring.tsx +46 -0
  406. package/lib/goo-indicator.tsx +261 -0
  407. package/lib/icon-context.tsx +310 -0
  408. package/lib/inert.ts +22 -0
  409. package/lib/morph-layers.tsx +102 -0
  410. package/lib/morph-part.tsx +49 -0
  411. package/lib/popup.ts +52 -0
  412. package/lib/reduced-motion.ts +19 -0
  413. package/lib/row-index.tsx +29 -0
  414. package/lib/row-order.tsx +115 -0
  415. package/lib/scroll-column.tsx +244 -0
  416. package/lib/shape-context.tsx +124 -0
  417. package/lib/size-context.tsx +66 -0
  418. package/lib/size-scale.ts +115 -0
  419. package/lib/slot.ts +35 -0
  420. package/lib/springs.ts +62 -0
  421. package/lib/surface-classes.ts +58 -0
  422. package/lib/surface-context.tsx +21 -0
  423. package/lib/use-controllable-state.ts +44 -0
  424. package/lib/use-fluid-hover.ts +541 -0
  425. package/lib/use-is-mobile.ts +25 -0
  426. package/lib/use-keyboard-nav-gate.ts +45 -0
  427. package/lib/use-merge-split.tsx +209 -0
  428. package/lib/use-morph.ts +557 -0
  429. package/lib/use-touch-primary.tsx +43 -0
  430. package/lib/utils.ts +37 -0
  431. package/package.json +177 -4
  432. package/preset.ts +196 -0
  433. package/primitives/code/examples.mdx +27 -0
  434. package/primitives/code/index.tsx +80 -0
  435. package/primitives/craft/demos.tsx +173 -0
  436. package/primitives/craft/examples.mdx +138 -0
  437. package/primitives/craft/index.tsx +76 -0
  438. package/primitives/fluid-hover/examples.mdx +84 -0
  439. package/primitives/fluid-hover/index.tsx +316 -0
  440. package/primitives/morph/examples.mdx +135 -0
  441. package/primitives/morph/index.tsx +192 -0
  442. package/primitives/motion/examples.mdx +100 -0
  443. package/primitives/motion/index.tsx +160 -0
  444. package/primitives/scroll-area/examples.mdx +58 -0
  445. package/primitives/scroll-area/index.tsx +200 -0
  446. package/primitives/separator/examples.mdx +30 -0
  447. package/primitives/separator/index.tsx +53 -0
  448. package/primitives/sizes/examples.mdx +167 -0
  449. package/primitives/sizes/index.tsx +89 -0
  450. package/primitives/skeleton/examples.mdx +51 -0
  451. package/primitives/skeleton/index.tsx +72 -0
  452. package/primitives/skeleton/skeleton.css +15 -0
  453. package/primitives/surface/examples.mdx +173 -0
  454. package/primitives/surface/index.tsx +109 -0
  455. package/primitives/surface/surface.css +176 -0
  456. package/dist/shared/components-0sn7ypjw.js +0 -61
  457. package/dist/shared/components-1434p1mh.js +0 -123
  458. package/dist/shared/components-3ky44908.js +0 -9
  459. package/dist/shared/components-a86dytt0.js +0 -986
  460. package/dist/shared/components-gf5s6ap9.js +0 -42
  461. package/dist/shared/components-hyq23phv.js +0 -310
  462. package/dist/shared/components-ng72k97e.js +0 -128
  463. package/dist/shared/components-raknhppb.js +0 -64
  464. package/dist/shared/components-vynjtrk6.js +0 -19
  465. package/dist/shared/components-wy9jpqny.js +0 -146
  466. /package/dist/shared/{components-1v7rxytx.js → components-3ey0hzpb.js} +0 -0
  467. /package/dist/shared/{components-ezxhrmer.js → components-jcnjay5g.js} +0 -0
package/DESIGN.md ADDED
@@ -0,0 +1,608 @@
1
+ ---
2
+ version: alpha
3
+ name: UI — Fluid Functionalism for modo
4
+ description: >-
5
+ A quiet, neutral, dense interface where motion carries the meaning: springs instead of durations,
6
+ one hover highlight that glides between rows, labels that get heavier without moving, eight nesting
7
+ surface levels, and overlays that melt out of whatever opened them through a liquid neck. Light values
8
+ are the plain token names; `-dark` twins are the dark scheme (every color is one light-dark() token in
9
+ tokens/colors.css and primitives/surface/surface.css).
10
+ colors:
11
+ primary: "oklch(0.205 0 0)"
12
+ on-primary: "oklch(0.985 0 0)"
13
+ background: "oklch(0.985 0 0)"
14
+ background-dark: "oklch(0.205 0 0)"
15
+ foreground: "oklch(0.205 0 0)"
16
+ foreground-dark: "oklch(0.97 0 0)"
17
+ card: "oklch(1 0 0)"
18
+ card-dark: "oklch(0.264 0 0)"
19
+ muted: "oklch(0.967 0.001 286.375)"
20
+ muted-dark: "oklch(0.235 0 0)"
21
+ muted-foreground: "oklch(0.556 0 0)"
22
+ muted-foreground-dark: "oklch(0.7155 0 0)"
23
+ accent: "oklch(0.922 0 0)"
24
+ accent-dark: "oklch(0.439 0 0)"
25
+ selected: "oklch(0.87 0 0)"
26
+ selected-dark: "oklch(0.439 0 0)"
27
+ border: "color-mix(in srgb, oklch(0.205 0 0) 12%, transparent)"
28
+ border-dark: "color-mix(in srgb, oklch(0.97 0 0) 12%, transparent)"
29
+ input: "oklch(0.922 0 0)"
30
+ input-dark: "oklch(0.371 0 0)"
31
+ control-border: "oklch(0.708 0 0)"
32
+ control-border-dark: "oklch(0.556 0 0)"
33
+ hover: "rgb(0 0 0 / 0.04)"
34
+ hover-dark: "rgb(255 255 255 / 0.06)"
35
+ active: "rgb(0 0 0 / 0.07)"
36
+ active-dark: "rgb(255 255 255 / 0.1)"
37
+ scrim: "oklch(0 0 0 / 0.4)"
38
+ scrim-dark: "oklch(0 0 0 / 0.8)"
39
+ brand: "oklch(0.693 0.16 265)"
40
+ brand-hover: "oklch(0.649 0.164 264.76)"
41
+ focus-ring: "oklch(0.693 0.16 265)"
42
+ thumb: "oklch(1 0 0)"
43
+ destructive: "oklch(0.6368 0.2078 25.33)"
44
+ destructive-dark: "oklch(0.7106 0.1661 22.22)"
45
+ destructive-light: "oklch(0.971 0.013 17.38)"
46
+ destructive-light-dark: "oklch(0.2575 0.0886 26.04)"
47
+ destructive-strong: "oklch(0.47 0.175 27)"
48
+ destructive-strong-dark: "oklch(0.81 0.105 22)"
49
+ destructive-wash: "color-mix(in oklab, oklch(0.6368 0.2078 25.33) 16%, transparent)"
50
+ destructive-wash-dark: "color-mix(in oklab, oklch(0.7106 0.1661 22.22) 16%, transparent)"
51
+ destructive-wash-hover: "color-mix(in oklab, oklch(0.6368 0.2078 25.33) 20%, transparent)"
52
+ destructive-wash-hover-dark: "color-mix(in oklab, oklch(0.7106 0.1661 22.22) 20%, transparent)"
53
+ destructive-wash-active: "color-mix(in oklab, oklch(0.6368 0.2078 25.33) 24%, transparent)"
54
+ destructive-wash-active-dark: "color-mix(in oklab, oklch(0.7106 0.1661 22.22) 24%, transparent)"
55
+ success-strong: "oklch(0.46 0.12 151)"
56
+ success-strong-dark: "oklch(0.83 0.15 152)"
57
+ success-wash: "color-mix(in oklab, oklch(0.723 0.192 149.58) 16%, transparent)"
58
+ success-wash-hover: "color-mix(in oklab, oklch(0.723 0.192 149.58) 20%, transparent)"
59
+ success-wash-active: "color-mix(in oklab, oklch(0.723 0.192 149.58) 24%, transparent)"
60
+ status-success: "oklch(0.723 0.192 149.58)"
61
+ status-loading: "oklch(0.715 0 0)"
62
+ status-warning: "oklch(0.7686 0.1647 70.08)"
63
+ status-info: "oklch(0.623 0.188 259.81)"
64
+ skeleton: "color-mix(in oklab, oklch(0.205 0 0) 8%, transparent)"
65
+ skeleton-dark: "color-mix(in oklab, oklch(0.97 0 0) 8%, transparent)"
66
+ image-edge: "rgb(0 0 0 / 0.1)"
67
+ image-edge-dark: "rgb(255 255 255 / 0.1)"
68
+ progress-track: "color-mix(in oklab, oklch(0.205 0 0) 10%, transparent)"
69
+ progress-track-dark: "color-mix(in oklab, oklch(0.97 0 0) 10%, transparent)"
70
+ surface-1: "oklch(0.985 0 0)"
71
+ surface-2: "oklch(0.991 0 0)"
72
+ surface-3: "oklch(1 0 0)"
73
+ surface-4: "oklch(1 0 0)"
74
+ surface-5: "oklch(1 0 0)"
75
+ surface-6: "oklch(1 0 0)"
76
+ surface-7: "oklch(1 0 0)"
77
+ surface-8: "oklch(1 0 0)"
78
+ surface-1-dark: "oklch(0.205 0 0)"
79
+ surface-2-dark: "oklch(0.235 0 0)"
80
+ surface-3-dark: "oklch(0.264 0 0)"
81
+ surface-4-dark: "oklch(0.293 0 0)"
82
+ surface-5-dark: "oklch(0.321 0 0)"
83
+ surface-6-dark: "oklch(0.349 0 0)"
84
+ surface-7-dark: "oklch(0.375 0 0)"
85
+ surface-8-dark: "oklch(0.402 0 0)"
86
+ typography:
87
+ display:
88
+ fontFamily: Inter Variable
89
+ fontSize: 28px
90
+ fontWeight: 700
91
+ lineHeight: 34px
92
+ fontVariation: "'wght' 700, 'opsz' 25"
93
+ title:
94
+ fontFamily: Inter Variable
95
+ fontSize: 16px
96
+ fontWeight: 550
97
+ lineHeight: 22px
98
+ fontVariation: "'wght' 550, 'opsz' 18"
99
+ subtitle:
100
+ fontFamily: Inter Variable
101
+ fontSize: 14px
102
+ fontWeight: 550
103
+ lineHeight: 20px
104
+ fontVariation: "'wght' 550, 'opsz' 18"
105
+ body:
106
+ fontFamily: Inter Variable
107
+ fontSize: 13px
108
+ fontWeight: 400
109
+ lineHeight: 20px
110
+ fontVariation: "'wght' 400, 'opsz' 14"
111
+ body-selected:
112
+ fontFamily: Inter Variable
113
+ fontSize: 13px
114
+ fontWeight: 550
115
+ lineHeight: 20px
116
+ fontVariation: "'wght' 550, 'opsz' 18"
117
+ caption:
118
+ fontFamily: Inter Variable
119
+ fontSize: 12px
120
+ fontWeight: 400
121
+ lineHeight: 16px
122
+ fontVariation: "'wght' 400, 'opsz' 14"
123
+ caption-medium:
124
+ fontFamily: Inter Variable
125
+ fontSize: 12px
126
+ fontWeight: 450
127
+ lineHeight: 16px
128
+ fontVariation: "'wght' 450, 'opsz' 15"
129
+ micro:
130
+ fontFamily: Inter Variable
131
+ fontSize: 11px
132
+ fontWeight: 400
133
+ lineHeight: 14px
134
+ fontVariation: "'wght' 400, 'opsz' 14"
135
+ display-compact:
136
+ fontFamily: Inter Variable
137
+ fontSize: 24px
138
+ fontWeight: 700
139
+ lineHeight: 34px
140
+ fontVariation: "'wght' 700, 'opsz' 25"
141
+ title-compact:
142
+ fontFamily: Inter Variable
143
+ fontSize: 15px
144
+ fontWeight: 550
145
+ lineHeight: 22px
146
+ fontVariation: "'wght' 550, 'opsz' 18"
147
+ subtitle-compact:
148
+ fontFamily: Inter Variable
149
+ fontSize: 13px
150
+ fontWeight: 550
151
+ lineHeight: 20px
152
+ fontVariation: "'wght' 550, 'opsz' 18"
153
+ body-compact:
154
+ fontFamily: Inter Variable
155
+ fontSize: 12px
156
+ fontWeight: 400
157
+ lineHeight: 20px
158
+ fontVariation: "'wght' 400, 'opsz' 14"
159
+ caption-compact:
160
+ fontFamily: Inter Variable
161
+ fontSize: 11px
162
+ fontWeight: 400
163
+ lineHeight: 16px
164
+ fontVariation: "'wght' 400, 'opsz' 14"
165
+ micro-compact:
166
+ fontFamily: Inter Variable
167
+ fontSize: 10px
168
+ fontWeight: 400
169
+ lineHeight: 14px
170
+ fontVariation: "'wght' 400, 'opsz' 14"
171
+ code:
172
+ fontFamily: ui-monospace
173
+ fontSize: 12px
174
+ fontWeight: 400
175
+ lineHeight: 16px
176
+ rounded:
177
+ glyph: 3px
178
+ box: 5px
179
+ md: 6px
180
+ lg: 8px
181
+ focus-ring: 10px
182
+ xl: 12px
183
+ 2xl: 16px
184
+ pill: 20px
185
+ pill-focus-ring: 22px
186
+ 3xl: 24px
187
+ full: 9999px
188
+ spacing:
189
+ space-1: 4px
190
+ space-2: 8px
191
+ space-3: 12px
192
+ space-4: 16px
193
+ space-5: 24px
194
+ space-6: 32px
195
+ control: 36px
196
+ control-compact: 28px
197
+ segment-item: 28px
198
+ segment-item-compact: 24px
199
+ control-px: 12px
200
+ control-px-compact: 10px
201
+ row-px: 8px
202
+ row-px-compact: 6px
203
+ gap: 8px
204
+ gap-compact: 4px
205
+ icon: 16px
206
+ icon-compact: 14px
207
+ goo-neck: 12px
208
+ measure: 45rem
209
+ motion:
210
+ fast: 80ms
211
+ fast-exit: 60ms
212
+ moderate: 160ms
213
+ moderate-exit: 120ms
214
+ slow: 240ms
215
+ slow-exit: 160ms
216
+ goo: 250ms
217
+ goo-exit: 200ms
218
+ loop: 2s
219
+ components:
220
+ button-primary:
221
+ backgroundColor: "{colors.foreground}"
222
+ textColor: "{colors.background}"
223
+ typography: "{typography.body}"
224
+ rounded: "{rounded.lg}"
225
+ height: "{spacing.control}"
226
+ padding: 0 16px
227
+ button-primary-hover:
228
+ backgroundColor: "color-mix(in srgb, oklch(0.205 0 0) 90%, oklch(0.985 0 0))"
229
+ button-primary-active:
230
+ backgroundColor: "color-mix(in srgb, oklch(0.205 0 0) 80%, oklch(0.985 0 0))"
231
+ button-secondary:
232
+ backgroundColor: "{colors.accent}"
233
+ textColor: "{colors.foreground}"
234
+ typography: "{typography.body}"
235
+ rounded: "{rounded.lg}"
236
+ height: "{spacing.control}"
237
+ padding: 0 16px
238
+ button-secondary-hover:
239
+ backgroundColor: "color-mix(in srgb, oklch(0.922 0 0) 80%, oklch(0.985 0 0))"
240
+ button-tertiary:
241
+ textColor: "{colors.foreground}"
242
+ rounded: "{rounded.lg}"
243
+ height: "{spacing.control}"
244
+ padding: 0 16px
245
+ button-tertiary-hover:
246
+ backgroundColor: "{colors.hover}"
247
+ button-ghost:
248
+ textColor: "{colors.muted-foreground}"
249
+ rounded: "{rounded.lg}"
250
+ height: "{spacing.control}"
251
+ button-ghost-hover:
252
+ backgroundColor: "{colors.hover}"
253
+ button-compact:
254
+ typography: "{typography.body-compact}"
255
+ height: "{spacing.control-compact}"
256
+ padding: 0 12px
257
+ input-field:
258
+ textColor: "{colors.foreground}"
259
+ typography: "{typography.body}"
260
+ rounded: "{rounded.lg}"
261
+ height: "{spacing.control}"
262
+ padding: 0 10px
263
+ input-field-focus:
264
+ backgroundColor: "{colors.card}"
265
+ menu-popup:
266
+ backgroundColor: "{colors.surface-3}"
267
+ rounded: "{rounded.xl}"
268
+ menu-row:
269
+ textColor: "{colors.foreground}"
270
+ typography: "{typography.body}"
271
+ rounded: "{rounded.lg}"
272
+ height: "{spacing.control}"
273
+ padding: 0 8px
274
+ hover-highlight:
275
+ backgroundColor: "{colors.hover}"
276
+ rounded: "{rounded.lg}"
277
+ popover:
278
+ backgroundColor: "{colors.surface-3}"
279
+ rounded: "{rounded.xl}"
280
+ padding: 16px
281
+ dialog:
282
+ backgroundColor: "{colors.surface-5}"
283
+ rounded: "{rounded.xl}"
284
+ padding: 24px
285
+ width: 400px
286
+ tabs-list:
287
+ backgroundColor: "{colors.muted}"
288
+ rounded: "{rounded.xl}"
289
+ height: "{spacing.control}"
290
+ padding: 4px
291
+ tabs-indicator:
292
+ backgroundColor: "{colors.surface-4}"
293
+ rounded: "{rounded.lg}"
294
+ height: "{spacing.segment-item}"
295
+ tooltip:
296
+ backgroundColor: "{colors.foreground}"
297
+ textColor: "{colors.background}"
298
+ typography: "{typography.caption-medium}"
299
+ rounded: "{rounded.lg}"
300
+ padding: 4px 8px
301
+ badge:
302
+ typography: "{typography.caption-medium}"
303
+ rounded: "{rounded.lg}"
304
+ height: 24px
305
+ padding: 0 10px
306
+ switch-track-on:
307
+ backgroundColor: "{colors.brand}"
308
+ switch-thumb:
309
+ backgroundColor: "{colors.thumb}"
310
+ rounded: "{rounded.full}"
311
+ code-block:
312
+ backgroundColor: "{colors.surface-2}"
313
+ textColor: "{colors.foreground}"
314
+ typography: "{typography.code}"
315
+ rounded: "{rounded.xl}"
316
+ padding: 16px
317
+ ---
318
+
319
+ # UI — Fluid Functionalism for modo
320
+
321
+ The `ui` design system: Fluid Functionalism (Base UI flavor, MIT © 2026 Micka Touillaud) on UnoCSS and
322
+ `cn`, plus a local morph layer that makes overlays and indicators liquid. The front matter holds the
323
+ normative values; the CSS custom properties in `tokens/*.css` and `primitives/surface/surface.css` are
324
+ the source they are copied from, and they win if the two ever disagree. Component code never writes
325
+ these values: it reads the utility that maps to each token (the class in backticks below).
326
+
327
+ ## Overview
328
+
329
+ Functional first, then fluid. The interface is neutral, compact and quiet at rest, and almost every
330
+ pixel of personality is spent on *how things change*: a state change is always legible, never abrupt,
331
+ and never decorative.
332
+
333
+ - **Neutral, ink on paper.** Greyscale everywhere. The one chromatic accent, a cool blue (`brand`), is
334
+ reserved for focus, "on" (switch track), drop targets and the selected swatch. Status hues appear only
335
+ in toasts, badges and errors.
336
+ - **Dense, desktop-grade.** 13px body text, 36px controls, a 28px compact step for toolbars, tables and
337
+ sidebars. Density is chosen per region (`SizeProvider`), never per control.
338
+ - **Hierarchy from size, weight and elevation**, not from color, borders, capitals or letter-spacing.
339
+ - **Motion is the signature.** Springs, not durations. One hover highlight per list that glides to the
340
+ nearest row. Selection backgrounds that melt from item to item like a drop of liquid. Overlays that
341
+ grow out of the control that opened them through a goo neck, then let go.
342
+ - **Calm under reduced motion.** Every effect degrades to a fade: fewer and gentler, never none.
343
+
344
+ The feeling to aim for: a precise instrument that happens to be made of liquid. Nothing bounces for
345
+ fun; the only overshoot in the system is the slow tier's 0.12 and the goo's 0.15.
346
+
347
+ ## Colors
348
+
349
+ Two text colors, a greyscale ground, one blue.
350
+
351
+ - **Foreground (`text-foreground`)** — near-black ink in light, near-white in dark. Headings, labels,
352
+ values, selected items, the primary button's fill.
353
+ - **Muted foreground (`text-muted-foreground`)** — the only secondary text color: descriptions, captions,
354
+ placeholders, idle icons, idle tab labels. Never an opacity step of foreground (`text-foreground/60`
355
+ is wrong); a third text tone does not exist.
356
+ - **Background (`bg-background`)** — the page, equal to surface 1. Also the text color on the primary
357
+ button and the tooltip (`text-background`).
358
+ - **Surfaces (`bg-surface-1` … `bg-surface-8`)** — the elevation ladder (see Elevation & Depth). Prefer a
359
+ surface level over `card`/`background` for anything that floats or nests.
360
+ - **Muted (`bg-muted`)** — a recessed track: the segmented tabs' well, a sunken field at rest.
361
+ - **Accent (`bg-accent`)** — the secondary button and the switch's off track. `selected` is a firmer
362
+ grey for persistent selection fills.
363
+ - **Hover / Active (`bg-hover`, `bg-active`)** — translucent overlays (black 4% / 7% in light, white 6% /
364
+ 10% in dark) so they read on any surface level. Every hover and press fill uses them; never an ad-hoc
365
+ grey. The raw tint direction is `--overlay` (`bg-overlay/8` → `rgb(var(--overlay) / 0.08)`).
366
+ - **Border (`border-border`, `ring-border`)** — 12% of foreground, so it tracks the scheme and nested
367
+ forced scopes. Hairlines only, 1px.
368
+ - **Brand (`bg-brand`, `ring-focus-ring`)** — `focus-ring` is `brand`: every keyboard ring is a 1px blue
369
+ ring 2px outside the element. The single theming point for focus.
370
+ - **Destructive (`text-destructive`, `bg-destructive-light`)** — errors and destructive confirms only.
371
+ - **Tinted actions (`text-destructive-strong` on `bg-destructive-wash`, `text-success-strong` on
372
+ `bg-success-wash`)** — the destructive and success buttons: the hue as text, dark enough for AA on its
373
+ wash, over a translucent wash of it that deepens on hover and press (`-hover`, `-active`). Never a
374
+ solid fill.
375
+ - **Skeleton (`bg-skeleton`)** — 8% of foreground: the loading placeholder, quieter than any border.
376
+ - **Image edge (`outline-image-edge`)** — a 1px inset edge (black or white at 10%) on images and
377
+ avatars, so a picture keeps its outline on any substrate.
378
+ - **Progress track (`bg-progress-track`)** — 10% of foreground under the ink (`bg-foreground`) fill of
379
+ a progress bar.
380
+ - **Status (`text-status-*`, `bg-status-*/16`)** — success, loading, error, warning, info, action. A
381
+ status shows as tinted text on a 16% wash of itself, never as a solid fill behind text.
382
+ - **Scrim (`bg-scrim`)** — behind a modal dialog; it fades with the morph's progress.
383
+ - **Syntax (`text-syntax-*`)** — low-chroma hues for code, so code reads as text first.
384
+
385
+ Every color is one `light-dark(light, dark)` token; the scheme comes from `color-scheme` on `<html>`
386
+ (`.light` / `.dark` pin it, also on any subtree). Shadows cannot ride `light-dark()`, so `--shadow-N` is
387
+ re-pointed per scheme in `surface.css`.
388
+
389
+ ## Typography
390
+
391
+ **Inter Variable** with its optical-size axis (`@fontsource-variable/inter/opsz.css`). The opsz axis is
392
+ not optional: every weight is paired with an optical size so a label keeps its width when it gets
393
+ heavier.
394
+
395
+ Six roles, each a size and a whole-pixel line height, as utilities `text-<role>` (and
396
+ `text-<role>-compact` for the compact step, which keeps the role's leading):
397
+
398
+ | Role | Default | Compact | Use |
399
+ |---|---|---|---|
400
+ | `text-display` | 28/34 | 24 | The page title. The only bold text. |
401
+ | `text-title` | 16/22 | 15 | Section headings, dialog titles. |
402
+ | `text-subtitle` | 14/20 | 13 | Card and popover titles, emphasized rows. |
403
+ | `text-body` | 13/20 | 12 | Control labels, rows, body copy. The default. |
404
+ | `text-caption` | 12/16 | 11 | Descriptions, meta, errors, group labels, tooltips, badges. |
405
+ | `text-micro` | 11/14 | 10 | Keycaps, counters, tiny chips. Single line only. |
406
+
407
+ Body's 20px leading is what puts a padded row on the ladder: 8 + 20 + 8 = 36px.
408
+
409
+ Weights come from `font-variation-settings`, through the `weight-*` utilities, never `font-*`:
410
+
411
+ - `weight-normal` (400, opsz 14) — all running text and idle labels.
412
+ - `weight-medium` (450, opsz 15) — small labels that need to hold on a fill: tooltips, badges, errors.
413
+ - `weight-semibold` (550, opsz 18) — headings and anything selected, active or open.
414
+ - `weight-bold` (700, opsz 25) — the display role only.
415
+
416
+ A label that changes weight with state (a tab, a selected card title, an open accordion row) renders
417
+ twice in one grid cell: an invisible `weight-semibold` copy reserves the width, the visible copy tweens
418
+ `transition-[color,font-variation-settings] duration-fast`. Neighbours never move.
419
+
420
+ No uppercase, no letter-spacing, no eyebrows above titles in new UI (`Card.Eyebrow` is the one vendored
421
+ exception). Sentence case everywhere; the docs chrome capitalizes in content, not with CSS. Fixed-height
422
+ labels trim their box (`[text-box:trim-both_cap_alphabetic]`) so the cap height sits optically centered.
423
+ `h1–h3` balance, `p` wraps pretty. Code is `font-mono` at caption size.
424
+
425
+ ## Layout
426
+
427
+ A 4px grid. Spacing tokens `--space-1…6` = 4, 8, 12, 16, 24, 32px (the docs chrome reads them);
428
+ component code uses the matching utilities (`gap-1`, `p-4`, …), which sit on the same 4px grid.
429
+
430
+ **The size ladder** is the layout system. Two steps only, and everything steps down together:
431
+
432
+ | Token (`useSize()`) | Default | Compact |
433
+ |---|---|---|
434
+ | `control` (buttons, inputs, select triggers, **and** list/menu rows) | 36px `h-9` | 28px `h-7` |
435
+ | `segmentItem` + `segmentPad` (segmented tabs) | 28 + 4 | 24 + 2 |
436
+ | `text` | `text-body` | `text-body-compact` |
437
+ | `px` (bounded controls) | 12px | 10px |
438
+ | `itemPx` (rows inside a padded popup) | 8px | 6px |
439
+ | `gap` (icon ↔ label, control ↔ control) | 8px | 4px |
440
+ | `icon` (glyphs, checkbox squares) | 16px | 14px |
441
+
442
+ A popup row is exactly as tall as the trigger that opened it. Density is a region decision: wrap the
443
+ region in one `SizeProvider size="compact"` and every control in it (menus it opens included) follows.
444
+ A component's own `size` prop wins over the provider.
445
+
446
+ Other fixed measures: popovers cap at `min(92vw, 20rem)`; dialogs are 400 / 540 / 880px wide (`sm`,
447
+ `lg`, `xl`), one notch narrower in compact regions; popup lists cap at `min(300px,
448
+ var(--available-height))` and scroll inside `ScrollArea` with a scroll-aware `scroll-fade`; reading
449
+ columns cap at `--modo-measure` (45rem). Native scrollbars are thin and low-contrast (overlay 8% → 12% on
450
+ hover → 16% while dragging), matching the `ScrollArea` thumb.
451
+
452
+ ## Elevation & Depth
453
+
454
+ Eight surface levels that nest. Each `bg-surface-N` pairs 1:1 with a `shadow-surface-N`.
455
+
456
+ - **Light:** only two color steps (floor 0.985, sunken 0.991), then flat white from surface 3 up.
457
+ Elevation is carried by shadow alone: a 1px `--shadow-color` hairline (black 6%) plus stacked drops
458
+ whose offsets double per level (1, 3, 6, 12, 24, 48, 96px).
459
+ - **Dark:** an additive ladder from 0.205 to 0.402 in even steps, each with an inset top highlight, an
460
+ inset ring and black drops.
461
+
462
+ Substrate flows through React context (`useSurface()`, 1 = the page). A floating surface computes
463
+ `min(substrate + offset, 8)`, paints that level and re-provides it, so a popover inside a dialog lands
464
+ above the dialog without any prop:
465
+
466
+ | Surface | Offset | Shadow |
467
+ |---|---|---|
468
+ | Code block | +1 | none |
469
+ | Popover, dropdown, select, combobox, context menu, preview card, toast, search suggestions | +2 | fixed `shadow-surface-3` |
470
+ | Tabs' active indicator | +3 | matches level |
471
+ | Dialog, alert dialog, sheet | +4 | matches level |
472
+ | Tooltip | inverted: `bg-foreground`, no shadow | — |
473
+
474
+ Menus keep a fixed `shadow-surface-3` whatever their depth, so a menu still reads as a menu three layers
475
+ down. Hover and pressed states are overlays (`bg-hover`, `bg-active`), not levels, so they work at any
476
+ elevation. Use `Elevated offset={n}` (or `surfaceClasses(level)` / `SURFACE_BG[level]`) for anything new
477
+ that floats; never hard-code a surface.
478
+
479
+ ## Shapes
480
+
481
+ Rounded, concentric, never mixed. Two shape modes from `useShape()`; **rounded** is the default and what
482
+ everything renders without a provider:
483
+
484
+ | Slot | Rounded | Pill |
485
+ |---|---|---|
486
+ | `item`, `bg`, `button`, `input` | 8px `rounded-lg` | 20px |
487
+ | `focusRing` (2px outside the element) | 10px | 22px |
488
+ | `container` (popups, cards, dialogs, segmented tracks) | 12px `rounded-xl` | 24px |
489
+ | `mergedBg` (merged selection runs) | 8px | 16px |
490
+
491
+ A rounded surface nested close inside another uses concentric corners: inner = outer − inset − border.
492
+ The shipped pair (12 → 8) assumes a 4px inset. Small boxes use `rounded-box` (5px: checkbox squares,
493
+ keycaps) and `rounded-glyph` (3px). Thumbs, dots and the loading button are circles (`rounded-full`).
494
+ Never write a radius: read `shape.*` so the pill mode follows.
495
+
496
+ ## Motion
497
+
498
+ Springs, tiered by the size of what moves. The bigger the thing, the slower the spring. Never a
499
+ hand-written duration; never a fourth speed.
500
+
501
+ | Tier | Enter | Exit (tween, one tier quicker) | Use |
502
+ |---|---|---|---|
503
+ | `spring.fast` | 80ms spring, bounce 0 | 60ms | Hover highlight, focus ring, fades, icon stroke, weight changes |
504
+ | `spring.moderate` | 160ms spring, bounce 0 | 120ms | Indicators, thumbs, small panels, width changes, the loading fold |
505
+ | `spring.slow` | 240ms spring, bounce 0.12 | 160ms | Large surfaces: plain morphs, the search field's growth, shared parts |
506
+ | `spring.goo` | 250ms visual, bounce 0.15 | 200ms spring back | Every goo morph, so the liquid neck is on screen long enough to read |
507
+
508
+ CSS consumers use `duration-fast|moderate|slow[-exit]` and `delay-*`. `--duration-loop` (2s) is the
509
+ period of pending-state loops (the indeterminate progress sweep, the spinner, the skeleton pulse); it is
510
+ not a spring tier, and under reduced motion a loop pulses opacity in place. Animate `transform` and `opacity`;
511
+ a measured geometry engine (the morph layer, `GooIndicator`) is the only thing that writes boxes.
512
+
513
+ The signature behaviors:
514
+
515
+ - **Fluid hover.** One `bg-hover` highlight per list, on the item nearest the cursor (inside wins, else
516
+ nearest center), so it never blinks off in a gap. It travels on `spring.fast` and melts between rows.
517
+ A fresh pointer entry fades in where it is instead of sliding across. A click in a gap goes to the lit
518
+ row: what is lit is what a click hits.
519
+ - **Liquid indicators.** Anything that travels between items (the active tab, a switch thumb, selection
520
+ runs, the hover pill) leaves a blob behind that lingers a third of the spring and follows, melted
521
+ through a goo filter. At rest nothing is filtered.
522
+ - **Morphing overlays.** Popover, dropdown, select, combobox, context menu, tooltip, preview card,
523
+ dialog, sheet, command, search suggestions and toasts grow from their source (`from`: the trigger,
524
+ the press point, the center, a window edge, or any element) with an `effect`: `goo` (default, a
525
+ liquid neck that pinches off), `morph` (the box grows), `slide` or `fade`. They close back into where
526
+ they came from. With `hideSource` the trigger *becomes* the panel.
527
+ - **Press geometry.** A pressed button's fill shrinks exactly 1px per side at any width (spread shadow
528
+ collapse, not a scale), releasing slower than it presses.
529
+ - **Icons thicken, labels don't move.** Hovered/active icons step stroke 1.5 → 2 on the fast tier.
530
+ - **Loading folds.** A loading button folds its surface into a circle around the spinner while its box
531
+ stays put.
532
+ - **Exits are crisp.** A dismissal never replays the entrance backwards.
533
+ - **Reduced motion.** Every morph becomes a fade; travel snaps; opacity and color fades stay.
534
+
535
+ ## Components
536
+
537
+ Each item lives at `<tier>/[<group>/]<name>/index.tsx` and composes from Base UI (behavior) plus the
538
+ shared systems above (look and motion). Ready-made patterns to reach for before writing anything:
539
+
540
+ - **Buttons** — `primary` (ink fill), `secondary` (accent fill), `tertiary` (transparent, a 1px
541
+ `border` ring that moves inward on press), `ghost` (transparent, muted text that turns foreground over
542
+ the hover overlay). A component token with no `backgroundColor` is transparent over its substrate.
543
+ Sizes `default` 36 / `compact` 28 / `icon` / `icon-compact`. Icons sit 4px closer to their edge than
544
+ text. `active` holds the pressed look while a menu it opened is showing. One primary per view.
545
+ `outline` is `tertiary` under its shadcn name. `destructive` and `success` are their hue as text on a
546
+ wash of it (Tinted actions), never a solid fill; `link` is underlined foreground text on the control
547
+ height, no padding. `xs` and `sm` resolve to `compact`: the ladder has two steps.
548
+ - **Inputs** — transparent and ringless at rest, a `bg-muted/50` wash plus a `ring-border` ring on
549
+ hover, `bg-card` plus the ring on focus; read-only keeps the ring but never lifts the fill. The label
550
+ sits above in muted body text (an asterisk when required), help text below in muted caption, both
551
+ inset to the text. Errors: destructive caption at `weight-medium`, a `destructive-light` wash.
552
+ Adornments are muted and light with the field; field actions are 4px inside the ring. File
553
+ dropzones: a dashed hairline on the container radius, a muted fill while files hover over it,
554
+ destructive for files it won't take; a drag it can partly take keeps the neutral fill and says that
555
+ some will be skipped.
556
+ - **Lists and menus** — rows on the control height, `itemPx` inset, one fluid hover highlight, rows
557
+ without their own `:hover` fill. Disabled rows stay registered but are skipped. Keyboard focus earns a
558
+ 1px brand ring that springs between rows; pointer focus never shows it.
559
+ - **Selection** — checkbox and radio rows melt their selected backgrounds into one run; tabs and
560
+ subtle tabs carry an elevated (+3) or translucent indicator that melts between items; the selected
561
+ label goes `weight-semibold`. Checkbox, radio and switch also come bare (no label): just the box,
562
+ circle or track, with a 1px focus ring offset 2px, for tables and hand-laid forms. An indeterminate
563
+ checkbox shows a dash that blurs into the check. A loading control keeps its box: the combobox
564
+ chevron blurs into a spinner in the same slot.
565
+ - **Overlays** — popup surfaces at +2 with `shadow-surface-3`, dialogs at +4, `rounded-xl` containers,
566
+ 12px goo neck between trigger and panel, content padding 16px (popover) or 24px (dialog). A confirm
567
+ that runs a task folds its button into the loading circle and holds the dialog until it settles.
568
+ - **Tooltips** — inverted (`bg-foreground` / `text-background`), caption at `weight-medium`, morph from
569
+ the trigger, a hand-off between grouped tooltips is instant; the same label whether written flat or
570
+ as Trigger + Content parts.
571
+ - **Badges** — 24px (20px compact), caption medium, a 15% wash of the color into the background with
572
+ foreground text; dot variant keeps a neutral outline; `outline` is that frame without the dot, and
573
+ no color.
574
+ - **Toasts** — a status pill that melts into a body; status color as text on a 16% wash. Shown from
575
+ anywhere through the global `toast()`; a custom icon sits in the same status disc.
576
+ - **Cards** — transparent and borderless, inheriting the substrate; hairline dividers and the fluid
577
+ hover do the framing; only clickable cards join the hover. `border="outlined"` gives a standalone
578
+ card the outlined group's hairline frame; `Card.Divider` is a full-bleed hairline between its parts.
579
+ - **Pagination** — the current page is a liquid `bg-accent` indicator on the fluid-hover strip; dense
580
+ tables use the jump-to-page field.
581
+ - **Avatar** — one control tall (36px, 28px compact), a circle, or a square on the item radius,
582
+ with the image edge; initials or an icon on `bg-accent` until the image loads, which then fades in.
583
+ - **Progress** — a 4px pill: the ink fill slides to its value on `spring.moderate`; with no value a
584
+ segment sweeps the track on the loop period. The spinner is the loading button's figure-eight.
585
+ - **Skeleton** — `bg-skeleton` blocks on the item radius pulsing in opacity, rows on the control
586
+ height; hidden from assistive tech (mark the loading region busy instead).
587
+ - **Separator** — the one 1px `border` hairline, horizontal or vertical; no ornament.
588
+
589
+ ## Do's and Don'ts
590
+
591
+ - Do compose from an existing item before writing a new one; wrap rather than fork vendored code.
592
+ - Do read every visual from a token utility: `text-<role>`, `weight-*`, `bg-surface-N`,
593
+ `shadow-surface-N`, `bg-hover`, `shape.*`, `duration-<tier>`, `spring.*`.
594
+ - Do lift floating surfaces relative to the substrate (`useSurface()` + offset) and re-provide the level.
595
+ - Do size controls from `useSize()` and let regions choose density with `SizeProvider`.
596
+ - Do give every list exactly one fluid hover highlight, and every traveling indicator a `GooIndicator`.
597
+ - Do grow every new overlay out of its source through `useMorph` + `MorphSurface`.
598
+ - Do test light, dark, compact, keyboard and reduced motion.
599
+ - Don't write an arbitrary value for color, type, radius, shadow or motion (`text-[13px]`, `bg-[#…]`,
600
+ `rounded-[10px]`, `duration-[200ms]`); add the token instead. Layout geometry may be arbitrary.
601
+ - Don't use a third text color, an opacity step of a text color, uppercase, letter-spacing, or `font-*`
602
+ weights.
603
+ - Don't add a per-row `:hover` background next to a fluid hover list.
604
+ - Don't hand-write a `duration`, an easing curve or a fourth spring; don't animate `top`/`left`/`width`/
605
+ `height` outside the morph engine.
606
+ - Don't use the brand blue for anything but focus, "on", drop targets and selected swatches.
607
+ - Don't hard-code a surface background or a radius: a menu inside a dialog must still lift.
608
+ - Don't stack two measured-height animations: the outer one snaps when a child changed its size.