@remix-run/ui 0.2.0 → 0.4.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 (395) hide show
  1. package/README.md +50 -164
  2. package/dist/accordion/index.d.ts +46 -0
  3. package/dist/accordion/index.js +142 -0
  4. package/dist/accordion/index.js.map +1 -0
  5. package/dist/accordion/primitives.d.ts +104 -0
  6. package/dist/accordion/primitives.js +312 -0
  7. package/dist/accordion/primitives.js.map +1 -0
  8. package/dist/{components/anchor/anchor.d.ts → anchor/index.d.ts} +8 -1
  9. package/dist/{components/anchor/anchor.js → anchor/index.js} +31 -12
  10. package/dist/anchor/index.js.map +1 -0
  11. package/dist/animation/demos/aspect-ratio.d.ts +2 -0
  12. package/dist/animation/demos/aspect-ratio.js +69 -0
  13. package/dist/animation/demos/aspect-ratio.js.map +1 -0
  14. package/dist/animation/demos/bouncy-switch.d.ts +2 -0
  15. package/dist/animation/demos/bouncy-switch.js +36 -0
  16. package/dist/animation/demos/bouncy-switch.js.map +1 -0
  17. package/dist/animation/demos/color-interpolation.d.ts +1 -0
  18. package/dist/animation/demos/color-interpolation.js +53 -0
  19. package/dist/animation/demos/color-interpolation.js.map +1 -0
  20. package/dist/animation/demos/cube.d.ts +2 -0
  21. package/dist/animation/demos/cube.js +86 -0
  22. package/dist/animation/demos/cube.js.map +1 -0
  23. package/dist/animation/demos/default-animate.d.ts +2 -0
  24. package/dist/animation/demos/default-animate.js +89 -0
  25. package/dist/animation/demos/default-animate.js.map +1 -0
  26. package/dist/animation/demos/drag-release.d.ts +24 -0
  27. package/dist/animation/demos/drag-release.js +96 -0
  28. package/dist/animation/demos/drag-release.js.map +1 -0
  29. package/dist/animation/demos/enter.d.ts +1 -0
  30. package/dist/animation/demos/enter.js +19 -0
  31. package/dist/animation/demos/enter.js.map +1 -0
  32. package/dist/animation/demos/exit.d.ts +2 -0
  33. package/dist/animation/demos/exit.js +58 -0
  34. package/dist/animation/demos/exit.js.map +1 -0
  35. package/dist/animation/demos/flip-toggle.d.ts +2 -0
  36. package/dist/animation/demos/flip-toggle.js +37 -0
  37. package/dist/animation/demos/flip-toggle.js.map +1 -0
  38. package/dist/animation/demos/hold-to-confirm.d.ts +13 -0
  39. package/dist/animation/demos/hold-to-confirm.js +211 -0
  40. package/dist/animation/demos/hold-to-confirm.js.map +1 -0
  41. package/dist/animation/demos/html-content.d.ts +2 -0
  42. package/dist/animation/demos/html-content.js +30 -0
  43. package/dist/animation/demos/html-content.js.map +1 -0
  44. package/dist/animation/demos/interruptible-keyframes.d.ts +1 -0
  45. package/dist/animation/demos/interruptible-keyframes.js +48 -0
  46. package/dist/animation/demos/interruptible-keyframes.js.map +1 -0
  47. package/dist/animation/demos/keyframes.d.ts +1 -0
  48. package/dist/animation/demos/keyframes.js +24 -0
  49. package/dist/animation/demos/keyframes.js.map +1 -0
  50. package/dist/animation/demos/material-ripple.d.ts +2 -0
  51. package/dist/animation/demos/material-ripple.js +127 -0
  52. package/dist/animation/demos/material-ripple.js.map +1 -0
  53. package/dist/animation/demos/mixin-presence-list.d.ts +2 -0
  54. package/dist/animation/demos/mixin-presence-list.js +88 -0
  55. package/dist/animation/demos/mixin-presence-list.js.map +1 -0
  56. package/dist/animation/demos/mixin-reclaim.d.ts +2 -0
  57. package/dist/animation/demos/mixin-reclaim.js +90 -0
  58. package/dist/animation/demos/mixin-reclaim.js.map +1 -0
  59. package/dist/animation/demos/multi-state-badge.d.ts +2 -0
  60. package/dist/animation/demos/multi-state-badge.js +231 -0
  61. package/dist/animation/demos/multi-state-badge.js.map +1 -0
  62. package/dist/animation/demos/press.d.ts +2 -0
  63. package/dist/animation/demos/press.js +63 -0
  64. package/dist/animation/demos/press.js.map +1 -0
  65. package/dist/animation/demos/reordering.d.ts +2 -0
  66. package/dist/animation/demos/reordering.js +53 -0
  67. package/dist/animation/demos/reordering.js.map +1 -0
  68. package/dist/animation/demos/rolling-square.d.ts +2 -0
  69. package/dist/animation/demos/rolling-square.js +42 -0
  70. package/dist/animation/demos/rolling-square.js.map +1 -0
  71. package/dist/animation/demos/rotate.d.ts +1 -0
  72. package/dist/animation/demos/rotate.js +18 -0
  73. package/dist/animation/demos/rotate.js.map +1 -0
  74. package/dist/animation/demos/shared-layout.d.ts +2 -0
  75. package/dist/animation/demos/shared-layout.js +114 -0
  76. package/dist/animation/demos/shared-layout.js.map +1 -0
  77. package/dist/animation/demos/transition-options.d.ts +1 -0
  78. package/dist/animation/demos/transition-options.js +21 -0
  79. package/dist/animation/demos/transition-options.js.map +1 -0
  80. package/dist/animation/spring.d.ts +22 -0
  81. package/dist/animation/spring.js +22 -0
  82. package/dist/animation/spring.js.map +1 -1
  83. package/dist/{components/breadcrumbs/breadcrumbs.js → breadcrumbs/index.js} +19 -19
  84. package/dist/breadcrumbs/index.js.map +1 -0
  85. package/dist/button/index.d.ts +15 -0
  86. package/dist/button/index.js +128 -0
  87. package/dist/button/index.js.map +1 -0
  88. package/dist/checkbox/index.d.ts +14 -0
  89. package/dist/checkbox/index.js +125 -0
  90. package/dist/checkbox/index.js.map +1 -0
  91. package/dist/combobox/index.d.ts +21 -0
  92. package/dist/combobox/index.js +58 -0
  93. package/dist/combobox/index.js.map +1 -0
  94. package/dist/{components/combobox/combobox.d.ts → combobox/primitives.d.ts} +3 -7
  95. package/dist/{components/combobox/combobox.js → combobox/primitives.js} +8 -61
  96. package/dist/combobox/primitives.js.map +1 -0
  97. package/dist/input/index.d.ts +13 -0
  98. package/dist/input/index.js +121 -0
  99. package/dist/input/index.js.map +1 -0
  100. package/dist/listbox/flash-attribute.js.map +1 -0
  101. package/dist/{components/listbox/listbox.d.ts → listbox/index.d.ts} +2 -6
  102. package/dist/{components/listbox/listbox.js → listbox/index.js} +4 -82
  103. package/dist/listbox/index.js.map +1 -0
  104. package/dist/menu/hover-aim.js.map +1 -0
  105. package/dist/menu/index.d.ts +42 -0
  106. package/dist/menu/index.js +250 -0
  107. package/dist/menu/index.js.map +1 -0
  108. package/dist/{components/menu/menu.d.ts → menu/primitives.d.ts} +6 -32
  109. package/dist/{components/menu/menu.js → menu/primitives.js} +117 -279
  110. package/dist/menu/primitives.js.map +1 -0
  111. package/dist/{components/popover/popover.d.ts → popover/index.d.ts} +3 -5
  112. package/dist/{components/popover/popover.js → popover/index.js} +15 -51
  113. package/dist/popover/index.js.map +1 -0
  114. package/dist/{interactions/outside-click/outside-click-mixin.js → popover/outside-click.js} +1 -1
  115. package/dist/popover/outside-click.js.map +1 -0
  116. package/dist/popover/scroll-lock.d.ts +1 -0
  117. package/dist/{utils → popover}/scroll-lock.js +0 -18
  118. package/dist/popover/scroll-lock.js.map +1 -0
  119. package/dist/radio/index.d.ts +12 -0
  120. package/dist/radio/index.js +77 -0
  121. package/dist/radio/index.js.map +1 -0
  122. package/dist/runtime/client-entries.d.ts +1 -2
  123. package/dist/runtime/client-entries.js.map +1 -1
  124. package/dist/runtime/component.d.ts +2 -2
  125. package/dist/runtime/component.js +1 -1
  126. package/dist/runtime/component.js.map +1 -1
  127. package/dist/runtime/diff-dom.js +194 -21
  128. package/dist/runtime/diff-dom.js.map +1 -1
  129. package/dist/runtime/frame.d.ts +7 -0
  130. package/dist/runtime/frame.js +176 -41
  131. package/dist/runtime/frame.js.map +1 -1
  132. package/dist/runtime/jsx.d.ts +2 -3
  133. package/dist/runtime/jsx.js.map +1 -1
  134. package/dist/runtime/reconcile.js +11 -5
  135. package/dist/runtime/reconcile.js.map +1 -1
  136. package/dist/runtime/vdom.js +3 -0
  137. package/dist/runtime/vdom.js.map +1 -1
  138. package/dist/select/index.d.ts +19 -0
  139. package/dist/select/index.js +95 -0
  140. package/dist/select/index.js.map +1 -0
  141. package/dist/select/keydown.js.map +1 -0
  142. package/dist/{components/select/select.d.ts → select/primitives.d.ts} +4 -6
  143. package/dist/{components/select/select.js → select/primitives.js} +12 -64
  144. package/dist/select/primitives.js.map +1 -0
  145. package/dist/server/stream.js +5 -4
  146. package/dist/server/stream.js.map +1 -1
  147. package/dist/shared/focus-styles.d.ts +1 -0
  148. package/dist/shared/focus-styles.js +2 -0
  149. package/dist/shared/focus-styles.js.map +1 -0
  150. package/dist/shared/icons.d.ts +9 -0
  151. package/dist/shared/icons.js +42 -0
  152. package/dist/shared/icons.js.map +1 -0
  153. package/dist/shared/listbox-popover-styles.d.ts +6 -0
  154. package/dist/shared/listbox-popover-styles.js +137 -0
  155. package/dist/shared/listbox-popover-styles.js.map +1 -0
  156. package/dist/shared/style-values.d.ts +78 -0
  157. package/dist/{theme/presets/rmx-01/index.js → shared/style-values.js} +3 -34
  158. package/dist/shared/style-values.js.map +1 -0
  159. package/dist/{interactions/typeahead/typeahead-mixin.js → shared/typeahead.js} +1 -1
  160. package/dist/shared/typeahead.js.map +1 -0
  161. package/dist/shared/wait-for-css-transition.js.map +1 -0
  162. package/dist/shared/wait.js.map +1 -0
  163. package/dist/style/style.js +27 -10
  164. package/dist/style/style.js.map +1 -1
  165. package/dist/tabs/index.d.ts +32 -0
  166. package/dist/tabs/index.js +157 -0
  167. package/dist/tabs/index.js.map +1 -0
  168. package/dist/tabs/primitives.d.ts +57 -0
  169. package/dist/tabs/primitives.js +238 -0
  170. package/dist/tabs/primitives.js.map +1 -0
  171. package/dist/toggle/index.d.ts +9 -0
  172. package/dist/toggle/index.js +108 -0
  173. package/dist/toggle/index.js.map +1 -0
  174. package/dist/toggle/primitives.d.ts +29 -0
  175. package/dist/toggle/primitives.js +127 -0
  176. package/dist/toggle/primitives.js.map +1 -0
  177. package/package.json +74 -45
  178. package/src/{components/accordion → accordion}/README.md +50 -14
  179. package/src/accordion/index.tsx +285 -0
  180. package/src/accordion/primitives.tsx +512 -0
  181. package/src/{components/anchor → anchor}/README.md +48 -5
  182. package/src/{components/anchor/anchor.ts → anchor/index.ts} +53 -12
  183. package/src/animation/demos/aspect-ratio.tsx +120 -0
  184. package/src/animation/demos/bouncy-switch.tsx +47 -0
  185. package/src/animation/demos/color-interpolation.tsx +73 -0
  186. package/src/animation/demos/cube.tsx +116 -0
  187. package/src/animation/demos/default-animate.tsx +129 -0
  188. package/src/animation/demos/drag-release.ts +118 -0
  189. package/src/animation/demos/enter.tsx +22 -0
  190. package/src/animation/demos/exit.tsx +79 -0
  191. package/src/animation/demos/flip-toggle.tsx +46 -0
  192. package/src/animation/demos/hold-to-confirm.tsx +317 -0
  193. package/src/animation/demos/html-content.tsx +41 -0
  194. package/src/animation/demos/interruptible-keyframes.tsx +62 -0
  195. package/src/animation/demos/keyframes.tsx +29 -0
  196. package/src/animation/demos/material-ripple.tsx +158 -0
  197. package/src/animation/demos/mixin-presence-list.tsx +121 -0
  198. package/src/animation/demos/mixin-reclaim.tsx +125 -0
  199. package/src/animation/demos/multi-state-badge.tsx +426 -0
  200. package/src/animation/demos/press.tsx +67 -0
  201. package/src/animation/demos/reordering.tsx +68 -0
  202. package/src/animation/demos/rolling-square.tsx +57 -0
  203. package/src/animation/demos/rotate.tsx +21 -0
  204. package/src/animation/demos/shared-layout.tsx +211 -0
  205. package/src/animation/demos/transition-options.tsx +24 -0
  206. package/src/animation/spring.ts +22 -0
  207. package/src/{components/breadcrumbs → breadcrumbs}/README.md +4 -4
  208. package/src/{components/breadcrumbs/breadcrumbs.tsx → breadcrumbs/index.tsx} +18 -18
  209. package/src/button/README.md +44 -0
  210. package/src/button/index.ts +168 -0
  211. package/src/checkbox/README.md +59 -0
  212. package/src/checkbox/index.ts +162 -0
  213. package/src/{components/combobox → combobox}/README.md +58 -9
  214. package/src/combobox/index.tsx +112 -0
  215. package/src/{components/combobox/combobox.tsx → combobox/primitives.tsx} +5 -91
  216. package/src/input/README.md +52 -0
  217. package/src/input/index.ts +147 -0
  218. package/src/{components/listbox → listbox}/README.md +9 -41
  219. package/src/{components/listbox/listbox.ts → listbox/index.ts} +2 -87
  220. package/src/menu/README.md +161 -0
  221. package/src/menu/index.tsx +354 -0
  222. package/src/{components/menu/menu.tsx → menu/primitives.tsx} +117 -359
  223. package/src/{components/popover → popover}/README.md +20 -39
  224. package/src/{components/popover/popover.ts → popover/index.ts} +16 -61
  225. package/src/{utils → popover}/scroll-lock.ts +0 -28
  226. package/src/radio/README.md +53 -0
  227. package/src/radio/index.ts +100 -0
  228. package/src/runtime/client-entries.ts +1 -5
  229. package/src/runtime/component.ts +3 -3
  230. package/src/runtime/demos/readme.demo.tsx +704 -0
  231. package/src/runtime/diff-dom.ts +270 -20
  232. package/src/runtime/frame.ts +218 -41
  233. package/src/runtime/jsx.ts +6 -10
  234. package/src/runtime/reconcile.ts +11 -5
  235. package/src/runtime/vdom.ts +3 -0
  236. package/src/{components/select → select}/README.md +29 -19
  237. package/src/select/index.tsx +159 -0
  238. package/src/{components/select/select.tsx → select/primitives.tsx} +11 -99
  239. package/src/server/stream.ts +10 -5
  240. package/src/shared/focus-styles.ts +2 -0
  241. package/src/shared/icons.tsx +62 -0
  242. package/src/shared/listbox-popover-styles.ts +146 -0
  243. package/src/shared/style-values.ts +165 -0
  244. package/src/style/style.ts +29 -10
  245. package/src/tabs/README.md +141 -0
  246. package/src/tabs/index.tsx +244 -0
  247. package/src/tabs/primitives.ts +365 -0
  248. package/src/toggle/README.md +56 -0
  249. package/src/toggle/index.tsx +134 -0
  250. package/src/toggle/primitives.ts +192 -0
  251. package/dist/components/accordion/accordion.d.ts +0 -92
  252. package/dist/components/accordion/accordion.js +0 -335
  253. package/dist/components/accordion/accordion.js.map +0 -1
  254. package/dist/components/accordion/demos/card.demo.d.ts +0 -6
  255. package/dist/components/accordion/demos/card.demo.js +0 -58
  256. package/dist/components/accordion/demos/card.demo.js.map +0 -1
  257. package/dist/components/accordion/demos/multiple.demo.d.ts +0 -6
  258. package/dist/components/accordion/demos/multiple.demo.js +0 -19
  259. package/dist/components/accordion/demos/multiple.demo.js.map +0 -1
  260. package/dist/components/accordion/demos/overview.demo.d.ts +0 -6
  261. package/dist/components/accordion/demos/overview.demo.js +0 -19
  262. package/dist/components/accordion/demos/overview.demo.js.map +0 -1
  263. package/dist/components/anchor/anchor.js.map +0 -1
  264. package/dist/components/breadcrumbs/breadcrumbs.js.map +0 -1
  265. package/dist/components/breadcrumbs/demos/basic.demo.d.ts +0 -5
  266. package/dist/components/breadcrumbs/demos/basic.demo.js +0 -14
  267. package/dist/components/breadcrumbs/demos/basic.demo.js.map +0 -1
  268. package/dist/components/breadcrumbs/demos/separator.demo.d.ts +0 -5
  269. package/dist/components/breadcrumbs/demos/separator.demo.js +0 -14
  270. package/dist/components/breadcrumbs/demos/separator.demo.js.map +0 -1
  271. package/dist/components/button/button.d.ts +0 -105
  272. package/dist/components/button/button.js +0 -207
  273. package/dist/components/button/button.js.map +0 -1
  274. package/dist/components/button/demos/aliases.demo.d.ts +0 -6
  275. package/dist/components/button/demos/aliases.demo.js +0 -18
  276. package/dist/components/button/demos/aliases.demo.js.map +0 -1
  277. package/dist/components/button/demos/basic.demo.d.ts +0 -6
  278. package/dist/components/button/demos/basic.demo.js +0 -19
  279. package/dist/components/button/demos/basic.demo.js.map +0 -1
  280. package/dist/components/button/demos/states.demo.d.ts +0 -6
  281. package/dist/components/button/demos/states.demo.js +0 -29
  282. package/dist/components/button/demos/states.demo.js.map +0 -1
  283. package/dist/components/combobox/combobox.js.map +0 -1
  284. package/dist/components/combobox/demos/overview.demo.d.ts +0 -6
  285. package/dist/components/combobox/demos/overview.demo.js +0 -153
  286. package/dist/components/combobox/demos/overview.demo.js.map +0 -1
  287. package/dist/components/glyph/glyph.d.ts +0 -14
  288. package/dist/components/glyph/glyph.js +0 -65
  289. package/dist/components/glyph/glyph.js.map +0 -1
  290. package/dist/components/listbox/demos/overview.demo.d.ts +0 -6
  291. package/dist/components/listbox/demos/overview.demo.js +0 -64
  292. package/dist/components/listbox/demos/overview.demo.js.map +0 -1
  293. package/dist/components/listbox/listbox.js.map +0 -1
  294. package/dist/components/menu/demos/bubbling.demo.d.ts +0 -5
  295. package/dist/components/menu/demos/bubbling.demo.js +0 -15
  296. package/dist/components/menu/demos/bubbling.demo.js.map +0 -1
  297. package/dist/components/menu/demos/overview.demo.d.ts +0 -6
  298. package/dist/components/menu/demos/overview.demo.js +0 -32
  299. package/dist/components/menu/demos/overview.demo.js.map +0 -1
  300. package/dist/components/menu/hover-aim.js.map +0 -1
  301. package/dist/components/menu/menu.js.map +0 -1
  302. package/dist/components/popover/demos/overview.demo.d.ts +0 -6
  303. package/dist/components/popover/demos/overview.demo.js +0 -64
  304. package/dist/components/popover/demos/overview.demo.js.map +0 -1
  305. package/dist/components/popover/popover.js.map +0 -1
  306. package/dist/components/select/demos/deconstructed.demo.d.ts +0 -6
  307. package/dist/components/select/demos/deconstructed.demo.js +0 -53
  308. package/dist/components/select/demos/deconstructed.demo.js.map +0 -1
  309. package/dist/components/select/demos/overview.demo.d.ts +0 -5
  310. package/dist/components/select/demos/overview.demo.js +0 -27
  311. package/dist/components/select/demos/overview.demo.js.map +0 -1
  312. package/dist/components/select/select.js.map +0 -1
  313. package/dist/components/separator/separator.d.ts +0 -2
  314. package/dist/components/separator/separator.js +0 -15
  315. package/dist/components/separator/separator.js.map +0 -1
  316. package/dist/components/tabs/tabs.d.ts +0 -78
  317. package/dist/components/tabs/tabs.js +0 -339
  318. package/dist/components/tabs/tabs.js.map +0 -1
  319. package/dist/interactions/keydown/keydown.js.map +0 -1
  320. package/dist/interactions/outside-click/outside-click-mixin.js.map +0 -1
  321. package/dist/interactions/typeahead/typeahead-mixin.js.map +0 -1
  322. package/dist/theme/contract.d.ts +0 -61
  323. package/dist/theme/contract.js +0 -131
  324. package/dist/theme/contract.js.map +0 -1
  325. package/dist/theme/glyph-contract.d.ts +0 -11
  326. package/dist/theme/glyph-contract.js +0 -34
  327. package/dist/theme/glyph-contract.js.map +0 -1
  328. package/dist/theme/presets/rmx-01/glyphs.d.ts +0 -2
  329. package/dist/theme/presets/rmx-01/glyphs.js +0 -252
  330. package/dist/theme/presets/rmx-01/glyphs.js.map +0 -1
  331. package/dist/theme/presets/rmx-01/index.d.ts +0 -4
  332. package/dist/theme/presets/rmx-01/index.js.map +0 -1
  333. package/dist/theme/runtime.d.ts +0 -2
  334. package/dist/theme/runtime.js +0 -84
  335. package/dist/theme/runtime.js.map +0 -1
  336. package/dist/theme/theme.d.ts +0 -6
  337. package/dist/theme/theme.js +0 -5
  338. package/dist/theme/theme.js.map +0 -1
  339. package/dist/utils/flash-attribute.js.map +0 -1
  340. package/dist/utils/scroll-lock.d.ts +0 -3
  341. package/dist/utils/scroll-lock.js.map +0 -1
  342. package/dist/utils/wait-for-css-transition.js.map +0 -1
  343. package/dist/utils/wait.js.map +0 -1
  344. package/src/components/accordion/accordion.tsx +0 -565
  345. package/src/components/accordion/demos/card.demo.tsx +0 -113
  346. package/src/components/accordion/demos/multiple.demo.tsx +0 -56
  347. package/src/components/accordion/demos/overview.demo.tsx +0 -56
  348. package/src/components/breadcrumbs/demos/basic.demo.tsx +0 -17
  349. package/src/components/breadcrumbs/demos/separator.demo.tsx +0 -18
  350. package/src/components/button/README.md +0 -44
  351. package/src/components/button/button.tsx +0 -286
  352. package/src/components/button/demos/aliases.demo.tsx +0 -27
  353. package/src/components/button/demos/basic.demo.tsx +0 -30
  354. package/src/components/button/demos/states.demo.tsx +0 -52
  355. package/src/components/combobox/demos/overview.demo.tsx +0 -199
  356. package/src/components/glyph/README.md +0 -72
  357. package/src/components/glyph/glyph.tsx +0 -110
  358. package/src/components/listbox/demos/overview.demo.tsx +0 -101
  359. package/src/components/menu/README.md +0 -96
  360. package/src/components/menu/demos/bubbling.demo.tsx +0 -34
  361. package/src/components/menu/demos/overview.demo.tsx +0 -76
  362. package/src/components/popover/demos/overview.demo.tsx +0 -143
  363. package/src/components/select/demos/deconstructed.demo.tsx +0 -95
  364. package/src/components/select/demos/overview.demo.tsx +0 -51
  365. package/src/components/separator/separator.ts +0 -19
  366. package/src/components/tabs/README.md +0 -105
  367. package/src/components/tabs/tabs.tsx +0 -504
  368. package/src/theme/README.md +0 -103
  369. package/src/theme/contract.ts +0 -215
  370. package/src/theme/glyph-contract.ts +0 -59
  371. package/src/theme/presets/rmx-01/glyphs.tsx +0 -308
  372. package/src/theme/presets/rmx-01/index.ts +0 -123
  373. package/src/theme/runtime.ts +0 -119
  374. package/src/theme/theme.ts +0 -15
  375. package/src/utils/scroll-lock/README.md +0 -33
  376. /package/dist/{components/breadcrumbs/breadcrumbs.d.ts → breadcrumbs/index.d.ts} +0 -0
  377. /package/dist/{utils → listbox}/flash-attribute.d.ts +0 -0
  378. /package/dist/{utils → listbox}/flash-attribute.js +0 -0
  379. /package/dist/{components/menu → menu}/hover-aim.d.ts +0 -0
  380. /package/dist/{components/menu → menu}/hover-aim.js +0 -0
  381. /package/dist/{interactions/outside-click/outside-click-mixin.d.ts → popover/outside-click.d.ts} +0 -0
  382. /package/dist/{interactions/keydown → select}/keydown.d.ts +0 -0
  383. /package/dist/{interactions/keydown → select}/keydown.js +0 -0
  384. /package/dist/{interactions/typeahead/typeahead-mixin.d.ts → shared/typeahead.d.ts} +0 -0
  385. /package/dist/{utils → shared}/wait-for-css-transition.d.ts +0 -0
  386. /package/dist/{utils → shared}/wait-for-css-transition.js +0 -0
  387. /package/dist/{utils → shared}/wait.d.ts +0 -0
  388. /package/dist/{utils → shared}/wait.js +0 -0
  389. /package/src/{utils → listbox}/flash-attribute.ts +0 -0
  390. /package/src/{components/menu → menu}/hover-aim.ts +0 -0
  391. /package/src/{interactions/outside-click/outside-click-mixin.ts → popover/outside-click.ts} +0 -0
  392. /package/src/{interactions/keydown → select}/keydown.ts +0 -0
  393. /package/src/{interactions/typeahead/typeahead-mixin.ts → shared/typeahead.ts} +0 -0
  394. /package/src/{utils → shared}/wait-for-css-transition.ts +0 -0
  395. /package/src/{utils → shared}/wait.ts +0 -0
