@remix-run/ui 0.1.2 → 0.3.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 (318) hide show
  1. package/README.md +9 -7
  2. package/dist/animation/demos/animation.demo.d.ts +5 -0
  3. package/dist/animation/demos/animation.demo.js +92 -0
  4. package/dist/animation/demos/animation.demo.js.map +1 -0
  5. package/dist/animation/demos/aspect-ratio.d.ts +2 -0
  6. package/dist/animation/demos/aspect-ratio.js +69 -0
  7. package/dist/animation/demos/aspect-ratio.js.map +1 -0
  8. package/dist/animation/demos/bouncy-switch.d.ts +2 -0
  9. package/dist/animation/demos/bouncy-switch.js +36 -0
  10. package/dist/animation/demos/bouncy-switch.js.map +1 -0
  11. package/dist/animation/demos/color-interpolation.d.ts +1 -0
  12. package/dist/animation/demos/color-interpolation.js +53 -0
  13. package/dist/animation/demos/color-interpolation.js.map +1 -0
  14. package/dist/animation/demos/cube.d.ts +2 -0
  15. package/dist/animation/demos/cube.js +86 -0
  16. package/dist/animation/demos/cube.js.map +1 -0
  17. package/dist/animation/demos/default-animate.d.ts +2 -0
  18. package/dist/animation/demos/default-animate.js +89 -0
  19. package/dist/animation/demos/default-animate.js.map +1 -0
  20. package/dist/animation/demos/drag-release.d.ts +24 -0
  21. package/dist/animation/demos/drag-release.js +96 -0
  22. package/dist/animation/demos/drag-release.js.map +1 -0
  23. package/dist/animation/demos/enter.d.ts +1 -0
  24. package/dist/animation/demos/enter.js +19 -0
  25. package/dist/animation/demos/enter.js.map +1 -0
  26. package/dist/animation/demos/exit.d.ts +2 -0
  27. package/dist/animation/demos/exit.js +58 -0
  28. package/dist/animation/demos/exit.js.map +1 -0
  29. package/dist/animation/demos/flip-toggle.d.ts +2 -0
  30. package/dist/animation/demos/flip-toggle.js +37 -0
  31. package/dist/animation/demos/flip-toggle.js.map +1 -0
  32. package/dist/animation/demos/hold-to-confirm.d.ts +13 -0
  33. package/dist/animation/demos/hold-to-confirm.js +211 -0
  34. package/dist/animation/demos/hold-to-confirm.js.map +1 -0
  35. package/dist/animation/demos/html-content.d.ts +2 -0
  36. package/dist/animation/demos/html-content.js +30 -0
  37. package/dist/animation/demos/html-content.js.map +1 -0
  38. package/dist/animation/demos/interruptible-keyframes.d.ts +1 -0
  39. package/dist/animation/demos/interruptible-keyframes.js +48 -0
  40. package/dist/animation/demos/interruptible-keyframes.js.map +1 -0
  41. package/dist/animation/demos/keyframes.d.ts +1 -0
  42. package/dist/animation/demos/keyframes.js +24 -0
  43. package/dist/animation/demos/keyframes.js.map +1 -0
  44. package/dist/animation/demos/material-ripple.d.ts +2 -0
  45. package/dist/animation/demos/material-ripple.js +127 -0
  46. package/dist/animation/demos/material-ripple.js.map +1 -0
  47. package/dist/animation/demos/mixin-presence-list.d.ts +2 -0
  48. package/dist/animation/demos/mixin-presence-list.js +88 -0
  49. package/dist/animation/demos/mixin-presence-list.js.map +1 -0
  50. package/dist/animation/demos/mixin-reclaim.d.ts +2 -0
  51. package/dist/animation/demos/mixin-reclaim.js +90 -0
  52. package/dist/animation/demos/mixin-reclaim.js.map +1 -0
  53. package/dist/animation/demos/multi-state-badge.d.ts +2 -0
  54. package/dist/animation/demos/multi-state-badge.js +231 -0
  55. package/dist/animation/demos/multi-state-badge.js.map +1 -0
  56. package/dist/animation/demos/press.d.ts +2 -0
  57. package/dist/animation/demos/press.js +63 -0
  58. package/dist/animation/demos/press.js.map +1 -0
  59. package/dist/animation/demos/reordering.d.ts +2 -0
  60. package/dist/animation/demos/reordering.js +50 -0
  61. package/dist/animation/demos/reordering.js.map +1 -0
  62. package/dist/animation/demos/rolling-square.d.ts +2 -0
  63. package/dist/animation/demos/rolling-square.js +42 -0
  64. package/dist/animation/demos/rolling-square.js.map +1 -0
  65. package/dist/animation/demos/rotate.d.ts +1 -0
  66. package/dist/animation/demos/rotate.js +18 -0
  67. package/dist/animation/demos/rotate.js.map +1 -0
  68. package/dist/animation/demos/shared-layout.d.ts +2 -0
  69. package/dist/animation/demos/shared-layout.js +114 -0
  70. package/dist/animation/demos/shared-layout.js.map +1 -0
  71. package/dist/animation/demos/spring.demo.d.ts +6 -0
  72. package/dist/animation/demos/spring.demo.js +304 -0
  73. package/dist/animation/demos/spring.demo.js.map +1 -0
  74. package/dist/animation/demos/transition-options.d.ts +1 -0
  75. package/dist/animation/demos/transition-options.js +21 -0
  76. package/dist/animation/demos/transition-options.js.map +1 -0
  77. package/dist/animation/spring.d.ts +24 -31
  78. package/dist/animation/spring.js +31 -8
  79. package/dist/animation/spring.js.map +1 -1
  80. package/dist/components/accordion/accordion.d.ts +6 -6
  81. package/dist/components/accordion/accordion.js.map +1 -1
  82. package/dist/components/accordion/demos/card.demo.d.ts +1 -0
  83. package/dist/components/accordion/demos/card.demo.js +1 -0
  84. package/dist/components/accordion/demos/card.demo.js.map +1 -1
  85. package/dist/components/accordion/demos/multiple.demo.d.ts +1 -0
  86. package/dist/components/accordion/demos/multiple.demo.js +1 -0
  87. package/dist/components/accordion/demos/multiple.demo.js.map +1 -1
  88. package/dist/components/accordion/demos/overview.demo.d.ts +1 -0
  89. package/dist/components/accordion/demos/overview.demo.js +1 -0
  90. package/dist/components/accordion/demos/overview.demo.js.map +1 -1
  91. package/dist/components/anchor/anchor.d.ts +8 -1
  92. package/dist/components/anchor/anchor.js +30 -11
  93. package/dist/components/anchor/anchor.js.map +1 -1
  94. package/dist/components/breadcrumbs/{demos/basic.demo.d.ts → basic.demo.d.ts} +1 -0
  95. package/dist/components/breadcrumbs/{demos/basic.demo.js → basic.demo.js} +1 -0
  96. package/dist/components/breadcrumbs/basic.demo.js.map +1 -0
  97. package/dist/components/breadcrumbs/breadcrumbs.d.ts +1 -1
  98. package/dist/components/breadcrumbs/{demos/separator.demo.d.ts → separator.demo.d.ts} +1 -0
  99. package/dist/components/breadcrumbs/{demos/separator.demo.js → separator.demo.js} +1 -0
  100. package/dist/components/breadcrumbs/separator.demo.js.map +1 -0
  101. package/dist/components/button/button.d.ts +4 -4
  102. package/dist/components/button/button.js +3 -1
  103. package/dist/components/button/button.js.map +1 -1
  104. package/dist/components/button/demos/aliases.demo.d.ts +1 -0
  105. package/dist/components/button/demos/aliases.demo.js +1 -0
  106. package/dist/components/button/demos/aliases.demo.js.map +1 -1
  107. package/dist/components/button/demos/basic.demo.d.ts +1 -0
  108. package/dist/components/button/demos/basic.demo.js +1 -0
  109. package/dist/components/button/demos/basic.demo.js.map +1 -1
  110. package/dist/components/button/demos/states.demo.d.ts +1 -0
  111. package/dist/components/button/demos/states.demo.js +1 -0
  112. package/dist/components/button/demos/states.demo.js.map +1 -1
  113. package/dist/components/combobox/combobox.d.ts +10 -10
  114. package/dist/components/combobox/combobox.js.map +1 -1
  115. package/dist/components/combobox/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  116. package/dist/components/combobox/{demos/overview.demo.js → overview.demo.js} +1 -0
  117. package/dist/components/combobox/overview.demo.js.map +1 -0
  118. package/dist/components/listbox/listbox.d.ts +3 -3
  119. package/dist/components/listbox/listbox.js.map +1 -1
  120. package/dist/components/listbox/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  121. package/dist/components/listbox/{demos/overview.demo.js → overview.demo.js} +1 -0
  122. package/dist/components/listbox/overview.demo.js.map +1 -0
  123. package/dist/components/menu/demos/bubbling.demo.d.ts +1 -0
  124. package/dist/components/menu/demos/bubbling.demo.js +1 -0
  125. package/dist/components/menu/demos/bubbling.demo.js.map +1 -1
  126. package/dist/components/menu/demos/context-trigger.demo.d.ts +7 -0
  127. package/dist/components/menu/demos/context-trigger.demo.js +87 -0
  128. package/dist/components/menu/demos/context-trigger.demo.js.map +1 -0
  129. package/dist/components/menu/demos/overview.demo.d.ts +1 -0
  130. package/dist/components/menu/demos/overview.demo.js +1 -0
  131. package/dist/components/menu/demos/overview.demo.js.map +1 -1
  132. package/dist/components/menu/menu.d.ts +15 -12
  133. package/dist/components/menu/menu.js +55 -0
  134. package/dist/components/menu/menu.js.map +1 -1
  135. package/dist/components/popover/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  136. package/dist/components/popover/{demos/overview.demo.js → overview.demo.js} +1 -0
  137. package/dist/components/popover/overview.demo.js.map +1 -0
  138. package/dist/components/popover/popover.d.ts +7 -7
  139. package/dist/components/popover/popover.js +11 -4
  140. package/dist/components/popover/popover.js.map +1 -1
  141. package/dist/components/select/{demos/deconstructed.demo.d.ts → deconstructed.demo.d.ts} +1 -0
  142. package/dist/components/select/{demos/deconstructed.demo.js → deconstructed.demo.js} +1 -0
  143. package/dist/components/select/deconstructed.demo.js.map +1 -0
  144. package/dist/components/select/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  145. package/dist/components/select/{demos/overview.demo.js → overview.demo.js} +1 -0
  146. package/dist/components/select/overview.demo.js.map +1 -0
  147. package/dist/components/select/select.d.ts +10 -10
  148. package/dist/components/select/select.js +1 -1
  149. package/dist/components/select/select.js.map +1 -1
  150. package/dist/components/tabs/tabs.d.ts +3 -3
  151. package/dist/index.d.ts +1 -1
  152. package/dist/index.js.map +1 -1
  153. package/dist/interactions/keydown/keydown.d.ts +1 -1
  154. package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -1
  155. package/dist/interactions/typeahead/typeahead-mixin.d.ts +3 -1
  156. package/dist/runtime/client-entries.d.ts +1 -2
  157. package/dist/runtime/client-entries.js.map +1 -1
  158. package/dist/runtime/component.d.ts +20 -17
  159. package/dist/runtime/component.js +15 -6
  160. package/dist/runtime/component.js.map +1 -1
  161. package/dist/runtime/core/attributes.d.ts +3 -1
  162. package/dist/runtime/core/attributes.js +19 -1
  163. package/dist/runtime/core/attributes.js.map +1 -1
  164. package/dist/runtime/core/props.js +6 -5
  165. package/dist/runtime/core/props.js.map +1 -1
  166. package/dist/runtime/demos/basic.demo.d.ts +6 -0
  167. package/dist/runtime/demos/basic.demo.js +16 -0
  168. package/dist/runtime/demos/basic.demo.js.map +1 -0
  169. package/dist/runtime/demos/controlled-uncontrolled-values.demo.d.ts +6 -0
  170. package/dist/runtime/demos/controlled-uncontrolled-values.demo.js +77 -0
  171. package/dist/runtime/demos/controlled-uncontrolled-values.demo.js.map +1 -0
  172. package/dist/runtime/demos/keyed-list.demo.d.ts +6 -0
  173. package/dist/runtime/demos/keyed-list.demo.js +62 -0
  174. package/dist/runtime/demos/keyed-list.demo.js.map +1 -0
  175. package/dist/runtime/demos/readme.demo.d.ts +5 -0
  176. package/dist/runtime/demos/readme.demo.js +396 -0
  177. package/dist/runtime/demos/readme.demo.js.map +1 -0
  178. package/dist/runtime/diff-dom.js +194 -21
  179. package/dist/runtime/diff-dom.js.map +1 -1
  180. package/dist/runtime/dom.d.ts +16 -9
  181. package/dist/runtime/frame.d.ts +7 -0
  182. package/dist/runtime/frame.js +176 -41
  183. package/dist/runtime/frame.js.map +1 -1
  184. package/dist/runtime/jsx.d.ts +3 -4
  185. package/dist/runtime/jsx.js.map +1 -1
  186. package/dist/runtime/mixins/link-mixin.d.ts +5 -1
  187. package/dist/runtime/mixins/link-mixin.js.map +1 -1
  188. package/dist/runtime/mixins/mixin.d.ts +2 -1
  189. package/dist/runtime/mixins/mixin.js.map +1 -1
  190. package/dist/runtime/mixins/ref-mixin.d.ts +2 -1
  191. package/dist/runtime/mixins/ref-mixin.js.map +1 -1
  192. package/dist/runtime/reconcile.d.ts +1 -1
  193. package/dist/runtime/reconcile.js +19 -5
  194. package/dist/runtime/reconcile.js.map +1 -1
  195. package/dist/runtime/scheduler.d.ts +10 -10
  196. package/dist/runtime/scheduler.js +1 -1
  197. package/dist/runtime/scheduler.js.map +1 -1
  198. package/dist/runtime/svg-attributes.js +1 -0
  199. package/dist/runtime/svg-attributes.js.map +1 -1
  200. package/dist/runtime/to-vnode.js +2 -2
  201. package/dist/runtime/to-vnode.js.map +1 -1
  202. package/dist/runtime/vnode.d.ts +6 -1
  203. package/dist/runtime/vnode.js +4 -0
  204. package/dist/runtime/vnode.js.map +1 -1
  205. package/dist/server/stream.d.ts +2 -0
  206. package/dist/server/stream.js +82 -11
  207. package/dist/server/stream.js.map +1 -1
  208. package/dist/style/css-mixin.d.ts +2 -1
  209. package/dist/style/css-mixin.js.map +1 -1
  210. package/dist/style/index.d.ts +1 -1
  211. package/dist/style/style.js +27 -10
  212. package/dist/style/style.js.map +1 -1
  213. package/dist/style/stylesheet.d.ts +13 -13
  214. package/dist/style/stylesheet.js.map +1 -1
  215. package/dist/theme/contract.d.ts +24 -222
  216. package/dist/theme/contract.js +2 -2
  217. package/dist/theme/contract.js.map +1 -1
  218. package/dist/theme/glyph-contract.d.ts +1 -3
  219. package/dist/theme/glyph-contract.js.map +1 -1
  220. package/dist/theme/presets/rmx-01/index.d.ts +4 -2
  221. package/dist/theme/presets/rmx-01/index.js.map +1 -1
  222. package/dist/utils/scroll-lock.d.ts +3 -3
  223. package/dist/utils/scroll-lock.js.map +1 -1
  224. package/package.json +4 -4
  225. package/src/animation/README.md +7 -5
  226. package/src/animation/demos/animation.demo.tsx +194 -0
  227. package/src/animation/demos/aspect-ratio.tsx +120 -0
  228. package/src/animation/demos/bouncy-switch.tsx +47 -0
  229. package/src/animation/demos/color-interpolation.tsx +73 -0
  230. package/src/animation/demos/cube.tsx +116 -0
  231. package/src/animation/demos/default-animate.tsx +129 -0
  232. package/src/animation/demos/drag-release.ts +118 -0
  233. package/src/animation/demos/enter.tsx +22 -0
  234. package/src/animation/demos/exit.tsx +79 -0
  235. package/src/animation/demos/flip-toggle.tsx +46 -0
  236. package/src/animation/demos/hold-to-confirm.tsx +317 -0
  237. package/src/animation/demos/html-content.tsx +41 -0
  238. package/src/animation/demos/interruptible-keyframes.tsx +62 -0
  239. package/src/animation/demos/keyframes.tsx +29 -0
  240. package/src/animation/demos/material-ripple.tsx +158 -0
  241. package/src/animation/demos/mixin-presence-list.tsx +121 -0
  242. package/src/animation/demos/mixin-reclaim.tsx +125 -0
  243. package/src/animation/demos/multi-state-badge.tsx +426 -0
  244. package/src/animation/demos/press.tsx +67 -0
  245. package/src/animation/demos/reordering.tsx +65 -0
  246. package/src/animation/demos/rolling-square.tsx +57 -0
  247. package/src/animation/demos/rotate.tsx +21 -0
  248. package/src/animation/demos/shared-layout.tsx +211 -0
  249. package/src/animation/demos/spring.demo.tsx +385 -0
  250. package/src/animation/demos/transition-options.tsx +24 -0
  251. package/src/animation/spring.ts +43 -9
  252. package/src/components/accordion/accordion.tsx +7 -5
  253. package/src/components/accordion/demos/card.demo.tsx +1 -0
  254. package/src/components/accordion/demos/multiple.demo.tsx +1 -0
  255. package/src/components/accordion/demos/overview.demo.tsx +1 -0
  256. package/src/components/anchor/README.md +11 -3
  257. package/src/components/anchor/anchor.ts +54 -13
  258. package/src/components/breadcrumbs/{demos/basic.demo.tsx → basic.demo.tsx} +1 -0
  259. package/src/components/breadcrumbs/breadcrumbs.tsx +1 -1
  260. package/src/components/breadcrumbs/{demos/separator.demo.tsx → separator.demo.tsx} +1 -0
  261. package/src/components/button/button.tsx +30 -18
  262. package/src/components/button/demos/aliases.demo.tsx +1 -0
  263. package/src/components/button/demos/basic.demo.tsx +1 -0
  264. package/src/components/button/demos/states.demo.tsx +1 -0
  265. package/src/components/combobox/README.md +1 -1
  266. package/src/components/combobox/combobox.tsx +30 -12
  267. package/src/components/combobox/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  268. package/src/components/glyph/glyph.tsx +2 -2
  269. package/src/components/listbox/listbox.ts +88 -83
  270. package/src/components/listbox/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  271. package/src/components/menu/README.md +26 -2
  272. package/src/components/menu/demos/bubbling.demo.tsx +1 -0
  273. package/src/components/menu/demos/context-trigger.demo.tsx +133 -0
  274. package/src/components/menu/demos/overview.demo.tsx +1 -0
  275. package/src/components/menu/menu.tsx +100 -16
  276. package/src/components/popover/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  277. package/src/components/popover/popover.ts +101 -81
  278. package/src/components/select/{demos/deconstructed.demo.tsx → deconstructed.demo.tsx} +1 -0
  279. package/src/components/select/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  280. package/src/components/select/select.tsx +27 -10
  281. package/src/index.ts +1 -0
  282. package/src/runtime/client-entries.ts +1 -5
  283. package/src/runtime/component.ts +37 -15
  284. package/src/runtime/core/attributes.ts +21 -0
  285. package/src/runtime/core/props.ts +6 -4
  286. package/src/runtime/demos/basic.demo.tsx +21 -0
  287. package/src/runtime/demos/controlled-uncontrolled-values.demo.tsx +177 -0
  288. package/src/runtime/demos/keyed-list.demo.tsx +94 -0
  289. package/src/runtime/demos/readme.demo.tsx +702 -0
  290. package/src/runtime/diff-dom.ts +270 -20
  291. package/src/runtime/dom.ts +16 -9
  292. package/src/runtime/frame.ts +218 -41
  293. package/src/runtime/jsx.ts +7 -11
  294. package/src/runtime/mixins/link-mixin.ts +88 -86
  295. package/src/runtime/mixins/mixin.ts +9 -1
  296. package/src/runtime/mixins/ref-mixin.ts +4 -5
  297. package/src/runtime/reconcile.ts +23 -14
  298. package/src/runtime/scheduler.ts +20 -20
  299. package/src/runtime/svg-attributes.ts +1 -0
  300. package/src/runtime/to-vnode.ts +2 -2
  301. package/src/runtime/vnode.ts +10 -0
  302. package/src/server/README.md +4 -2
  303. package/src/server/stream.ts +84 -10
  304. package/src/style/css-mixin.ts +6 -1
  305. package/src/style/index.ts +1 -2
  306. package/src/style/style.ts +29 -10
  307. package/src/style/stylesheet.ts +14 -2
  308. package/src/theme/contract.ts +37 -5
  309. package/src/theme/glyph-contract.ts +3 -1
  310. package/src/theme/presets/rmx-01/index.ts +4 -2
  311. package/src/utils/scroll-lock.ts +7 -3
  312. package/dist/components/breadcrumbs/demos/basic.demo.js.map +0 -1
  313. package/dist/components/breadcrumbs/demos/separator.demo.js.map +0 -1
  314. package/dist/components/combobox/demos/overview.demo.js.map +0 -1
  315. package/dist/components/listbox/demos/overview.demo.js.map +0 -1
  316. package/dist/components/popover/demos/overview.demo.js.map +0 -1
  317. package/dist/components/select/demos/deconstructed.demo.js.map +0 -1
  318. package/dist/components/select/demos/overview.demo.js.map +0 -1