@@ -0,0 +1,168 @@
1
+ import { createMixin, css } from '@remix-run/ui'
2
+ import type { CSSMixinDescriptor, ElementProps, MixinDescriptor } from '@remix-run/ui'
3
+ import { renderMixinElement } from '../runtime/mixins/mixin.ts'
4
+
5
+ export type ButtonSize = 'md' | 'lg'
6
+ export type ButtonTone = 'neutral' | 'primary' | 'ghost'
7
+
8
+ export interface ButtonOptions {
9
+ size?: ButtonSize
10
+ tone?: ButtonTone
11
+ }
12
+
13
+ type ButtonMixin = readonly [
14
+ MixinDescriptor<Element, [], ElementProps>,
15
+ CSSMixinDescriptor,
16
+ CSSMixinDescriptor,
17
+ CSSMixinDescriptor,
18
+ ]
19
+
20
+ const buttonDefaultAttrs = createMixin<Element, [], ElementProps>((handle, hostType) => (props) => {
21
+ if (hostType !== 'button' || props.type !== undefined) {
22
+ return handle.element
23
+ }
24
+
25
+ return renderMixinElement(handle.element, {
26
+ ...props,
27
+ type: 'button',
28
+ })
29
+ })()
30
+
31
+ const baseStyle: CSSMixinDescriptor = css({
32
+ '--rmx-button-shadow': '0 0 0 0 rgba(0, 0, 0, 0)',
33
+ '--rmx-button-focus-shadow':
34
+ '0 0 0 1px #3573F6, var(--rmx-button-shadow), 0 0 0 4px rgba(53, 115, 246, 0.1), 0 6px 32px 4px rgba(53, 115, 246, 0.08), inset 0 0 8px 1px rgba(53, 115, 246, 0.05)',
35
+ appearance: 'none',
36
+ margin: 0,
37
+ boxSizing: 'border-box',
38
+ cursor: 'revert',
39
+ display: 'inline-flex',
40
+ alignItems: 'center',
41
+ justifyContent: 'center',
42
+ flexDirection: 'row',
43
+ gap: '4px',
44
+ paddingBlock: 0,
45
+ width: 'max-content',
46
+ maxWidth: '100%',
47
+ borderRadius: '999px',
48
+ fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
49
+ fontStyle: 'normal',
50
+ fontWeight: 500,
51
+ fontFeatureSettings: '"cv01" on, "ss01" on',
52
+ letterSpacing: 0,
53
+ whiteSpace: 'nowrap',
54
+ textDecoration: 'none',
55
+ userSelect: 'none',
56
+ WebkitUserSelect: 'none',
57
+ verticalAlign: 'top',
58
+ boxShadow: 'var(--rmx-button-shadow)',
59
+ '&:focus-visible': {
60
+ outline: 0,
61
+ boxShadow: 'var(--rmx-button-focus-shadow)',
62
+ },
63
+ '&:disabled, &[aria-disabled="true"]': {
64
+ cursor: 'not-allowed',
65
+ opacity: 0.55,
66
+ },
67
+ })
68
+
69
+ const mediumStyle: CSSMixinDescriptor = css({
70
+ height: '26px',
71
+ minHeight: '26px',
72
+ paddingInline: '12px',
73
+ fontSize: '12px',
74
+ lineHeight: '17px',
75
+ '--rmx-button-neutral-shadow-alpha': '0.03',
76
+ })
77
+
78
+ const largeStyle: CSSMixinDescriptor = css({
79
+ height: '30px',
80
+ minHeight: '30px',
81
+ paddingInline: '12px',
82
+ fontSize: '13px',
83
+ lineHeight: '20px',
84
+ '--rmx-button-neutral-shadow-alpha': '0.04',
85
+ })
86
+
87
+ const neutralStyle: CSSMixinDescriptor = css({
88
+ background: '#FCFCFC',
89
+ border: '1px solid rgba(0, 0, 0, 0.08)',
90
+ '--rmx-button-shadow':
91
+ '0 -2px 0 -2px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(255, 255, 255, 0.75), 0 1px 0 #FFFFFF, 0 2px 4px -1px rgb(0 0 0 / var(--rmx-button-neutral-shadow-alpha)), inset 0 2px 0 #FFFFFF, inset 0 -1px 0 rgba(0, 0, 0, 0.05)',
92
+ color: '#101010',
93
+ textShadow: '0 1px 0 #FFFFFF',
94
+ '&:hover:not(:disabled):not([aria-disabled="true"])': {
95
+ background: '#FFFFFF',
96
+ borderColor: 'rgba(0, 0, 0, 0.1)',
97
+ '--rmx-button-shadow':
98
+ '0 -2px 0 -2px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(255, 255, 255, 0.8), 0 1px 0 #FFFFFF, 0 3px 6px -2px rgb(0 0 0 / calc(var(--rmx-button-neutral-shadow-alpha) + 0.03)), inset 0 2px 0 #FFFFFF, inset 0 -1px 0 rgba(0, 0, 0, 0.05)',
99
+ },
100
+ '&:active:not(:disabled):not([aria-disabled="true"]), &[aria-pressed="true"]:not(:disabled):not([aria-disabled="true"])':
101
+ {
102
+ background: '#F7F7F7',
103
+ borderColor: 'rgba(0, 0, 0, 0.12)',
104
+ '--rmx-button-shadow':
105
+ '0 0 0 1px rgba(255, 255, 255, 0.65), inset 0 1px 2px rgba(0, 0, 0, 0.08), inset 0 -1px 0 rgba(255, 255, 255, 0.8)',
106
+ textShadow: 'none',
107
+ },
108
+ })
109
+
110
+ const primaryStyle: CSSMixinDescriptor = css({
111
+ background:
112
+ 'radial-gradient(50% 50% at 50% 0%, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%), radial-gradient(49.59% 37.11% at 50.41% 101.56%, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0) 66.46%), #101010',
113
+ border: 0,
114
+ '--rmx-button-shadow':
115
+ '0 16px 16px -8px rgba(0, 0, 0, 0.1), 0 8px 8px -4px rgba(0, 0, 0, 0.1), 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 2px 2px -1px rgba(0, 0, 0, 0.1), inset 0 0 4px 2px #101010, inset 0 0 4px 2px rgba(255, 255, 255, 0.1), inset 0 0 12px -6px rgba(255, 255, 255, 0.75)',
116
+ color: '#FFFFFF',
117
+ textShadow: '0 1px 1px #000000',
118
+ '&:hover:not(:disabled):not([aria-disabled="true"])': {
119
+ background:
120
+ 'radial-gradient(50% 50% at 50% 0%, rgba(255, 255, 255, 0.24) 0%, rgba(255, 255, 255, 0) 100%), radial-gradient(49.59% 37.11% at 50.41% 101.56%, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 66.46%), #171717',
121
+ '--rmx-button-shadow':
122
+ '0 18px 18px -10px rgba(0, 0, 0, 0.14), 0 8px 8px -4px rgba(0, 0, 0, 0.12), 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 2px 2px -1px rgba(0, 0, 0, 0.1), inset 0 0 4px 2px #101010, inset 0 0 4px 2px rgba(255, 255, 255, 0.13), inset 0 0 12px -6px rgba(255, 255, 255, 0.85)',
123
+ },
124
+ '&:active:not(:disabled):not([aria-disabled="true"]), &[aria-pressed="true"]:not(:disabled):not([aria-disabled="true"])':
125
+ {
126
+ background:
127
+ 'radial-gradient(50% 50% at 50% 0%, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0) 100%), radial-gradient(49.59% 37.11% at 50.41% 101.56%, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 66.46%), #080808',
128
+ '--rmx-button-shadow':
129
+ '0 2px 2px -1px rgba(0, 0, 0, 0.14), inset 0 0 4px 2px #050505, inset 0 1px 2px rgba(0, 0, 0, 0.45), inset 0 0 10px -6px rgba(255, 255, 255, 0.55)',
130
+ textShadow: '0 1px 1px #000000',
131
+ },
132
+ '&:active:not(:disabled):not([aria-disabled="true"])': {
133
+ transform: 'translateY(1px)',
134
+ },
135
+ })
136
+
137
+ const ghostStyle: CSSMixinDescriptor = css({
138
+ background: 'transparent',
139
+ border: '1px solid transparent',
140
+ '--rmx-button-shadow': '0 0 0 0 rgba(0, 0, 0, 0)',
141
+ color: '#101010',
142
+ textShadow: 'none',
143
+ '&:hover:not(:disabled):not([aria-disabled="true"])': {
144
+ background: 'rgba(16, 16, 16, 0.05)',
145
+ },
146
+ '&:active:not(:disabled):not([aria-disabled="true"]), &[aria-pressed="true"]:not(:disabled):not([aria-disabled="true"])':
147
+ {
148
+ background: 'rgba(16, 16, 16, 0.08)',
149
+ },
150
+ })
151
+
152
+ const sizeStyles = {
153
+ md: mediumStyle,
154
+ lg: largeStyle,
155
+ } as const satisfies Record<ButtonSize, CSSMixinDescriptor>
156
+
157
+ const toneStyles = {
158
+ neutral: neutralStyle,
159
+ primary: primaryStyle,
160
+ ghost: ghostStyle,
161
+ } as const satisfies Record<ButtonTone, CSSMixinDescriptor>
162
+
163
+ export function button(options: ButtonOptions = {}): ButtonMixin {
164
+ let { size = 'md', tone = 'neutral' } = options
165
+ return [buttonDefaultAttrs, baseStyle, sizeStyles[size], toneStyles[tone]]
166
+ }
167
+
168
+ export default button
@@ -0,0 +1,59 @@
1
+ # checkbox
2
+
3
+ `checkbox` is a style mixin for native checkbox inputs. It only owns checkbox visuals, an optional visual state, and the default `type="checkbox"` behavior for native `<input>` hosts.
4
+
5
+ ## Usage
6
+
7
+ ```tsx
8
+ import checkbox from 'remix/ui/checkbox'
9
+
10
+ function VisibilityToggle() {
11
+ return () => (
12
+ <label>
13
+ <input defaultChecked mix={checkbox()} name="visibility" value="archived" />
14
+ Show archived items
15
+ </label>
16
+ )
17
+ }
18
+ ```
19
+
20
+ Use `size: 'lg'` when the surrounding UI needs a larger visual control:
21
+
22
+ ```tsx
23
+ <input mix={checkbox({ size: 'lg' })} />
24
+ ```
25
+
26
+ For mixed state, app code owns the checkbox state and the native `indeterminate` property:
27
+
28
+ ```tsx
29
+ import checkbox from 'remix/ui/checkbox'
30
+
31
+ function PermissionParent() {
32
+ let state: 'checked' | 'mixed' | 'unchecked' = 'mixed'
33
+
34
+ return () => (
35
+ <input
36
+ checked={state === 'checked'}
37
+ indeterminate={state === 'mixed'}
38
+ mix={checkbox({ state })}
39
+ />
40
+ )
41
+ }
42
+ ```
43
+
44
+ ## `remix/ui/checkbox`
45
+
46
+ - `checkbox(options)`: style mixin for native checkbox inputs or checkbox-like hosts.
47
+ - `CheckboxOptions`: accepts `size` and `state`.
48
+ - `CheckboxSize`: `'md'` or `'lg'`. Defaults to `'md'`.
49
+ - `CheckboxState`: `'checked'`, `'mixed'`, or `'unchecked'`.
50
+
51
+ ## Behavior Notes
52
+
53
+ - `checkbox()` returns a mixin descriptor, so it composes with other mixins in the host element's `mix` prop.
54
+ - Native `<input>` hosts receive `type="checkbox"` unless an explicit `type` is provided.
55
+ - Native input state is browser-owned through `checked`, `defaultChecked`, and user interaction.
56
+ - The optional `state` option adds `aria-checked` and `data-state` for app-owned mixed state or custom hosts.
57
+ - Checked styles apply through `:checked`, `aria-checked="true"`, or `data-state="checked"`.
58
+ - Mixed styles apply through `:indeterminate`, `indeterminate`, `aria-checked="mixed"`, or `data-state="mixed"`.
59
+ - Disabled hosts use the shared disabled treatment through `disabled` or `aria-disabled="true"`.
@@ -0,0 +1,162 @@
1
+ import { createMixin, css } from '@remix-run/ui'
2
+ import type { CSSMixinDescriptor, ElementProps, MixinDescriptor } from '@remix-run/ui'
3
+ import { renderMixinElement } from '../runtime/mixins/mixin.ts'
4
+ import { controlFocusShadow } from '../shared/focus-styles.ts'
5
+
6
+ export type CheckboxSize = 'md' | 'lg'
7
+ export type CheckboxState = 'checked' | 'mixed' | 'unchecked'
8
+
9
+ export interface CheckboxOptions {
10
+ size?: CheckboxSize
11
+ state?: CheckboxState
12
+ }
13
+
14
+ type CheckboxMixin = readonly [
15
+ MixinDescriptor<Element, [state?: CheckboxState], ElementProps>,
16
+ CSSMixinDescriptor,
17
+ CSSMixinDescriptor,
18
+ ]
19
+
20
+ const checkedSelector = '&:checked, &[aria-checked="true"], &[data-state="checked"]'
21
+ const mixedSelector =
22
+ '&:indeterminate, &[indeterminate], &[aria-checked="mixed"], &[data-state="mixed"]'
23
+ const checkedMarkSelector =
24
+ '&:checked::before, &[aria-checked="true"]::before, &[data-state="checked"]::before'
25
+ const mixedMarkSelector =
26
+ '&:indeterminate::before, &[indeterminate]::before, &[aria-checked="mixed"]::before, &[data-state="mixed"]::before'
27
+ const enabledSelector = ':not(:disabled):not([aria-disabled="true"])'
28
+ const checkedActiveSelector =
29
+ '&:checked:active:not(:disabled):not([aria-disabled="true"]), &[aria-checked="true"]:active:not(:disabled):not([aria-disabled="true"]), &[data-state="checked"]:active:not(:disabled):not([aria-disabled="true"])'
30
+ const mixedActiveSelector =
31
+ '&:indeterminate:active:not(:disabled):not([aria-disabled="true"]), &[indeterminate]:active:not(:disabled):not([aria-disabled="true"]), &[aria-checked="mixed"]:active:not(:disabled):not([aria-disabled="true"]), &[data-state="mixed"]:active:not(:disabled):not([aria-disabled="true"])'
32
+ const checkIconUrl =
33
+ "url(\"data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg filter='url(%23filter0_d_2_2264)'%3E%3Cpath d='M2.75 5.76562L5.10156 8.25L9.23438 1.75' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M2.75 5.76562L5.10156 8.25L9.23438 1.75' stroke='url(%23paint0_linear_2_2264)' stroke-opacity='0.1' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_2_2264' x='0' y='0' width='11.9845' height='12' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dy='1'/%3E%3CfeGaussianBlur stdDeviation='1'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4 0'/%3E%3CfeBlend mode='overlay' in2='BackgroundImageFix' result='effect1_dropShadow_2_2264'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_2_2264' result='shape'/%3E%3C/filter%3E%3ClinearGradient id='paint0_linear_2_2264' x1='5.99219' y1='1.75' x2='5.99219' y2='8.25' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0.25' stop-color='%233573F6' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%233573F6'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E\")"
34
+
35
+ const checkboxAriaChecked = {
36
+ checked: 'true',
37
+ mixed: 'mixed',
38
+ unchecked: 'false',
39
+ } as const satisfies Record<CheckboxState, 'true' | 'mixed' | 'false'>
40
+
41
+ const checkboxDefaultAttrs = createMixin<Element, [state?: CheckboxState], ElementProps>(
42
+ (handle, hostType) => (state, props) => {
43
+ let stateProps =
44
+ state === undefined
45
+ ? props
46
+ : {
47
+ ...props,
48
+ 'aria-checked': props['aria-checked'] ?? checkboxAriaChecked[state],
49
+ 'data-state': props['data-state'] ?? state,
50
+ }
51
+
52
+ if (hostType !== 'input' || stateProps.type !== undefined) {
53
+ return state === undefined ? handle.element : renderMixinElement(handle.element, stateProps)
54
+ }
55
+
56
+ return renderMixinElement(handle.element, {
57
+ ...stateProps,
58
+ type: 'checkbox',
59
+ })
60
+ },
61
+ )
62
+
63
+ const baseStyle: CSSMixinDescriptor = css({
64
+ appearance: 'none',
65
+ WebkitAppearance: 'none',
66
+ margin: 0,
67
+ boxSizing: 'border-box',
68
+ position: 'relative',
69
+ display: 'inline-grid',
70
+ placeItems: 'center',
71
+ width: 'var(--rmx-checkbox-size)',
72
+ height: 'var(--rmx-checkbox-size)',
73
+ minWidth: 'var(--rmx-checkbox-size)',
74
+ minHeight: 'var(--rmx-checkbox-size)',
75
+ padding: 0,
76
+ border: 0,
77
+ borderRadius: 'var(--rmx-checkbox-radius)',
78
+ background: '#FFFFFF',
79
+ boxShadow:
80
+ '0 2px 2px -1px rgba(0, 0, 0, 0.05), 0 3px 4px -1.5px rgba(0, 0, 0, 0.05), 0 4px 8px -2px rgba(0, 0, 0, 0.05), 0 5px 16px -2.5px rgba(0, 0, 0, 0.05), 0 0 0 1px rgba(0, 0, 0, 0.12)',
81
+ color: '#FFFFFF',
82
+ verticalAlign: 'middle',
83
+ flex: 'none',
84
+ '&::before': {
85
+ content: '""',
86
+ position: 'absolute',
87
+ opacity: 0,
88
+ pointerEvents: 'none',
89
+ },
90
+ '&:disabled, &[aria-disabled="true"]': {
91
+ opacity: 0.55,
92
+ },
93
+ [`${checkedSelector}, ${mixedSelector}`]: {
94
+ background:
95
+ 'linear-gradient(180deg, rgba(0, 0, 0, 0) 24.52%, rgba(0, 0, 0, 0.1) 100%), #3573F6',
96
+ backgroundBlendMode: 'overlay, normal',
97
+ borderRadius: 'var(--rmx-checkbox-active-radius)',
98
+ boxShadow:
99
+ '0 1px 1px -0.5px rgba(9, 68, 190, 0.12), 0 2px 2px -1px rgba(9, 68, 190, 0.12), 0 4px 4px -2px rgba(9, 68, 190, 0.12), 0 8px 8px -4px rgba(9, 68, 190, 0.12), 0 2px 8px rgba(53, 115, 246, 0.4), inset 0 0 3px 1px rgba(0, 0, 0, 0.1)',
100
+ },
101
+ [checkedMarkSelector]: {
102
+ opacity: 1,
103
+ left: '50%',
104
+ top: '50%',
105
+ width: 'var(--rmx-checkbox-check-size)',
106
+ height: 'var(--rmx-checkbox-check-size)',
107
+ background: `${checkIconUrl} center / contain no-repeat`,
108
+ transform: 'translate(-50%, calc(-50% + var(--rmx-checkbox-check-y)))',
109
+ },
110
+ [mixedMarkSelector]: {
111
+ opacity: 1,
112
+ left: '50%',
113
+ top: '50%',
114
+ width: 'var(--rmx-checkbox-mixed-width)',
115
+ height: 'var(--rmx-checkbox-mixed-height)',
116
+ border: 0,
117
+ borderRadius: '999px',
118
+ background: 'currentColor',
119
+ filter: 'drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4))',
120
+ transform: 'translate(-50%, -50%)',
121
+ },
122
+ [`&:active${enabledSelector}`]: {
123
+ boxShadow:
124
+ '0 1px 1px -0.5px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.14), inset 0 1px 2px rgba(0, 0, 0, 0.08)',
125
+ },
126
+ [`${checkedActiveSelector}, ${mixedActiveSelector}`]: {
127
+ boxShadow:
128
+ '0 1px 1px -0.5px rgba(9, 68, 190, 0.1), 0 2px 2px -1px rgba(9, 68, 190, 0.1), 0 4px 4px -2px rgba(9, 68, 190, 0.1), 0 6px 8px -4px rgba(9, 68, 190, 0.1), 0 2px 6px rgba(53, 115, 246, 0.32), inset 0 1px 2px rgba(0, 0, 0, 0.3), inset 0 0 3px 1px rgba(0, 0, 0, 0.12)',
129
+ },
130
+ '&:focus-visible': {
131
+ outline: 0,
132
+ boxShadow: controlFocusShadow,
133
+ },
134
+ })
135
+
136
+ const sizeStyles = {
137
+ md: css({
138
+ '--rmx-checkbox-size': '16px',
139
+ '--rmx-checkbox-radius': '4px',
140
+ '--rmx-checkbox-active-radius': '5px',
141
+ '--rmx-checkbox-check-size': '12px',
142
+ '--rmx-checkbox-check-y': '1px',
143
+ '--rmx-checkbox-mixed-width': '7px',
144
+ '--rmx-checkbox-mixed-height': '2px',
145
+ }),
146
+ lg: css({
147
+ '--rmx-checkbox-size': '20px',
148
+ '--rmx-checkbox-radius': '5px',
149
+ '--rmx-checkbox-active-radius': '6px',
150
+ '--rmx-checkbox-check-size': '15px',
151
+ '--rmx-checkbox-check-y': '1.25px',
152
+ '--rmx-checkbox-mixed-width': '9px',
153
+ '--rmx-checkbox-mixed-height': '2.25px',
154
+ }),
155
+ } as const satisfies Record<CheckboxSize, CSSMixinDescriptor>
156
+
157
+ export function checkbox(options: CheckboxOptions = {}): CheckboxMixin {
158
+ let { size = 'md', state } = options
159
+ return [checkboxDefaultAttrs(state), baseStyle, sizeStyles[size]]
160
+ }
161
+
162
+ export default checkbox
@@ -1,14 +1,15 @@
1
- # Combobox
1
+ # combobox
2
2
 
3
- `Combobox` is the input-first popup value picker for `remix/ui`.
3
+ `Combobox` is the input-first popup value picker for `remix/ui/combobox`.
4
4
 
5
5
  Use it when the user should type draft text, filter a popup list, and still commit one stable form value. If you just need a button-triggered picker, use `Select` instead.
6
6
 
7
- ## Usage
7
+ ## Component Usage
8
8
 
9
9
  ```tsx
10
10
  import { css, type Handle } from 'remix/ui'
11
- import { Combobox, ComboboxOption, onComboboxChange } from 'remix/ui/combobox'
11
+ import { Combobox, ComboboxOption } from 'remix/ui/combobox'
12
+ import { onComboboxChange } from 'remix/ui/combobox/primitives'
12
13
 
13
14
  let airports = [
14
15
  {
@@ -58,7 +59,39 @@ let root = css({
58
59
  })
59
60
  ```
60
61
 
61
- ## Public API
62
+ ## Primitive Usage
63
+
64
+ Use the lower-level primitives when app code owns the input, popover, list, and option markup:
65
+
66
+ ```tsx
67
+ import * as combobox from 'remix/ui/combobox/primitives'
68
+ import { inputStyle, listStyle, optionStyle, popoverStyle } from './combobox.styles'
69
+
70
+ let frameworks = [
71
+ { label: 'Remix', searchValue: ['remix', 'rmx'], value: 'remix' },
72
+ { label: 'React Router', value: 'react-router' },
73
+ ]
74
+
75
+ export function PrimitiveCombobox() {
76
+ return (
77
+ <combobox.Context name="framework">
78
+ <input mix={[inputStyle, combobox.input()]} placeholder="Search frameworks" />
79
+ <div mix={[popoverStyle, combobox.popover()]}>
80
+ <div mix={[listStyle, combobox.list()]}>
81
+ {frameworks.map((option) => (
82
+ <div key={option.value} mix={[optionStyle, combobox.option(option)]}>
83
+ {option.label}
84
+ </div>
85
+ ))}
86
+ </div>
87
+ </div>
88
+ <input mix={combobox.hiddenInput()} />
89
+ </combobox.Context>
90
+ )
91
+ }
92
+ ```
93
+
94
+ ## `remix/ui/combobox`
62
95
 