@@ -230,7 +230,7 @@ function isMultipleProps(props: AccordionProps | null): props is AccordionMultip
230
230
  return props?.type === 'multiple'
231
231
  }
232
232
 
233
- function AccordionImpl(handle: Handle<AccordionProps, AccordionContext>) {
233
+ function AccordionImpl(handle: Handle<AccordionProps, AccordionContext>): () => RemixNode {
234
234
  let rootNode: HTMLElement | null = null
235
235
  let registeredItems: RegisteredItem[] = []
236
236
  let uncontrolledSingleValue: string | null = null
@@ -418,13 +418,15 @@ function AccordionImpl(handle: Handle<AccordionProps, AccordionContext>) {
418
418
  export function onAccordionChange<target extends HTMLElement>(
419
419
  handler: AccordionChangeHandler<target>,
420
420
  captureBoolean?: boolean,
421
- ) {
421
+ ): ReturnType<typeof on<target, typeof ACCORDION_CHANGE_EVENT>> {
422
422
  return on(ACCORDION_CHANGE_EVENT, handler, captureBoolean)
423
423
  }
424
424
 
425
425
  export const Accordion = AccordionImpl
426
426
 
427
- export function AccordionItem(handle: Handle<AccordionItemProps, AccordionItemContext>) {
427
+ export function AccordionItem(
428
+ handle: Handle<AccordionItemProps, AccordionItemContext>,
429
+ ): () => RemixNode {
428
430
  let triggerNode: HTMLButtonElement | null = null
429
431
  let triggerId = `${handle.id}-trigger`
430
432
  let panelId = `${handle.id}-panel`
@@ -468,7 +470,7 @@ export function AccordionItem(handle: Handle<AccordionItemProps, AccordionItemCo
468
470
  }
469
471
  }
470
472
 
471
- export function AccordionTrigger(handle: Handle<AccordionTriggerProps>) {
473
+ export function AccordionTrigger(handle: Handle<AccordionTriggerProps>): () => RemixNode {
472
474
  return () => {
473
475
  let accordion = handle.context.get(Accordion)
474
476
  let item = handle.context.get(AccordionItem)
@@ -539,7 +541,7 @@ export function AccordionTrigger(handle: Handle<AccordionTriggerProps>) {
539
541
  }
540
542
  }
541
543
 
542
- export function AccordionContent(handle: Handle<AccordionContentProps>) {
544
+ export function AccordionContent(handle: Handle<AccordionContentProps>): () => RemixNode {
543
545
  return () => {
544
546
  let item = handle.context.get(AccordionItem)
545
547
  let { children, mix, ...panelProps } = handle.props
@@ -10,6 +10,7 @@ import { theme } from '@remix-run/ui/theme'
10
10
  /**
11
11
  * @name Accordion in a Card
12
12
  * @description The Accordion can sit inside a card when the surrounding surface needs stronger grouping.
13
+ * @layout center
13
14
  * @order 3
14
15
  */
15
16
  export default function Example() {
@@ -11,6 +11,7 @@ import { theme } from '@remix-run/ui/theme'
11
11
  * @order 2
12
12
  * @name Accordion Multiple Open
13
13
  * @description Multiple mode allows several sections to stay open simultaneously, useful for operational checklists and dashboards.
14
+ * @layout center
14
15
  */
15
16
  export default function Example() {
16
17
  return () => (
@@ -10,6 +10,7 @@ import { theme } from '@remix-run/ui/theme'
10
10
  /**
11
11
  * @name Accordion Overview
12
12
  * @description A single-open disclosure list that keeps settings, billing, or notification rules in one calm section.
13
+ * @layout center
13
14
  * @order 1
14
15
  */
15
16
  export default function Example() {
@@ -1,6 +1,6 @@
1
1
  # anchor
2
2
 
3
- `anchor` positions a floating element against an anchor element and keeps it constrained to the viewport. Use it for custom floating surfaces that need placement, flipping, offsets, and optional relative alignment.
3
+ `anchor` positions a floating element against an anchor element or viewport coordinates and keeps it constrained to the viewport. Use it for custom floating surfaces that need placement, flipping, offsets, and optional relative alignment.
4
4
 
5
5
  ## Usage
6
6
 
@@ -49,11 +49,19 @@ popover.addEventListener('beforetoggle', (event) => {
49
49
  })
50
50
  ```
51
51
 
52
+ Anchor to coordinates when the surface should open at a pointer location.
53
+
54
+ ```tsx
55
+ let cleanup = anchor(popover, { x: event.clientX, y: event.clientY }, { placement: 'bottom-start' })
56
+ ```
57
+
52
58
  ## `anchor.*`
53
59
 
54
- - `anchor(floatingElement, anchorElement, options)`: positions `floatingElement` against `anchorElement`, starts animation-frame polling for geometry changes, and returns a cleanup function.
60
+ - `anchor(floatingElement, anchorTarget, options)`: positions `floatingElement` against an element or coordinate target, starts animation-frame polling for geometry changes, and returns a cleanup function.
55
61
  - `AnchorOptions`: placement, inset, relative alignment, and offset options.
62
+ - `AnchorPoint`: viewport coordinate target with `x`, `y`, and optional `width`/`height`.
56
63
  - `AnchorPlacement`: exported placement names for the main sides and top/bottom start/end alignment.
64
+ - `AnchorTarget`: an `HTMLElement` or `AnchorPoint`.
57
65
 
58
66
  ## Placements
59
67
 
@@ -149,5 +157,5 @@ anchor(listbox, trigger, {
149
157
  - Oversized inset surfaces with `relativeTo` preserve alignment by scrolling the nearest scrollable descendant when possible.
150
158
  - `offset`, `offsetX`, and `offsetY` may be numbers or functions that receive the floating element.
151
159
  - `relativeTo` lets a surface align to an inner element, which is useful for selected options inside popovers.
152
- - `anchor` polls on animation frames for anchor or floating geometry changes and repositions when either changes.
160
+ - `anchor` polls on animation frames for anchor target or floating geometry changes and repositions when either changes.
153
161
  - The returned cleanup function cancels animation-frame polling.
@@ -24,6 +24,15 @@ export type AnchorPlacement =
24
24
 
25
25
  type AnchorOffsetValue = number | ((floating: HTMLElement) => number)
26
26
 
27
+ export interface AnchorPoint {
28
+ height?: number
29
+ width?: number
30
+ x: number
31
+ y: number
32
+ }
33
+
34
+ export type AnchorTarget = HTMLElement | AnchorPoint
35
+
27
36
  export type AnchorOptions = {
28
37
  placement?: ExtendedAnchorPlacement
29
38
  inset?: boolean
@@ -125,6 +134,39 @@ function resolveOffsetValue(offset: AnchorOffsetValue | undefined, floating: HTM
125
134
  return offset ?? 0
126
135
  }
127
136
 
137
+ function isAnchorPoint(value: unknown): value is AnchorPoint {
138
+ if (typeof value !== 'object' || value === null) {
139
+ return false
140
+ }
141
+
142
+ if (!('x' in value) || !('y' in value)) {
143
+ return false
144
+ }
145
+
146
+ let width = 'width' in value ? value.width : undefined
147
+ let height = 'height' in value ? value.height : undefined
148
+
149
+ return (
150
+ typeof value.x === 'number' &&
151
+ typeof value.y === 'number' &&
152
+ (width === undefined || typeof width === 'number') &&
153
+ (height === undefined || typeof height === 'number')
154
+ )
155
+ }
156
+
157
+ function readAnchorRect(anchorTarget: AnchorTarget) {
158
+ if (anchorTarget instanceof HTMLElement) {
159
+ return anchorTarget.getBoundingClientRect()
160
+ }
161
+
162
+ return new DOMRect(
163
+ anchorTarget.x,
164
+ anchorTarget.y,
165
+ anchorTarget.width ?? 0,
166
+ anchorTarget.height ?? 0,
167
+ )
168
+ }
169
+
128
170
  function calculatePosition(
129
171
  placement: ExtendedAnchorPlacement,
130
172
  anchorRect: DOMRect,
@@ -468,7 +510,7 @@ function getFlippedPlacement(
468
510
  placementHeight: number,
469
511
  collisionWidth: number,
470
512
  collisionHeight: number,
471
- anchor: HTMLElement,
513
+ anchorIsAbsolutePositioned: boolean,
472
514
  inset: boolean,
473
515
  offset: number,
474
516
  offsetX: number,
@@ -476,11 +518,9 @@ function getFlippedPlacement(
476
518
  relativeOffsetX?: number | null,
477
519
  relativeOffsetY?: number | null,
478
520
  ) {
479
- let computedStyle = getComputedStyle(anchor)
480
- let isAbsolutePositioned = computedStyle.position !== 'fixed'
481
521
  let hasScroll = window.scrollY > 0
482
522
 
483
- if (placement.startsWith('top') && isAbsolutePositioned && hasScroll) {
523
+ if (placement.startsWith('top') && anchorIsAbsolutePositioned && hasScroll) {
484
524
  if (anchorRect.top - placementHeight < 0) {
485
525
  return placement
486
526
  }
@@ -641,9 +681,9 @@ function solveRelativeAlignmentAxis(options: {
641
681
 
642
682
  export function anchor(
643
683
  floating: HTMLElement,
644
- anchorElement: HTMLElement,
684
+ anchorTarget: AnchorTarget,
645
685
  options: AnchorOptions = {},
646
- ) {
686
+ ): () => void {
647
687
  let lastAnchorRect: DOMRect
648
688
  let lastFloatingDimensions: FloatingDimensions
649
689
 
@@ -651,8 +691,8 @@ export function anchor(
651
691
  throw new TypeError('anchor() expected a floating HTMLElement')
652
692
  }
653
693
 
654
- if (!(anchorElement instanceof HTMLElement)) {
655
- throw new TypeError('anchor() expected an anchor HTMLElement')
694
+ if (!(anchorTarget instanceof HTMLElement) && !isAnchorPoint(anchorTarget)) {
695
+ throw new TypeError('anchor() expected an anchor HTMLElement or coordinates')
656
696
  }
657
697
 
658
698
  let {
@@ -664,12 +704,13 @@ export function anchor(
664
704
  offsetY: rawOffsetY = 0,
665
705
  } = options
666
706
 
667
- let isFixed =
668
- floating.hasAttribute('popover') || getComputedStyle(anchorElement).position === 'fixed'
707
+ let anchorIsAbsolutePositioned =
708
+ anchorTarget instanceof HTMLElement && getComputedStyle(anchorTarget).position !== 'fixed'
709
+ let isFixed = floating.hasAttribute('popover') || !anchorIsAbsolutePositioned
669
710
  let animationFrameId = 0
670
711
 
671
712
  function updatePosition(
672
- anchorRect = anchorElement.getBoundingClientRect(),
713
+ anchorRect = readAnchorRect(anchorTarget),
673
714
  naturalDimensions = getFloatingDimensions(floating, relativeTo, {
674
715
  ignoreInlineMaxSize: true,
675
716
  }),
@@ -695,7 +736,7 @@ export function anchor(
695
736
  placementHeight,
696
737
  naturalDimensions.width,
697
738
  naturalDimensions.height,
698
- anchorElement,
739
+ anchorIsAbsolutePositioned,
699
740
  inset,
700
741
  offset,
701
742
  offsetX,
@@ -872,7 +913,7 @@ export function anchor(
872
913
  updatePosition()
873
914
 
874
915
  function pollForPositionChanges() {
875
- let currentRect = anchorElement.getBoundingClientRect()
916
+ let currentRect = readAnchorRect(anchorTarget)
876
917
  let currentDimensions = getFloatingDimensions(floating, relativeTo)
877
918
  if (
878
919
  hasRectChanged(currentRect, lastAnchorRect) ||
@@ -3,6 +3,7 @@ import { Breadcrumbs } from '@remix-run/ui/breadcrumbs'
3
3
  /**
4
4
  * @name Breadcrumbs Basic
5
5
  * @description A basic breadcrumb trail linking back through the page hierarchy.
6
+ * @layout center
6
7
  */
7
8
  export default function Example() {
8
9
  return () => (
@@ -72,7 +72,7 @@ const textCss = css({
72
72
  whiteSpace: 'nowrap',
73
73
  })
74
74
 
75
- export function Breadcrumbs(handle: Handle<BreadcrumbsProps>) {
75
+ export function Breadcrumbs(handle: Handle<BreadcrumbsProps>): () => RemixNode {
76
76
  return () => {
77
77
  let { 'aria-label': ariaLabel, items, separator, mix, ...navProps } = handle.props
78
78
  let currentIndex = items.findIndex((item) => item.current)
@@ -3,6 +3,7 @@ import { Breadcrumbs } from '@remix-run/ui/breadcrumbs'
3
3
  /**
4
4
  * @name Breadcrumbs with Separator
5
5
  * @description Pass a custom separator string to override the default chevron icon.
6
+ * @layout center
6
7
  */
7
8
  export default function Example() {
8
9
  return () => (
@@ -1,5 +1,13 @@
1
1
  import { attrs, createElement, createMixin, css } from '@remix-run/ui'
2
- import type { CSSMixinDescriptor, ElementProps, Handle, Props, RemixNode } from '@remix-run/ui'
2
+ import type {
3
+ CSSMixinDescriptor,
4
+ ElementProps,
5
+ Handle,
6
+ MixinDescriptor,
7
+ MixinFactory,
8
+ Props,
9
+ RemixNode,
10
+ } from '@remix-run/ui'
3
11
 
4
12
  import { theme } from '../../theme/theme.ts'
5
13
 
@@ -74,22 +82,26 @@ const buttonIconCss: CSSMixinDescriptor = css({
74
82
  },
75
83
  })
76
84
 
77
- const buttonIconAttrsCss = attrs({ 'aria-hidden': true })
85
+ const buttonIconAttrsCss: MixinDescriptor<Element, [Partial<ElementProps>], ElementProps> = attrs({
86
+ 'aria-hidden': true,
87
+ })
78
88
 
79
- const buttonDefaultsMixin = createMixin<Element, [], ElementProps>(
80
- (handle, hostType) => (props) => {
81
- if (hostType !== 'button' || props.type !== undefined) {
82
- return handle.element
83
- }
89
+ const buttonDefaultsMixin: MixinFactory<Element, [], ElementProps> = createMixin<
90
+ Element,
91
+ [],
92
+ ElementProps
93
+ >((handle, hostType) => (props) => {
94
+ if (hostType !== 'button' || props.type !== undefined) {
95
+ return handle.element
96
+ }
84
97
 
85
- return createElement(handle.element as unknown as string, {
86
- ...props,
87
- type: 'button',
88
- })
89
- },
90
- )
98
+ return createElement(handle.element as unknown as string, {
99
+ ...props,
100
+ type: 'button',
101
+ })
102
+ })
91
103
 
92
- const buttonDefaultsCss = buttonDefaultsMixin()
104
+ const buttonDefaultsCss: MixinDescriptor<Element, [], ElementProps> = buttonDefaultsMixin()
93
105
 
94
106
  /**
95
107
  * Base button styling with the default `type="button"` behavior for `<button>`
@@ -121,7 +133,7 @@ export const labelStyle = buttonLabelCss
121
133
  *
122
134
  * @category mixin
123
135
  */
124
- export const primaryStyle = createButtonCss(theme.colors.action.primary)
136
+ export const primaryStyle: CSSMixinDescriptor = createButtonCss(theme.colors.action.primary)
125
137
 
126
138
  /**
127
139
  * Secondary visual treatment for buttons. Combine with {@link baseStyle} when
@@ -129,7 +141,7 @@ export const primaryStyle = createButtonCss(theme.colors.action.primary)
129
141
  *
130
142
  * @category mixin
131
143
  */
132
- export const secondaryStyle = createButtonCss(theme.colors.action.secondary)
144
+ export const secondaryStyle: CSSMixinDescriptor = createButtonCss(theme.colors.action.secondary)
133
145
 
134
146
  /**
135
147
  * Ghost visual treatment for buttons — transparent background with a hover
@@ -146,7 +158,7 @@ export const ghostStyle = ghostButtonToneCss
146
158
  *
147
159
  * @category mixin
148
160
  */
149
- export const dangerStyle = createButtonCss(theme.colors.action.danger)
161
+ export const dangerStyle: CSSMixinDescriptor = createButtonCss(theme.colors.action.danger)
150
162
 
151
163
  const toneStyleByTone = {
152
164
  primary: primaryStyle,
@@ -203,7 +215,7 @@ export type ButtonProps = Omit<Props<'button'>, 'children'> & {
203
215
  * </Button>
204
216
  * ```
205
217
  */
206
- export function Button(handle: Handle<ButtonProps>) {
218
+ export function Button(handle: Handle<ButtonProps>): () => RemixNode {
207
219
  return () => {
208
220
  let { children, endIcon, mix, startIcon, tone = 'secondary', ...buttonProps } = handle.props
209
221
 
@@ -5,6 +5,7 @@ import { theme } from '@remix-run/ui/theme'
5
5
  /**
6
6
  * @name Button Component
7
7
  * @description The Button component wraps the low-level style primitives and accepts a tone prop for quick theming.
8
+ * @layout center
8
9
  * @order 2
9
10
  */
10
11
  export default function Example() {
@@ -6,6 +6,7 @@ import { theme } from '@remix-run/ui/theme'
6
6
  /**
7
7
  * @name Basic Button
8
8
  * @description The default button contract supports both ordinary actions and link-shaped navigation.
9
+ * @layout center
9
10
  * @order 1
10
11
  */
11
12
  export default function Example() {
@@ -6,6 +6,7 @@ import { theme } from '@remix-run/ui/theme'
6
6
  /**
7
7
  * @name Button States
8
8
  * @description Buttons support normal, disabled, and busy (loading) states using aria attributes.
9
+ * @layout center
9
10
  * @order 3
10
11
  */
11
12
  export default function Example() {
@@ -8,7 +8,7 @@ Use it when the user should type draft text, filter a popup list, and still comm
8
8
 
9
9
  ```tsx
10
10
  import { css, type Handle } from 'remix/ui'
11
- import { Combobox, ComboboxOption, onComboboxChange } from 'remix/ui'
11
+ import { Combobox, ComboboxOption, onComboboxChange } from 'remix/ui/combobox'
12
12
 
13
13
  let airports = [
14
14
  {
@@ -10,6 +10,7 @@ import {
10
10
  type ElementProps,
11
11
  type Handle,
12
12
  type MixinHandle,
13
+ type MixinFactory,
13
14
  type Props,
14
15
  type RemixNode,
15
16
  } from '@remix-run/ui'
@@ -213,7 +214,9 @@ function getOptionSearchValue(option: {
213
214
  return option.searchValue ?? option.textValue ?? option.label
214
215
  }
215
216
 
216
- function ComboboxProvider(handle: Handle<ComboboxContextProps, ComboboxContextValue>) {
217
+ function ComboboxProvider(
218
+ handle: Handle<ComboboxContextProps, ComboboxContextValue>,
219
+ ): () => RemixNode {
217
220
  let inputRef: HTMLInputElement | undefined
218
221
  let listboxRef: listbox.ListboxRef | undefined
219
222
  let surfaceRef: HTMLElement | undefined
@@ -807,7 +810,11 @@ function getComboboxContext(handle: Handle | MixinHandle) {
807
810
  return handle.context.get(ComboboxProvider)
808
811
  }
809
812
 
810
- const inputMixin = createMixin<HTMLInputElement, [], ElementProps>((handle) => {
813
+ const inputMixin: MixinFactory<HTMLInputElement, [], ElementProps> = createMixin<
814
+ HTMLInputElement,
815
+ [],
816
+ ElementProps
817
+ >((handle) => {
811
818
  let context = getComboboxContext(handle)
812
819
 
813
820
  return (props) => [
@@ -882,7 +889,11 @@ const inputMixin = createMixin<HTMLInputElement, [], ElementProps>((handle) => {
882
889
  ]
883
890
  })
884
891
 
885
- const popoverMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
892
+ const popoverMixin: MixinFactory<HTMLElement, [], ElementProps> = createMixin<
893
+ HTMLElement,
894
+ [],
895
+ ElementProps
896
+ >((handle) => {
886
897
  let context = getComboboxContext(handle)
887
898
 
888
899
  return () => [
@@ -908,12 +919,20 @@ const popoverMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
908
919
  ]
909
920
  })
910
921
 
911
- const listMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
922
+ const listMixin: MixinFactory<HTMLElement, [], ElementProps> = createMixin<
923
+ HTMLElement,
924
+ [],
925
+ ElementProps
926
+ >((handle) => {
912
927
  let context = getComboboxContext(handle)
913
928
  return () => [attrs({ id: context.listId }), listbox.list()]
914
929
  })
915
930
 
916
- const hiddenInputMixin = createMixin<HTMLInputElement, [], ElementProps>((handle) => {
931
+ const hiddenInputMixin: MixinFactory<HTMLInputElement, [], ElementProps> = createMixin<
932
+ HTMLInputElement,
933
+ [],
934
+ ElementProps
935
+ >((handle) => {
917
936
  let context = getComboboxContext(handle)
918
937
  return () =>
919
938
  attrs({
@@ -924,8 +943,8 @@ const hiddenInputMixin = createMixin<HTMLInputElement, [], ElementProps>((handle
924
943
  })
925
944
  })
926
945
 
927
- const optionMixin = createMixin<HTMLElement, [options: ComboboxOptionOptions], ElementProps>(
928
- (handle) => {
946
+ const optionMixin: MixinFactory<HTMLElement, [options: ComboboxOptionOptions], ElementProps> =
947
+ createMixin<HTMLElement, [options: ComboboxOptionOptions], ElementProps>((handle) => {
929
948
  let context = getComboboxContext(handle)
930
949
 
931
950
  return (options) => {
@@ -946,8 +965,7 @@ const optionMixin = createMixin<HTMLElement, [options: ComboboxOptionOptions], E
946
965
  }),
947
966
  ]
948
967
  }
949
- },
950
- )
968
+ })
951
969
 
952
970
  export const Context = ComboboxProvider
953
971
  export const hiddenInput = hiddenInputMixin
@@ -968,11 +986,11 @@ const combobox = {
968
986
  export function onComboboxChange<target extends HTMLElement>(
969
987
  handler: ComboboxChangeHandler<target>,
970
988
  captureBoolean?: boolean,
971
- ) {
989
+ ): ReturnType<typeof on<target, typeof COMBOBOX_CHANGE_EVENT>> {
972
990
  return on(COMBOBOX_CHANGE_EVENT, handler, captureBoolean)
973
991
  }
974
992
 
975
- export function Combobox(handle: Handle<ComboboxProps>) {
993
+ export function Combobox(handle: Handle<ComboboxProps>): () => RemixNode {
976
994
  return () => {
977
995
  let { children, defaultValue, disabled, inputId, name, placeholder, ...divProps } = handle.props
978
996
 
@@ -995,7 +1013,7 @@ export function Combobox(handle: Handle<ComboboxProps>) {
995
1013
  }
996
1014
  }
997
1015
 
998
- export function ComboboxOption(handle: Handle<ComboboxOptionProps>) {
1016
+ export function ComboboxOption(handle: Handle<ComboboxOptionProps>): () => RemixNode {
999
1017
  return () => {
1000
1018
  let { children, disabled, label, mix, searchValue, value, ...divProps } = handle.props
1001
1019
 
@@ -5,6 +5,7 @@ import { theme } from '@remix-run/ui/theme'
5
5
  /**
6
6
  * @name Combobox Overview
7
7
  * @description A searchable combobox with keyboard navigation. Try typing airport names or codes like ord, lax, or jfk.
8
+ * @layout center
8
9
  */
9
10
  export default function Example(handle: Handle) {
10
11
  let value: string | null = null
@@ -31,7 +31,7 @@ export function createGlyphSheet(values: GlyphValues): GlyphSheetComponent {
31
31
  >,
32
32
  )
33
33
 
34
- function GlyphSheet(handle: Handle<GlyphSheetProps>) {
34
+ function GlyphSheet(handle: Handle<GlyphSheetProps>): () => RemixElement {
35
35
  return () => {
36
36
  let { style, ...svgProps } = handle.props
37
37
  let hiddenStyle = {
@@ -70,7 +70,7 @@ export function createGlyphSheet(values: GlyphValues): GlyphSheetComponent {
70
70
  })
71
71
  }
72
72
 
73
- export function Glyph(handle: Handle<GlyphProps>) {
73
+ export function Glyph(handle: Handle<GlyphProps>): () => RemixElement {
74
74
  return () => {
75
75
  let { fill, name, ...svgProps } = handle.props
76
76
  let glyphId = glyphContract[name].id