63
96
  ### `Combobox`
64
97
 
@@ -66,7 +99,7 @@ The convenience component.
66
99
 
67
100
  - Renders the text input, popover surface, listbox root, and hidden form input.
68
101
  - Dispatches a bubbled custom event that `onComboboxChange(...)` listens for when the committed value changes.
69
- - Accepts `defaultValue`, `disabled`, `inputId`, `name`, and `placeholder`.
102
+ - Accepts `children`, `defaultValue`, `disabled`, `inputId`, `name`, `placeholder`, and root `div` props.
70
103
 
71
104
  ### `ComboboxOption`
72
105
 
@@ -74,21 +107,31 @@ The default option row for `Combobox`.
74
107
 
75
108
  - Uses the shared listbox option visuals.
76
109
  - Accepts `label`, `value`, optional `searchValue`, and optional `disabled`.
110
+ - Renders `children` when provided, otherwise renders `label`.
77
111
  - `searchValue` can be a string or string array for aliases like airport codes, abbreviations, or alternate labels.
78
112
 
113
+ ### Style and Prop Exports
114
+
115
+ - `inputStyle`: default combobox input style.
116
+ - `popoverStyle`: default combobox popover behavior style.
117
+ - `ComboboxProps` and `ComboboxOptionProps`: public TypeScript props for the composed APIs.
118
+
119
+ ## `remix/ui/combobox/primitives`
120
+
79
121
  ### `onComboboxChange(...)`
80
122
 
81
- The listener mixin for bubbled committed-value changes.
123
+ The listener mixin from `remix/ui/combobox/primitives` for bubbled committed-value changes.
82
124
 
83
125
  The event object includes:
84
126
 
85
127
  - `event.value`: the committed value or `null`
86
128
  - `event.label`: the committed option label or `null`
87
129
  - `event.optionId`: the generated option id or `null`
130
+ - `ComboboxChangeEvent`: the event class dispatched for committed value changes.
88
131
 
89
132
  ### `combobox.Context`
90
133
 
91
- The lower-level coordinator for custom combobox composition.
134
+ The lower-level coordinator from `remix/ui/combobox/primitives` for custom combobox composition.
92
135
 
93
136
  It wraps the shared `popover` and `listbox` contexts and owns the draft text, committed value, popup state, and selection timing.
94
137
 
@@ -106,7 +149,7 @@ Turns the host into the combobox popover surface.
106
149
 
107
150
  - Uses the shared popover primitive.
108
151
  - Keeps anchor clicks inside the session so the input stays interactive while open.
109
- - Applies the combobox open/close reason contract used by `combobox.popoverStyle`.
152
+ - Applies the combobox open/close reason contract used by `popoverStyle`.
110
153
 
111
154
  ### `combobox.list()`
112
155
 
@@ -126,6 +169,12 @@ Mirrors the committed value into a hidden input for forms.
126
169
 
127
170
  Apply it to an `<input type="hidden" />` inside the same `combobox.Context`.
128
171
 
172
+ ### Primitive Types
173
+
174
+ - `ComboboxOpenStrategy`: initial active-option strategy when the popup opens.
175
+ - `ComboboxHandle`: imperative ref for reading or updating the combobox value and draft label.
176
+ - `ComboboxContextProps`, `ComboboxProps`, `ComboboxOptionOptions`, and `ComboboxOptionProps`: primitive prop and option types for custom composition.
177
+
129
178
  ## Behavior Notes
130
179
 
131
180
  - Typing opens the popup in hint mode when there are matches.
@@ -0,0 +1,112 @@
1
+ import { createElement, css } from '@remix-run/ui'
2
+ import type { CSSMixinDescriptor, Handle, Props, RemixNode } from '@remix-run/ui'
3
+
4
+ import * as combobox from '@remix-run/ui/combobox/primitives'
5
+ import type { SearchValue } from '../shared/typeahead.ts'
6
+ import { CheckIcon } from '../shared/icons.tsx'
7
+ import {
8
+ listboxIndicatorStyle,
9
+ listboxLabelStyle,
10
+ listboxListStyle,
11
+ listboxOptionStyle,
12
+ popoverSurfaceStyle,
13
+ } from '../shared/listbox-popover-styles.ts'
14
+ import { componentStyleValues as styles } from '../shared/style-values.ts'
15
+
16
+ export interface ComboboxProps extends Omit<Props<'div'>, 'children'> {
17
+ children?: RemixNode
18
+ defaultValue?: string | null
19
+ disabled?: boolean
20
+ inputId?: string
21
+ name?: string
22
+ placeholder?: string
23
+ }
24
+
25
+ export interface ComboboxOptionProps extends Omit<Props<'div'>, 'children'> {
26
+ children?: RemixNode
27
+ disabled?: boolean
28
+ label: string
29
+ searchValue?: SearchValue
30
+ value: string
31
+ }
32
+
33
+ const comboboxPopoverCss: CSSMixinDescriptor = css({
34
+ opacity: 0,
35
+ '&:popover-open': {
36
+ opacity: 1,
37
+ },
38
+ '&:not(:popover-open)': {
39
+ pointerEvents: 'none',
40
+ },
41
+ '&[data-show-reason="nav"]:not(:popover-open)': {
42
+ transition: 'opacity 180ms ease-in, overlay 180ms ease-in, display 180ms ease-in',
43
+ transitionBehavior: 'allow-discrete',
44
+ },
45
+ '&[data-show-reason="hint"]:not(:popover-open)': {
46
+ transition: 'none',
47
+ transitionBehavior: 'normal',
48
+ },
49
+ })
50
+
51
+ const comboboxInputCss: CSSMixinDescriptor = css({
52
+ minHeight: styles.control.height.sm,
53
+ width: '100%',
54
+ paddingInline: styles.space.sm,
55
+ border: `0.5px solid ${styles.colors.border.default}`,
56
+ borderRadius: styles.radius.md,
57
+ backgroundColor: styles.surface.lvl0,
58
+ color: styles.colors.text.primary,
59
+ fontFamily: styles.fontFamily.sans,
60
+ fontSize: styles.fontSize.sm,
61
+ lineHeight: styles.lineHeight.normal,
62
+ boxShadow: 'inset 0 1px 0 rgb(255 255 255 / 0.7)',
63
+ '&:focus-visible': {
64
+ outline: `2px solid ${styles.colors.focus.ring}`,
65
+ outlineOffset: styles.space.none,
66
+ },
67
+ '&[data-surface-visible="true"][aria-activedescendant]:focus-visible': {
68
+ outline: 'none',
69
+ },
70
+ })
71
+
72
+ export const inputStyle = comboboxInputCss
73
+ export const popoverStyle = comboboxPopoverCss
74
+
75
+ export function Combobox(handle: Handle<ComboboxProps>): () => RemixNode {
76
+ return () => {
77
+ let { children, defaultValue, disabled, inputId, name, placeholder, ...divProps } = handle.props
78
+
79
+ return (
80
+ <combobox.Context defaultValue={defaultValue} disabled={disabled} name={name}>
81
+ <div {...divProps}>
82
+ <input
83
+ defaultValue={defaultValue ?? undefined}
84
+ id={inputId}
85
+ mix={[inputStyle, combobox.input()]}
86
+ placeholder={placeholder}
87
+ />
88
+ <div mix={[popoverSurfaceStyle, popoverStyle, combobox.popover()]}>
89
+ <div mix={[listboxListStyle, combobox.list()]}>{children}</div>
90
+ </div>
91
+ {name && <input mix={combobox.hiddenInput()} />}
92
+ </div>
93
+ </combobox.Context>
94
+ )
95
+ }
96
+ }
97
+
98
+ export function ComboboxOption(handle: Handle<ComboboxOptionProps>): () => RemixNode {
99
+ return () => {
100
+ let { children, disabled, label, mix, searchValue, value, ...divProps } = handle.props
101
+
102
+ return (
103
+ <div
104
+ {...divProps}
105
+ mix={[listboxOptionStyle, combobox.option({ disabled, label, searchValue, value }), mix]}
106
+ >
107
+ <CheckIcon mix={listboxIndicatorStyle} />
108
+ <span mix={listboxLabelStyle}>{children ?? label}</span>
109
+ </div>
110
+ )
111
+ }
112
+ }