@ui-sdk/web 0.0.0-stage → 0.1.0-beta.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/LICENSE +202 -0
  2. package/README.md +10 -2
  3. package/browser/web/accordion.js +1 -0
  4. package/browser/web/alert-dialog.js +1 -0
  5. package/browser/web/autocomplete.js +1 -0
  6. package/browser/web/avatar.js +1 -0
  7. package/browser/web/button.js +1 -0
  8. package/browser/web/calendar.js +1 -0
  9. package/browser/web/carousel.js +1 -0
  10. package/browser/web/checkbox.js +1 -0
  11. package/browser/web/chunks/0eRGibL6.js +1 -0
  12. package/browser/web/chunks/2bIEUO6_.js +1 -0
  13. package/browser/web/chunks/5A9w2ULz.js +1 -0
  14. package/browser/web/chunks/5RSyydjV.js +1 -0
  15. package/browser/web/chunks/9pWTy2wl.js +1 -0
  16. package/browser/web/chunks/B0f2j6zz.js +1 -0
  17. package/browser/web/chunks/B1rux6br.js +1 -0
  18. package/browser/web/chunks/B3-APaNf.js +1 -0
  19. package/browser/web/chunks/B7EgSgjQ.js +1 -0
  20. package/browser/web/chunks/B8QcOzJU.js +1 -0
  21. package/browser/web/chunks/BFueEkwm.js +3 -0
  22. package/browser/web/chunks/BJj3oS29.js +1 -0
  23. package/browser/web/chunks/BNSvTCpr.js +1 -0
  24. package/browser/web/chunks/BPD0kAZB.js +1 -0
  25. package/browser/web/chunks/BQeduZqT.js +1 -0
  26. package/browser/web/chunks/B_CVFyiz.js +1 -0
  27. package/browser/web/chunks/B_ns1YHO.js +1 -0
  28. package/browser/web/chunks/BcwkVTsR.js +1 -0
  29. package/browser/web/chunks/BeaQ6TYM.js +1 -0
  30. package/browser/web/chunks/BgBB013u.js +1 -0
  31. package/browser/web/chunks/BgsKhHDm.js +1 -0
  32. package/browser/web/chunks/BjMcbuhi.js +9 -0
  33. package/browser/web/chunks/Bjiy_F2N.js +1 -0
  34. package/browser/web/chunks/BqScCLwv.js +1 -0
  35. package/browser/web/chunks/BqX3Utqx.js +1 -0
  36. package/browser/web/chunks/BtVkQf3z.js +1 -0
  37. package/browser/web/chunks/By30h-7B.js +1 -0
  38. package/browser/web/chunks/C1zqYpy2.js +1 -0
  39. package/browser/web/chunks/C436juFf.js +1 -0
  40. package/browser/web/chunks/C4_bULs1.js +1 -0
  41. package/browser/web/chunks/C4omqJEz.js +1 -0
  42. package/browser/web/chunks/C7tt0T3n.js +1 -0
  43. package/browser/web/chunks/C8w7s5dg.js +1 -0
  44. package/browser/web/chunks/CElY4erO.js +1 -0
  45. package/browser/web/chunks/CFODo-m2.js +1 -0
  46. package/browser/web/chunks/CL4wjam9.js +1 -0
  47. package/browser/web/chunks/CMg-8UVf.js +1 -0
  48. package/browser/web/chunks/COWKfakj.js +1 -0
  49. package/browser/web/chunks/CQBU0qZ4.js +1 -0
  50. package/browser/web/chunks/CV6uABgc.js +1 -0
  51. package/browser/web/chunks/C_SWFpWQ.js +1 -0
  52. package/browser/web/chunks/C_Uanbrl.js +1 -0
  53. package/browser/web/chunks/Ce_CSanC.js +1 -0
  54. package/browser/web/chunks/Cg-ZmRhS.js +1 -0
  55. package/browser/web/chunks/Cgf2wPI_.js +1 -0
  56. package/browser/web/chunks/Cuep_3Vf.js +1 -0
  57. package/browser/web/chunks/D02PaHn6.js +1 -0
  58. package/browser/web/chunks/D5LiaI5R.js +1 -0
  59. package/browser/web/chunks/D9UgVYvF.js +1 -0
  60. package/browser/web/chunks/DC08tnkE.js +2 -0
  61. package/browser/web/chunks/DG8YGSvu.js +1 -0
  62. package/browser/web/chunks/DIQd0FYD.js +1 -0
  63. package/browser/web/chunks/DMLqacE8.js +1 -0
  64. package/browser/web/chunks/DSUI3yI-.js +1 -0
  65. package/browser/web/chunks/DT3Hb76X.js +1 -0
  66. package/browser/web/chunks/DY75g3-W.js +1 -0
  67. package/browser/web/chunks/DZpNQKZ9.js +1 -0
  68. package/browser/web/chunks/Db-W1wrC.js +1 -0
  69. package/browser/web/chunks/Dc1xK3Ii.js +1 -0
  70. package/browser/web/chunks/DdF6awFs.js +1 -0
  71. package/browser/web/chunks/DdaCLml8.js +1 -0
  72. package/browser/web/chunks/Dh8MLjQO.js +1 -0
  73. package/browser/web/chunks/DiuslJRy.js +1 -0
  74. package/browser/web/chunks/DjRcw3Y0.js +1 -0
  75. package/browser/web/chunks/Djdv-16J.js +1 -0
  76. package/browser/web/chunks/DnFXe_lF.js +1 -0
  77. package/browser/web/chunks/DneGv1oC.js +1 -0
  78. package/browser/web/chunks/DsVxP-p-.js +1 -0
  79. package/browser/web/chunks/DwDilCo2.js +1 -0
  80. package/browser/web/chunks/DwV48AXv.js +1 -0
  81. package/browser/web/chunks/F37aF4-L.js +1 -0
  82. package/browser/web/chunks/LyB6XIbN.js +1 -0
  83. package/browser/web/chunks/NKnbYGjC.js +1 -0
  84. package/browser/web/chunks/Vk-rVi16.js +1 -0
  85. package/browser/web/chunks/XsM0J1Ex.js +1 -0
  86. package/browser/web/chunks/asSN9iEp.js +1 -0
  87. package/browser/web/chunks/c-AjHfdF.js +1 -0
  88. package/browser/web/chunks/dR-12TPY.js +1 -0
  89. package/browser/web/chunks/hR4xM0EC.js +1 -0
  90. package/browser/web/chunks/hqOaBxAg.js +1 -0
  91. package/browser/web/chunks/kMKqqeG4.js +1 -0
  92. package/browser/web/chunks/kXboBcfi.js +1 -0
  93. package/browser/web/chunks/l4aQIg22.js +1 -0
  94. package/browser/web/chunks/tJC0JSU4.js +1 -0
  95. package/browser/web/chunks/wHdS_QYM.js +1 -0
  96. package/browser/web/chunks/yv8dTvNI.js +1 -0
  97. package/browser/web/chunks/yw9M3nfi.js +1 -0
  98. package/browser/web/collapsible.js +1 -0
  99. package/browser/web/color-picker.js +1 -0
  100. package/browser/web/combobox.js +2 -0
  101. package/browser/web/command.js +1 -0
  102. package/browser/web/core.js +5 -0
  103. package/browser/web/date-input.js +1 -0
  104. package/browser/web/date-picker.js +1 -0
  105. package/browser/web/dialog.js +1 -0
  106. package/browser/web/drawer.js +1 -0
  107. package/browser/web/field.js +1 -0
  108. package/browser/web/file-input.js +1 -0
  109. package/browser/web/hover-card.js +1 -0
  110. package/browser/web/image-cropper.js +1 -0
  111. package/browser/web/input.js +1 -0
  112. package/browser/web/label.js +1 -0
  113. package/browser/web/listbox.js +1 -0
  114. package/browser/web/menu.js +1 -0
  115. package/browser/web/menubar.js +1 -0
  116. package/browser/web/meter.js +1 -0
  117. package/browser/web/navigation-menu.js +1 -0
  118. package/browser/web/number-input.js +1 -0
  119. package/browser/web/otp-input.js +2 -0
  120. package/browser/web/overlays.js +1 -0
  121. package/browser/web/pagination.js +1 -0
  122. package/browser/web/password-input.js +1 -0
  123. package/browser/web/popover.js +1 -0
  124. package/browser/web/preferences.js +1 -0
  125. package/browser/web/progress.js +1 -0
  126. package/browser/web/radio.js +1 -0
  127. package/browser/web/rating.js +1 -0
  128. package/browser/web/resizable.js +1 -0
  129. package/browser/web/scroll-area.js +1 -0
  130. package/browser/web/segmented-control.js +1 -0
  131. package/browser/web/select.js +1 -0
  132. package/browser/web/separator.js +1 -0
  133. package/browser/web/slider.js +1 -0
  134. package/browser/web/switch.js +1 -0
  135. package/browser/web/tabs.js +1 -0
  136. package/browser/web/textarea.js +1 -0
  137. package/browser/web/time-input.js +1 -0
  138. package/browser/web/timeline.js +1 -0
  139. package/browser/web/toast.js +1 -0
  140. package/browser/web/toggle.js +1 -0
  141. package/browser/web/token-input.js +8 -0
  142. package/browser/web/toolbar.js +1 -0
  143. package/browser/web/tooltip.js +1 -0
  144. package/browser/web/tree.js +1 -0
  145. package/browser/web/trigger-menu.js +4 -0
  146. package/browser/web/waterfall.js +1 -0
  147. package/browser/web.css +3378 -0
  148. package/browser/web.js +3 -0
  149. package/custom-elements.json +32315 -0
  150. package/dist/accordion.d.ts +2 -0
  151. package/dist/accordion.js +57 -0
  152. package/dist/action.d.ts +16 -0
  153. package/dist/action.js +39 -0
  154. package/dist/agent.d.ts +2 -0
  155. package/dist/agent.js +7 -0
  156. package/dist/autocomplete.d.ts +2 -0
  157. package/dist/autocomplete.js +120 -0
  158. package/dist/autoload.d.ts +18 -0
  159. package/dist/autoload.js +63 -0
  160. package/dist/avatar.d.ts +2 -0
  161. package/dist/avatar.js +27 -0
  162. package/dist/browser-core.d.ts +2 -0
  163. package/dist/browser-core.js +8 -0
  164. package/dist/button.d.ts +2 -0
  165. package/dist/button.js +35 -0
  166. package/dist/calendar.d.ts +23 -0
  167. package/dist/calendar.js +429 -0
  168. package/dist/carousel.d.ts +2 -0
  169. package/dist/carousel.js +54 -0
  170. package/dist/checkbox.d.ts +4 -0
  171. package/dist/checkbox.js +75 -0
  172. package/dist/collapsible.d.ts +2 -0
  173. package/dist/collapsible.js +28 -0
  174. package/dist/color-picker.d.ts +2 -0
  175. package/dist/color-picker.js +64 -0
  176. package/dist/combobox.d.ts +2 -0
  177. package/dist/combobox.js +334 -0
  178. package/dist/command.d.ts +2 -0
  179. package/dist/command.js +122 -0
  180. package/dist/date-input.d.ts +3 -0
  181. package/dist/date-input.js +87 -0
  182. package/dist/date-picker.d.ts +2 -0
  183. package/dist/date-picker.js +174 -0
  184. package/dist/default-text.d.ts +3 -0
  185. package/dist/default-text.js +24 -0
  186. package/dist/define-element.d.ts +404 -0
  187. package/dist/define-element.js +1489 -0
  188. package/dist/dialog.d.ts +3 -0
  189. package/dist/dialog.js +94 -0
  190. package/dist/drawer.d.ts +2 -0
  191. package/dist/drawer.js +96 -0
  192. package/dist/entries/accordion.d.ts +3 -0
  193. package/dist/entries/accordion.js +7 -0
  194. package/dist/entries/alert-dialog.d.ts +3 -0
  195. package/dist/entries/alert-dialog.js +13 -0
  196. package/dist/entries/autocomplete.d.ts +3 -0
  197. package/dist/entries/autocomplete.js +7 -0
  198. package/dist/entries/avatar.d.ts +2 -0
  199. package/dist/entries/avatar.js +3 -0
  200. package/dist/entries/button.d.ts +2 -0
  201. package/dist/entries/button.js +3 -0
  202. package/dist/entries/calendar.d.ts +3 -0
  203. package/dist/entries/calendar.js +7 -0
  204. package/dist/entries/carousel.d.ts +3 -0
  205. package/dist/entries/carousel.js +7 -0
  206. package/dist/entries/checkbox.d.ts +3 -0
  207. package/dist/entries/checkbox.js +15 -0
  208. package/dist/entries/collapsible.d.ts +3 -0
  209. package/dist/entries/collapsible.js +7 -0
  210. package/dist/entries/color-picker.d.ts +3 -0
  211. package/dist/entries/color-picker.js +7 -0
  212. package/dist/entries/combobox.d.ts +3 -0
  213. package/dist/entries/combobox.js +7 -0
  214. package/dist/entries/command.d.ts +3 -0
  215. package/dist/entries/command.js +7 -0
  216. package/dist/entries/date-input.d.ts +3 -0
  217. package/dist/entries/date-input.js +7 -0
  218. package/dist/entries/date-picker.d.ts +3 -0
  219. package/dist/entries/date-picker.js +7 -0
  220. package/dist/entries/dialog.d.ts +3 -0
  221. package/dist/entries/dialog.js +7 -0
  222. package/dist/entries/drawer.d.ts +3 -0
  223. package/dist/entries/drawer.js +8 -0
  224. package/dist/entries/field.d.ts +2 -0
  225. package/dist/entries/field.js +3 -0
  226. package/dist/entries/file-input.d.ts +3 -0
  227. package/dist/entries/file-input.js +13 -0
  228. package/dist/entries/hover-card.d.ts +3 -0
  229. package/dist/entries/hover-card.js +7 -0
  230. package/dist/entries/image-cropper.d.ts +3 -0
  231. package/dist/entries/image-cropper.js +7 -0
  232. package/dist/entries/input.d.ts +3 -0
  233. package/dist/entries/input.js +8 -0
  234. package/dist/entries/label.d.ts +2 -0
  235. package/dist/entries/label.js +3 -0
  236. package/dist/entries/listbox.d.ts +3 -0
  237. package/dist/entries/listbox.js +7 -0
  238. package/dist/entries/menu.d.ts +3 -0
  239. package/dist/entries/menu.js +7 -0
  240. package/dist/entries/menubar.d.ts +3 -0
  241. package/dist/entries/menubar.js +7 -0
  242. package/dist/entries/meter.d.ts +2 -0
  243. package/dist/entries/meter.js +3 -0
  244. package/dist/entries/navigation-menu.d.ts +3 -0
  245. package/dist/entries/navigation-menu.js +7 -0
  246. package/dist/entries/number-input.d.ts +3 -0
  247. package/dist/entries/number-input.js +7 -0
  248. package/dist/entries/otp-input.d.ts +3 -0
  249. package/dist/entries/otp-input.js +7 -0
  250. package/dist/entries/overlays.d.ts +2 -0
  251. package/dist/entries/overlays.js +3 -0
  252. package/dist/entries/pagination.d.ts +3 -0
  253. package/dist/entries/pagination.js +11 -0
  254. package/dist/entries/password-input.d.ts +3 -0
  255. package/dist/entries/password-input.js +7 -0
  256. package/dist/entries/popover.d.ts +3 -0
  257. package/dist/entries/popover.js +7 -0
  258. package/dist/entries/preferences.d.ts +3 -0
  259. package/dist/entries/preferences.js +13 -0
  260. package/dist/entries/progress.d.ts +2 -0
  261. package/dist/entries/progress.js +3 -0
  262. package/dist/entries/radio.d.ts +3 -0
  263. package/dist/entries/radio.js +7 -0
  264. package/dist/entries/rating.d.ts +3 -0
  265. package/dist/entries/rating.js +8 -0
  266. package/dist/entries/resizable.d.ts +3 -0
  267. package/dist/entries/resizable.js +11 -0
  268. package/dist/entries/scroll-area.d.ts +3 -0
  269. package/dist/entries/scroll-area.js +7 -0
  270. package/dist/entries/segmented-control.d.ts +3 -0
  271. package/dist/entries/segmented-control.js +7 -0
  272. package/dist/entries/select.d.ts +3 -0
  273. package/dist/entries/select.js +7 -0
  274. package/dist/entries/separator.d.ts +2 -0
  275. package/dist/entries/separator.js +3 -0
  276. package/dist/entries/slider.d.ts +3 -0
  277. package/dist/entries/slider.js +7 -0
  278. package/dist/entries/switch.d.ts +3 -0
  279. package/dist/entries/switch.js +7 -0
  280. package/dist/entries/tabs.d.ts +3 -0
  281. package/dist/entries/tabs.js +7 -0
  282. package/dist/entries/textarea.d.ts +3 -0
  283. package/dist/entries/textarea.js +7 -0
  284. package/dist/entries/time-input.d.ts +3 -0
  285. package/dist/entries/time-input.js +7 -0
  286. package/dist/entries/timeline.d.ts +3 -0
  287. package/dist/entries/timeline.js +7 -0
  288. package/dist/entries/toast.d.ts +2 -0
  289. package/dist/entries/toast.js +3 -0
  290. package/dist/entries/toggle.d.ts +3 -0
  291. package/dist/entries/toggle.js +12 -0
  292. package/dist/entries/token-input.d.ts +3 -0
  293. package/dist/entries/token-input.js +17 -0
  294. package/dist/entries/toolbar.d.ts +3 -0
  295. package/dist/entries/toolbar.js +7 -0
  296. package/dist/entries/tooltip.d.ts +3 -0
  297. package/dist/entries/tooltip.js +7 -0
  298. package/dist/entries/tree.d.ts +3 -0
  299. package/dist/entries/tree.js +17 -0
  300. package/dist/entries/trigger-menu.d.ts +3 -0
  301. package/dist/entries/trigger-menu.js +11 -0
  302. package/dist/entries/waterfall.d.ts +3 -0
  303. package/dist/entries/waterfall.js +11 -0
  304. package/dist/events.d.ts +1658 -0
  305. package/dist/events.js +0 -0
  306. package/dist/field.d.ts +2 -0
  307. package/dist/field.js +101 -0
  308. package/dist/file-input.d.ts +2 -0
  309. package/dist/file-input.js +140 -0
  310. package/dist/forward-attributes.d.ts +2 -0
  311. package/dist/forward-attributes.js +22 -0
  312. package/dist/history.d.ts +2 -0
  313. package/dist/history.js +11 -0
  314. package/dist/hover-card.d.ts +2 -0
  315. package/dist/hover-card.js +31 -0
  316. package/dist/image-cropper.d.ts +2 -0
  317. package/dist/image-cropper.js +66 -0
  318. package/dist/index.d.ts +66 -0
  319. package/dist/index.js +269 -0
  320. package/dist/input.d.ts +3 -0
  321. package/dist/input.js +69 -0
  322. package/dist/label.d.ts +2 -0
  323. package/dist/label.js +11 -0
  324. package/dist/list.d.ts +40 -0
  325. package/dist/list.js +192 -0
  326. package/dist/listbox.d.ts +2 -0
  327. package/dist/listbox.js +84 -0
  328. package/dist/menu.d.ts +4 -0
  329. package/dist/menu.js +295 -0
  330. package/dist/menubar.d.ts +4 -0
  331. package/dist/menubar.js +30 -0
  332. package/dist/meter.d.ts +2 -0
  333. package/dist/meter.js +22 -0
  334. package/dist/navigation-menu.d.ts +2 -0
  335. package/dist/navigation-menu.js +114 -0
  336. package/dist/number-input.d.ts +2 -0
  337. package/dist/number-input.js +73 -0
  338. package/dist/observe.d.ts +2 -0
  339. package/dist/observe.js +7 -0
  340. package/dist/otp-input.d.ts +2 -0
  341. package/dist/otp-input.js +72 -0
  342. package/dist/overlays.d.ts +13 -0
  343. package/dist/overlays.js +156 -0
  344. package/dist/pagination.d.ts +2 -0
  345. package/dist/pagination.js +127 -0
  346. package/dist/password-input.d.ts +2 -0
  347. package/dist/password-input.js +57 -0
  348. package/dist/popover.d.ts +2 -0
  349. package/dist/popover.js +64 -0
  350. package/dist/popup.d.ts +40 -0
  351. package/dist/popup.js +112 -0
  352. package/dist/preferences.d.ts +2 -0
  353. package/dist/preferences.js +56 -0
  354. package/dist/progress.d.ts +2 -0
  355. package/dist/progress.js +22 -0
  356. package/dist/radio.d.ts +2 -0
  357. package/dist/radio.js +33 -0
  358. package/dist/rating.d.ts +2 -0
  359. package/dist/rating.js +64 -0
  360. package/dist/resizable.d.ts +2 -0
  361. package/dist/resizable.js +42 -0
  362. package/dist/scroll-area.d.ts +2 -0
  363. package/dist/scroll-area.js +45 -0
  364. package/dist/segmented-control.d.ts +2 -0
  365. package/dist/segmented-control.js +28 -0
  366. package/dist/select.d.ts +2 -0
  367. package/dist/select.js +239 -0
  368. package/dist/separator.d.ts +2 -0
  369. package/dist/separator.js +11 -0
  370. package/dist/slider.d.ts +2 -0
  371. package/dist/slider.js +77 -0
  372. package/dist/tabs.d.ts +2 -0
  373. package/dist/tabs.js +37 -0
  374. package/dist/timeline.d.ts +2 -0
  375. package/dist/timeline.js +27 -0
  376. package/dist/toast.d.ts +20 -0
  377. package/dist/toast.js +177 -0
  378. package/dist/toggle.d.ts +3 -0
  379. package/dist/toggle.js +64 -0
  380. package/dist/token-input.d.ts +2 -0
  381. package/dist/token-input.js +103 -0
  382. package/dist/toolbar.d.ts +2 -0
  383. package/dist/toolbar.js +36 -0
  384. package/dist/tooltip.d.ts +2 -0
  385. package/dist/tooltip.js +35 -0
  386. package/dist/tree.d.ts +2 -0
  387. package/dist/tree.js +147 -0
  388. package/dist/trigger-menu.d.ts +2 -0
  389. package/dist/trigger-menu.js +133 -0
  390. package/dist/waterfall.d.ts +2 -0
  391. package/dist/waterfall.js +140 -0
  392. package/docs/README.md +89 -0
  393. package/docs/agents.md +1523 -0
  394. package/docs/components/accordion.md +1401 -0
  395. package/docs/components/alert-dialog.md +788 -0
  396. package/docs/components/autocomplete.md +1692 -0
  397. package/docs/components/avatar.md +261 -0
  398. package/docs/components/button.md +441 -0
  399. package/docs/components/calendar.md +1536 -0
  400. package/docs/components/carousel.md +1055 -0
  401. package/docs/components/checkbox.md +1045 -0
  402. package/docs/components/collapsible.md +535 -0
  403. package/docs/components/color-picker.md +651 -0
  404. package/docs/components/combobox.md +3274 -0
  405. package/docs/components/command.md +1431 -0
  406. package/docs/components/date-input.md +563 -0
  407. package/docs/components/date-picker.md +1136 -0
  408. package/docs/components/dialog.md +1360 -0
  409. package/docs/components/drawer.md +1201 -0
  410. package/docs/components/field.md +519 -0
  411. package/docs/components/file-input.md +617 -0
  412. package/docs/components/hover-card.md +486 -0
  413. package/docs/components/image-cropper.md +489 -0
  414. package/docs/components/input.md +327 -0
  415. package/docs/components/label.md +208 -0
  416. package/docs/components/listbox.md +1015 -0
  417. package/docs/components/menu.md +1212 -0
  418. package/docs/components/menubar.md +460 -0
  419. package/docs/components/meter.md +298 -0
  420. package/docs/components/navigation-menu.md +1573 -0
  421. package/docs/components/number-input.md +1087 -0
  422. package/docs/components/otp-input.md +558 -0
  423. package/docs/components/pagination.md +639 -0
  424. package/docs/components/password-input.md +649 -0
  425. package/docs/components/popover.md +1433 -0
  426. package/docs/components/preferences.md +243 -0
  427. package/docs/components/progress.md +406 -0
  428. package/docs/components/radio.md +855 -0
  429. package/docs/components/rating.md +431 -0
  430. package/docs/components/resizable.md +866 -0
  431. package/docs/components/scroll-area.md +898 -0
  432. package/docs/components/segmented-control.md +421 -0
  433. package/docs/components/select.md +1957 -0
  434. package/docs/components/separator.md +238 -0
  435. package/docs/components/slider.md +1018 -0
  436. package/docs/components/switch.md +583 -0
  437. package/docs/components/tabs.md +948 -0
  438. package/docs/components/textarea.md +331 -0
  439. package/docs/components/time-input.md +235 -0
  440. package/docs/components/timeline.md +622 -0
  441. package/docs/components/toast.md +1223 -0
  442. package/docs/components/toggle.md +522 -0
  443. package/docs/components/token-input.md +854 -0
  444. package/docs/components/toolbar.md +815 -0
  445. package/docs/components/tooltip.md +860 -0
  446. package/docs/components/tree.md +1243 -0
  447. package/docs/components/trigger-menu.md +1110 -0
  448. package/docs/components/waterfall.md +709 -0
  449. package/docs/controllers.md +66 -0
  450. package/docs/core.md +8027 -0
  451. package/docs/custom-parts.md +91 -0
  452. package/docs/first-component.md +88 -0
  453. package/docs/forms.md +125 -0
  454. package/docs/installation.md +141 -0
  455. package/docs/introduction.md +32 -0
  456. package/docs/lists-and-menus.md +166 -0
  457. package/docs/llms.txt +1566 -0
  458. package/docs/parts-and-composition.md +57 -0
  459. package/docs/popups.md +92 -0
  460. package/docs/rendering.md +71 -0
  461. package/docs/server-rendering.md +118 -0
  462. package/docs/state-and-events.md +134 -0
  463. package/docs/styling-and-animation.md +104 -0
  464. package/docs/utilities/async-options.md +332 -0
  465. package/docs/utilities/shortcuts.md +184 -0
  466. package/html-custom-data.json +14792 -0
  467. package/package.json +302 -3
@@ -0,0 +1,1573 @@
1
+ # Navigation Menu
2
+
3
+ Groups site links into panels that open from a row of triggers.
4
+
5
+ Use a Navigation Menu for a site's main links, grouped into panels that open from a row of triggers. For a list of actions, use a [Menu](./menu.md).
6
+
7
+ ```ts title="basic.ts"
8
+ import "@ui-sdk/web/navigation-menu"
9
+ import {
10
+ ArrowRight,
11
+ ChevronDown,
12
+ Circle,
13
+ CircleCheck,
14
+ CircleQuestionMark,
15
+ createElement,
16
+ } from "lucide"
17
+
18
+ const gettingStarted = [
19
+ {
20
+ title: "Introduction",
21
+ href: "#",
22
+ description: "Unstyled components with one core for every framework.",
23
+ },
24
+ {
25
+ title: "Installation",
26
+ href: "#",
27
+ description:
28
+ "Add the package for your framework and render your first part.",
29
+ },
30
+ {
31
+ title: "Styling",
32
+ href: "#",
33
+ description: "Style parts with data attributes, CSS variables and any CSS.",
34
+ },
35
+ ]
36
+
37
+ const components = [
38
+ {
39
+ title: "Alert Dialog",
40
+ href: "#",
41
+ description:
42
+ "Asks for a decision before anything else on the page can be used.",
43
+ },
44
+ {
45
+ title: "Hover Card",
46
+ href: "#",
47
+ description:
48
+ "A preview of what a link points to, for people who pause on it.",
49
+ },
50
+ {
51
+ title: "Progress",
52
+ href: "#",
53
+ description: "Shows how far along a task is, like an upload.",
54
+ },
55
+ {
56
+ title: "Scroll Area",
57
+ href: "#",
58
+ description: "Scrolls its content with scrollbars you can style.",
59
+ },
60
+ {
61
+ title: "Tabs",
62
+ href: "#",
63
+ description:
64
+ "Shows one panel of content at a time, picked from a list of tabs.",
65
+ },
66
+ {
67
+ title: "Tooltip",
68
+ href: "#",
69
+ description:
70
+ "A short label for a control, shown on hover or keyboard focus.",
71
+ },
72
+ ]
73
+
74
+ const statuses = [
75
+ { title: "Backlog", href: "#", icon: CircleQuestionMark },
76
+ { title: "To do", href: "#", icon: Circle },
77
+ { title: "Done", href: "#", icon: CircleCheck },
78
+ { title: "All issues", href: "#", icon: ArrowRight, mirror: true },
79
+ ]
80
+
81
+ function mount(root: HTMLElement) {
82
+ root.innerHTML = `
83
+ <ui-navigation-menu aria-label="Main">
84
+ <ui-navigation-menu-list>
85
+ <ui-navigation-menu-item value="getting-started">
86
+ <ui-navigation-menu-trigger>
87
+ <button>
88
+ Getting started
89
+ ${createElement(ChevronDown, { "aria-hidden": "true" }).outerHTML}
90
+ </button>
91
+ </ui-navigation-menu-trigger>
92
+ <ui-navigation-menu-content>
93
+ <ul data-slot="grid">
94
+ <li>
95
+ <ui-navigation-menu-link>
96
+ <a href="#" data-variant="featured">
97
+ <span>ui-sdk</span>
98
+ <span data-slot="hint">Accessible components for React, Vue, Svelte, Solid and Web Components.</span>
99
+ </a>
100
+ </ui-navigation-menu-link>
101
+ </li>
102
+ ${gettingStarted
103
+ .map(
104
+ (page) => `
105
+ <li>
106
+ <ui-navigation-menu-link>
107
+ <a href="${page.href}">
108
+ <span>${page.title}</span>
109
+ <span data-slot="hint">${page.description}</span>
110
+ </a>
111
+ </ui-navigation-menu-link>
112
+ </li>`
113
+ )
114
+ .join("")}
115
+ </ul>
116
+ </ui-navigation-menu-content>
117
+ </ui-navigation-menu-item>
118
+ <ui-navigation-menu-item value="components">
119
+ <ui-navigation-menu-trigger>
120
+ <button>
121
+ Components
122
+ ${createElement(ChevronDown, { "aria-hidden": "true" }).outerHTML}
123
+ </button>
124
+ </ui-navigation-menu-trigger>
125
+ <ui-navigation-menu-content>
126
+ <ul data-slot="grid">
127
+ ${components
128
+ .map(
129
+ (page) => `
130
+ <li>
131
+ <ui-navigation-menu-link>
132
+ <a href="${page.href}">
133
+ <span>${page.title}</span>
134
+ <span data-slot="hint">${page.description}</span>
135
+ </a>
136
+ </ui-navigation-menu-link>
137
+ </li>`
138
+ )
139
+ .join("")}
140
+ </ul>
141
+ </ui-navigation-menu-content>
142
+ </ui-navigation-menu-item>
143
+ <ui-navigation-menu-item value="with-icon">
144
+ <ui-navigation-menu-trigger>
145
+ <button>
146
+ With Icon
147
+ ${createElement(ChevronDown, { "aria-hidden": "true" }).outerHTML}
148
+ </button>
149
+ </ui-navigation-menu-trigger>
150
+ <ui-navigation-menu-content>
151
+ <ul>
152
+ ${statuses
153
+ .map(
154
+ (status) => `
155
+ <li>
156
+ <ui-navigation-menu-link>
157
+ <a href="${status.href}">
158
+ <ui-navigation-menu-link-icon${status.mirror ? " mirror" : ""}>${createElement(status.icon, { "aria-hidden": "true" }).outerHTML}</ui-navigation-menu-link-icon>
159
+ ${status.title}
160
+ </a>
161
+ </ui-navigation-menu-link>
162
+ </li>`
163
+ )
164
+ .join("")}
165
+ </ul>
166
+ </ui-navigation-menu-content>
167
+ </ui-navigation-menu-item>
168
+ <ui-navigation-menu-item>
169
+ <ui-navigation-menu-link>
170
+ <a href="#">Docs</a>
171
+ </ui-navigation-menu-link>
172
+ </ui-navigation-menu-item>
173
+ </ui-navigation-menu-list>
174
+ <ui-navigation-menu-viewport side-offset="8"></ui-navigation-menu-viewport>
175
+ </ui-navigation-menu>
176
+ `
177
+ }
178
+
179
+ export { mount }
180
+ ```
181
+
182
+ Open each section, then use Tab or the arrow keys to move through its links.
183
+
184
+ ## Installation
185
+
186
+ ```bash
187
+ npm install @ui-sdk/web @ui-sdk/core
188
+ ```
189
+
190
+ Importing the component defines its elements. On the server the import does nothing.
191
+
192
+ ```ts
193
+ import "@ui-sdk/web/navigation-menu"
194
+ ```
195
+
196
+ ## Composition
197
+
198
+ Use the following composition to build a `ui-navigation-menu`:
199
+
200
+ ```text
201
+ ui-navigation-menu
202
+ ├── ui-navigation-menu-list
203
+ │ ├── ui-navigation-menu-item
204
+ │ │ ├── ui-navigation-menu-trigger
205
+ │ │ │ └── button
206
+ │ │ └── ui-navigation-menu-content
207
+ │ │ └── ui-navigation-menu-link
208
+ │ │ └── a
209
+ │ └── ui-navigation-menu-item
210
+ │ ├── ui-navigation-menu-trigger
211
+ │ │ └── button
212
+ │ └── ui-navigation-menu-content
213
+ │ └── ui-navigation-menu-link
214
+ │ └── a
215
+ └── ui-navigation-menu-viewport
216
+ ```
217
+
218
+ The trigger and the link wrap your own `<button>` and `<a>`, so they stay a real button and a real link. The list and its items take the `list` and `listitem` roles.
219
+
220
+ Give the navigation an `aria-label`. Each item holds a trigger and its panel, or a plain link. This follows the [disclosure navigation pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/): buttons show groups of ordinary links. Use a [Menu](./menu.md) for actions instead.
221
+
222
+ The viewport holds the open panel and follows its size and position. Size it with `--viewport-width` and `--viewport-height` and clip its overflow when you use one; [Contents in the Viewport](#contents-in-the-viewport) explains the geometry. You can also [leave the viewport out](#without-a-viewport) and position each panel separately.
223
+
224
+ ## Usage
225
+
226
+ Import the parts and put them together:
227
+
228
+ ```html
229
+ <script type="module">
230
+ import "@ui-sdk/web/navigation-menu"
231
+ </script>
232
+
233
+ <ui-navigation-menu aria-label="Main">
234
+ <ui-navigation-menu-list>
235
+ <ui-navigation-menu-item value="guides">
236
+ <ui-navigation-menu-trigger
237
+ ><button>Guides</button></ui-navigation-menu-trigger
238
+ >
239
+ <ui-navigation-menu-content>
240
+ <ui-navigation-menu-link
241
+ ><a href="/docs">Introduction</a></ui-navigation-menu-link
242
+ >
243
+ </ui-navigation-menu-content>
244
+ </ui-navigation-menu-item>
245
+ </ui-navigation-menu-list>
246
+ <ui-navigation-menu-viewport></ui-navigation-menu-viewport>
247
+ </ui-navigation-menu>
248
+ ```
249
+
250
+ ## Link Component
251
+
252
+ Render your router's link for client-side navigation. `current` marks the link for the current page with `aria-current="page"` and `data-current`.
253
+
254
+ The link wraps your own `<a>`, so your router's click handling stays on it. Toggle the `current` attribute as the route changes.
255
+
256
+ ```ts title="link-component.ts"
257
+ import "@ui-sdk/web/navigation-menu"
258
+ import { ChevronDown, createElement } from "lucide"
259
+ import { log } from "./log"
260
+
261
+ const gettingStarted = [
262
+ {
263
+ title: "Introduction",
264
+ href: "#introduction",
265
+ description: "Unstyled components with one core for every framework.",
266
+ },
267
+ {
268
+ title: "Installation",
269
+ href: "#installation",
270
+ description:
271
+ "Add the package for your framework and render your first part.",
272
+ },
273
+ {
274
+ title: "Styling",
275
+ href: "#styling",
276
+ description: "Style parts with data attributes, CSS variables and any CSS.",
277
+ },
278
+ ]
279
+
280
+ function mount(root: HTMLElement) {
281
+ root.innerHTML = `
282
+ <ui-navigation-menu aria-label="Docs">
283
+ <ui-navigation-menu-list>
284
+ <ui-navigation-menu-item value="guides">
285
+ <ui-navigation-menu-trigger>
286
+ <button>
287
+ Guides
288
+ ${createElement(ChevronDown, { "aria-hidden": "true" }).outerHTML}
289
+ </button>
290
+ </ui-navigation-menu-trigger>
291
+ <ui-navigation-menu-content>
292
+ <ul>
293
+ ${gettingStarted
294
+ .map(
295
+ (page) => `
296
+ <li>
297
+ <ui-navigation-menu-link>
298
+ <a href="${page.href}" data-router>
299
+ <span>${page.title}</span>
300
+ <span data-slot="hint">${page.description}</span>
301
+ </a>
302
+ </ui-navigation-menu-link>
303
+ </li>`
304
+ )
305
+ .join("")}
306
+ </ul>
307
+ </ui-navigation-menu-content>
308
+ </ui-navigation-menu-item>
309
+ <ui-navigation-menu-item>
310
+ <ui-navigation-menu-link>
311
+ <a href="#components" data-router>Components</a>
312
+ </ui-navigation-menu-link>
313
+ </ui-navigation-menu-item>
314
+ </ui-navigation-menu-list>
315
+ <ui-navigation-menu-viewport side-offset="8"></ui-navigation-menu-viewport>
316
+ </ui-navigation-menu>
317
+ `
318
+ function setRoute(route: string) {
319
+ for (const link of root.querySelectorAll("ui-navigation-menu-link")) {
320
+ link.toggleAttribute(
321
+ "current",
322
+ link.querySelector("a")!.getAttribute("href") === route
323
+ )
324
+ }
325
+ }
326
+ for (const link of root.querySelectorAll<HTMLAnchorElement>(
327
+ "a[data-router]"
328
+ )) {
329
+ link.addEventListener("click", (event) => {
330
+ event.preventDefault()
331
+ const href = link.getAttribute("href")!
332
+ setRoute(href)
333
+ log(root, `Navigated to ${href}`)
334
+ })
335
+ }
336
+ setRoute("#introduction")
337
+ }
338
+
339
+ export { mount }
340
+ ```
341
+
342
+ Choose a link to update the current page without reloading the example.
343
+
344
+ Routers call `preventDefault()` on a link's click to navigate without a page load. The menu listens on the link element itself, so it still closes after a client-side route change. A press with a modifier key or the middle button opens the link elsewhere and leaves the menu open. Set `closeOnSelect` to false to keep it open on every press, see [Close on Select](#close-on-select).
345
+
346
+ ## Icons
347
+
348
+ Put a link's icon in a `ui-navigation-menu-link-icon`, before its text, like the With Icon panel in the menu at the top of the page. Screen readers skip it, so an SVG's `<title>`, an icon font's glyph or an emoji never joins the link's name.
349
+
350
+ - **Icons that point a way**, like the arrow on All issues, flip in right-to-left text with `mirror`.
351
+ - **Links with only an icon** need an `aria-label`. In development, one with no name warns.
352
+ - **Lining up links.** Links with an icon get `data-has-icon`, and the content or list around them gets `data-has-icons` while any link has one, so links without an icon can leave its space: `[data-has-icons] [data-slot="navigation-menu-link"]:not([data-has-icon])`.
353
+
354
+ ## Vertical
355
+
356
+ `orientation="vertical"` is for a sidebar: Up and Down move between the items, and panels open beside the menu, at the inline end, unless `side` says otherwise.
357
+
358
+ ```ts title="vertical.ts"
359
+ import "@ui-sdk/web/navigation-menu"
360
+ import { ChevronRight, createElement } from "lucide"
361
+
362
+ const sections = [
363
+ {
364
+ value: "guides",
365
+ label: "Guides",
366
+ links: [
367
+ {
368
+ title: "Introduction",
369
+ href: "#",
370
+ description: "Unstyled components with one core for every framework.",
371
+ },
372
+ {
373
+ title: "Installation",
374
+ href: "#",
375
+ description:
376
+ "Add the package for your framework and render your first part.",
377
+ },
378
+ {
379
+ title: "Styling",
380
+ href: "#",
381
+ description:
382
+ "Style parts with data attributes, CSS variables and any CSS.",
383
+ },
384
+ ],
385
+ },
386
+ {
387
+ value: "components",
388
+ label: "Components",
389
+ links: [
390
+ {
391
+ title: "Alert Dialog",
392
+ href: "#",
393
+ description:
394
+ "Asks for a decision before anything else on the page can be used.",
395
+ },
396
+ {
397
+ title: "Hover Card",
398
+ href: "#",
399
+ description:
400
+ "A preview of what a link points to, for people who pause on it.",
401
+ },
402
+ {
403
+ title: "Progress",
404
+ href: "#",
405
+ description: "Shows how far along a task is, like an upload.",
406
+ },
407
+ {
408
+ title: "Scroll Area",
409
+ href: "#",
410
+ description: "Scrolls its content with scrollbars you can style.",
411
+ },
412
+ ],
413
+ },
414
+ ]
415
+
416
+ function mount(root: HTMLElement) {
417
+ root.innerHTML = `
418
+ <ui-navigation-menu aria-label="Sidebar" orientation="vertical">
419
+ <ui-navigation-menu-list>
420
+ ${sections
421
+ .map(
422
+ (section) => `
423
+ <ui-navigation-menu-item value="${section.value}">
424
+ <ui-navigation-menu-trigger>
425
+ <button>
426
+ ${section.label}
427
+ ${createElement(ChevronRight, { "aria-hidden": "true" }).outerHTML}
428
+ </button>
429
+ </ui-navigation-menu-trigger>
430
+ <ui-navigation-menu-content>
431
+ <ul>
432
+ ${section.links
433
+ .map(
434
+ (link) => `
435
+ <li>
436
+ <ui-navigation-menu-link>
437
+ <a href="${link.href}">
438
+ <span>${link.title}</span>
439
+ <span data-slot="hint">${link.description}</span>
440
+ </a>
441
+ </ui-navigation-menu-link>
442
+ </li>`
443
+ )
444
+ .join("")}
445
+ </ul>
446
+ </ui-navigation-menu-content>
447
+ </ui-navigation-menu-item>`
448
+ )
449
+ .join("")}
450
+ <ui-navigation-menu-item>
451
+ <ui-navigation-menu-link><a href="#">Docs</a></ui-navigation-menu-link>
452
+ </ui-navigation-menu-item>
453
+ </ui-navigation-menu-list>
454
+ <ui-navigation-menu-viewport side-offset="8"></ui-navigation-menu-viewport>
455
+ </ui-navigation-menu>
456
+ `
457
+ }
458
+
459
+ export { mount }
460
+ ```
461
+
462
+ Use Up and Down between the sidebar items, then open a panel beside its trigger.
463
+
464
+ ## Click Only
465
+
466
+ Resting the pointer on a trigger opens its panel. With `openOnHover` set to false, the menu opens on click only: pressing a trigger opens and closes its panel, and moving the pointer does nothing. Keyboard and touch work the same either way.
467
+
468
+ ```ts title="click-only.ts"
469
+ import "@ui-sdk/web/navigation-menu"
470
+ import { ChevronDown, createElement } from "lucide"
471
+
472
+ const sections = [
473
+ {
474
+ value: "guides",
475
+ label: "Guides",
476
+ links: [
477
+ {
478
+ title: "Introduction",
479
+ href: "#",
480
+ description: "Unstyled components with one core for every framework.",
481
+ },
482
+ {
483
+ title: "Installation",
484
+ href: "#",
485
+ description:
486
+ "Add the package for your framework and render your first part.",
487
+ },
488
+ {
489
+ title: "Styling",
490
+ href: "#",
491
+ description:
492
+ "Style parts with data attributes, CSS variables and any CSS.",
493
+ },
494
+ ],
495
+ },
496
+ {
497
+ value: "components",
498
+ label: "Components",
499
+ links: [
500
+ {
501
+ title: "Alert Dialog",
502
+ href: "#",
503
+ description:
504
+ "Asks for a decision before anything else on the page can be used.",
505
+ },
506
+ {
507
+ title: "Hover Card",
508
+ href: "#",
509
+ description:
510
+ "A preview of what a link points to, for people who pause on it.",
511
+ },
512
+ {
513
+ title: "Progress",
514
+ href: "#",
515
+ description: "Shows how far along a task is, like an upload.",
516
+ },
517
+ {
518
+ title: "Scroll Area",
519
+ href: "#",
520
+ description: "Scrolls its content with scrollbars you can style.",
521
+ },
522
+ ],
523
+ },
524
+ ]
525
+
526
+ function mount(root: HTMLElement) {
527
+ root.innerHTML = `
528
+ <ui-navigation-menu aria-label="Main" open-on-hover="false">
529
+ <ui-navigation-menu-list>
530
+ ${sections
531
+ .map(
532
+ (section) => `
533
+ <ui-navigation-menu-item value="${section.value}">
534
+ <ui-navigation-menu-trigger>
535
+ <button>
536
+ ${section.label}
537
+ ${createElement(ChevronDown, { "aria-hidden": "true" }).outerHTML}
538
+ </button>
539
+ </ui-navigation-menu-trigger>
540
+ <ui-navigation-menu-content>
541
+ <ul>
542
+ ${section.links
543
+ .map(
544
+ (link) => `
545
+ <li>
546
+ <ui-navigation-menu-link>
547
+ <a href="${link.href}">
548
+ <span>${link.title}</span>
549
+ <span data-slot="hint">${link.description}</span>
550
+ </a>
551
+ </ui-navigation-menu-link>
552
+ </li>`
553
+ )
554
+ .join("")}
555
+ </ul>
556
+ </ui-navigation-menu-content>
557
+ </ui-navigation-menu-item>`
558
+ )
559
+ .join("")}
560
+ </ui-navigation-menu-list>
561
+ <ui-navigation-menu-viewport side-offset="8"></ui-navigation-menu-viewport>
562
+ </ui-navigation-menu>
563
+ `
564
+ }
565
+
566
+ export { mount }
567
+ ```
568
+
569
+ Click a trigger to open it. Moving the pointer between triggers does not open another panel.
570
+
571
+ ## Nested
572
+
573
+ A navigation menu inside another menu's content is a submenu. It renders a plain element instead of a second `<nav>` landmark, gets `data-nested`, and closes with its parent when the parent moves on to another item. Without a viewport of its own, its panels open beside its triggers. Pressing a link in it closes every menu.
574
+
575
+ ```ts title="nested.ts"
576
+ import "@ui-sdk/web/navigation-menu"
577
+ import { ChevronDown, ChevronRight, createElement } from "lucide"
578
+
579
+ const gettingStarted = [
580
+ {
581
+ title: "Introduction",
582
+ href: "#",
583
+ description: "Unstyled components with one core for every framework.",
584
+ },
585
+ {
586
+ title: "Installation",
587
+ href: "#",
588
+ description:
589
+ "Add the package for your framework and render your first part.",
590
+ },
591
+ {
592
+ title: "Styling",
593
+ href: "#",
594
+ description: "Style parts with data attributes, CSS variables and any CSS.",
595
+ },
596
+ ]
597
+
598
+ const groups = [
599
+ {
600
+ value: "overlays",
601
+ label: "Overlays",
602
+ links: [
603
+ { title: "Dialog", href: "#" },
604
+ { title: "Popover", href: "#" },
605
+ { title: "Tooltip", href: "#" },
606
+ ],
607
+ },
608
+ {
609
+ value: "forms",
610
+ label: "Forms",
611
+ links: [
612
+ { title: "Input", href: "#" },
613
+ { title: "Select", href: "#" },
614
+ { title: "Checkbox", href: "#" },
615
+ ],
616
+ },
617
+ ]
618
+
619
+ function mount(root: HTMLElement) {
620
+ root.innerHTML = `
621
+ <ui-navigation-menu aria-label="Main">
622
+ <ui-navigation-menu-list>
623
+ <ui-navigation-menu-item value="guides">
624
+ <ui-navigation-menu-trigger>
625
+ <button>
626
+ Guides
627
+ ${createElement(ChevronDown, { "aria-hidden": "true" }).outerHTML}
628
+ </button>
629
+ </ui-navigation-menu-trigger>
630
+ <ui-navigation-menu-content>
631
+ <ul>
632
+ ${gettingStarted
633
+ .map(
634
+ (link) => `
635
+ <li>
636
+ <ui-navigation-menu-link>
637
+ <a href="${link.href}">
638
+ <span>${link.title}</span>
639
+ <span data-slot="hint">${link.description}</span>
640
+ </a>
641
+ </ui-navigation-menu-link>
642
+ </li>`
643
+ )
644
+ .join("")}
645
+ </ul>
646
+ </ui-navigation-menu-content>
647
+ </ui-navigation-menu-item>
648
+ <ui-navigation-menu-item value="components">
649
+ <ui-navigation-menu-trigger>
650
+ <button>
651
+ Components
652
+ ${createElement(ChevronDown, { "aria-hidden": "true" }).outerHTML}
653
+ </button>
654
+ </ui-navigation-menu-trigger>
655
+ <ui-navigation-menu-content>
656
+ <ui-navigation-menu orientation="vertical">
657
+ <ui-navigation-menu-list>
658
+ ${groups
659
+ .map(
660
+ (group) => `
661
+ <ui-navigation-menu-item value="${group.value}">
662
+ <ui-navigation-menu-trigger>
663
+ <button>
664
+ ${group.label}
665
+ ${createElement(ChevronRight, { "aria-hidden": "true" }).outerHTML}
666
+ </button>
667
+ </ui-navigation-menu-trigger>
668
+ <ui-navigation-menu-content side-offset="9" align-offset="-5">
669
+ <ul>
670
+ ${group.links
671
+ .map(
672
+ (link) => `
673
+ <li>
674
+ <ui-navigation-menu-link>
675
+ <a href="${link.href}">${link.title}</a>
676
+ </ui-navigation-menu-link>
677
+ </li>`
678
+ )
679
+ .join("")}
680
+ </ul>
681
+ </ui-navigation-menu-content>
682
+ </ui-navigation-menu-item>`
683
+ )
684
+ .join("")}
685
+ </ui-navigation-menu-list>
686
+ </ui-navigation-menu>
687
+ </ui-navigation-menu-content>
688
+ </ui-navigation-menu-item>
689
+ </ui-navigation-menu-list>
690
+ <ui-navigation-menu-viewport side-offset="8"></ui-navigation-menu-viewport>
691
+ </ui-navigation-menu>
692
+ `
693
+ }
694
+
695
+ export { mount }
696
+ ```
697
+
698
+ Open Components, then open Overlays or Forms. Escape closes the inner panel first.
699
+
700
+ ## Animation
701
+
702
+ The viewport and contents get `data-starting-style` for their first frame and `data-ending-style` while they animate out, and stay mounted until their animation finishes. Contents also get `data-motion` while another item takes over: `from-end` or `from-start` on the one coming in, and `to-start` or `to-end` on the one giving way. Order is logical, so in right-to-left layouts the end is on the left. The viewport can transition `width`, `height`, `left` and `top` as it follows the open item.
703
+
704
+ `openchangecomplete` fires once the menu has finished opening or closing, after any animation, with the new open state in `event.detail.value`. Switching between items doesn't count.
705
+
706
+ ```ts title="animation.ts"
707
+ import "@ui-sdk/web/navigation-menu"
708
+ import type { UiNavigationMenuElement } from "@ui-sdk/web"
709
+ import { ChevronDown, createElement } from "lucide"
710
+ import { log } from "./log"
711
+
712
+ const gettingStarted = [
713
+ {
714
+ title: "Introduction",
715
+ href: "#",
716
+ description: "Unstyled components with one core for every framework.",
717
+ },
718
+ {
719
+ title: "Installation",
720
+ href: "#",
721
+ description:
722
+ "Add the package for your framework and render your first part.",
723
+ },
724
+ {
725
+ title: "Styling",
726
+ href: "#",
727
+ description: "Style parts with data attributes, CSS variables and any CSS.",
728
+ },
729
+ ]
730
+
731
+ const components = [
732
+ {
733
+ title: "Alert Dialog",
734
+ href: "#",
735
+ description:
736
+ "Asks for a decision before anything else on the page can be used.",
737
+ },
738
+ {
739
+ title: "Hover Card",
740
+ href: "#",
741
+ description:
742
+ "A preview of what a link points to, for people who pause on it.",
743
+ },
744
+ {
745
+ title: "Progress",
746
+ href: "#",
747
+ description: "Shows how far along a task is, like an upload.",
748
+ },
749
+ {
750
+ title: "Scroll Area",
751
+ href: "#",
752
+ description: "Scrolls its content with scrollbars you can style.",
753
+ },
754
+ {
755
+ title: "Tabs",
756
+ href: "#",
757
+ description:
758
+ "Shows one panel of content at a time, picked from a list of tabs.",
759
+ },
760
+ {
761
+ title: "Tooltip",
762
+ href: "#",
763
+ description:
764
+ "A short label for a control, shown on hover or keyboard focus.",
765
+ },
766
+ ]
767
+
768
+ function mount(root: HTMLElement) {
769
+ root.innerHTML = `
770
+ <ui-navigation-menu aria-label="Main">
771
+ <ui-navigation-menu-list>
772
+ <ui-navigation-menu-item value="guides">
773
+ <ui-navigation-menu-trigger>
774
+ <button>
775
+ Guides
776
+ ${createElement(ChevronDown, { "aria-hidden": "true" }).outerHTML}
777
+ </button>
778
+ </ui-navigation-menu-trigger>
779
+ <ui-navigation-menu-content>
780
+ <ul>
781
+ ${gettingStarted
782
+ .map(
783
+ (page) => `
784
+ <li>
785
+ <ui-navigation-menu-link>
786
+ <a href="${page.href}">
787
+ <span>${page.title}</span>
788
+ <span data-slot="hint">${page.description}</span>
789
+ </a>
790
+ </ui-navigation-menu-link>
791
+ </li>`
792
+ )
793
+ .join("")}
794
+ </ul>
795
+ </ui-navigation-menu-content>
796
+ </ui-navigation-menu-item>
797
+ <ui-navigation-menu-item value="components">
798
+ <ui-navigation-menu-trigger>
799
+ <button>
800
+ Components
801
+ ${createElement(ChevronDown, { "aria-hidden": "true" }).outerHTML}
802
+ </button>
803
+ </ui-navigation-menu-trigger>
804
+ <ui-navigation-menu-content>
805
+ <ul data-slot="grid">
806
+ ${components
807
+ .map(
808
+ (page) => `
809
+ <li>
810
+ <ui-navigation-menu-link>
811
+ <a href="${page.href}">
812
+ <span>${page.title}</span>
813
+ <span data-slot="hint">${page.description}</span>
814
+ </a>
815
+ </ui-navigation-menu-link>
816
+ </li>`
817
+ )
818
+ .join("")}
819
+ </ul>
820
+ </ui-navigation-menu-content>
821
+ </ui-navigation-menu-item>
822
+ </ui-navigation-menu-list>
823
+ <ui-navigation-menu-viewport side-offset="8" data-variant="slide"></ui-navigation-menu-viewport>
824
+ </ui-navigation-menu>
825
+ `
826
+ const menu =
827
+ root.querySelector<UiNavigationMenuElement>("ui-navigation-menu")!
828
+ menu.addEventListener("valuechange", (event) => {
829
+ log(root, event.detail.value ? `Showing ${event.detail.value}` : "Closing")
830
+ })
831
+ menu.addEventListener("openchangecomplete", (event) => {
832
+ log(root, event.detail.value ? "Finished opening" : "Finished closing")
833
+ })
834
+ }
835
+
836
+ export { mount }
837
+ ```
838
+
839
+ Switch between Guides and Components to see the viewport resize and the panels slide.
840
+
841
+ The menu waits for whatever animations run, and only those. Turn them off for people who ask for less motion with a `prefers-reduced-motion` media query, and panels switch and close at once.
842
+
843
+ ## Delay
844
+
845
+ `delay` is how long the pointer rests on a trigger before its panel opens, and `closeDelay` how long the panel stays once the pointer leaves the trigger and the panel: 50 milliseconds each by default. The pointer can cross the gap between a trigger and its panel without closing it. Once a panel is open, moving to another trigger switches at once, unless the pointer is only passing over it on the way into the open panel. Coming back right after closing skips the delay.
846
+
847
+ ```html
848
+ <ui-navigation-menu aria-label="Main" delay="400" close-delay="600">
849
+ ...
850
+ </ui-navigation-menu>
851
+ ```
852
+
853
+ ## Indicator
854
+
855
+ `ui-navigation-menu-indicator` sits under the open trigger, or under the top-level link with `current` while every panel is closed. Its box comes from the `--active-item-*` [variables](#styling-navigationmenuindicator), relative to its positioned ancestor, and it gets `data-visible` while it has something to cover. Screen readers skip it.
856
+
857
+ ```html
858
+ <ui-navigation-menu aria-label="Main">
859
+ <ui-navigation-menu-list>...</ui-navigation-menu-list>
860
+ <ui-navigation-menu-indicator></ui-navigation-menu-indicator>
861
+ <ui-navigation-menu-viewport></ui-navigation-menu-viewport>
862
+ </ui-navigation-menu>
863
+ ```
864
+
865
+ ```css
866
+ [data-slot="navigation-menu"] {
867
+ position: relative;
868
+ }
869
+
870
+ [data-slot="navigation-menu-indicator"] {
871
+ position: absolute;
872
+ left: var(--active-item-left);
873
+ bottom: var(--active-item-bottom);
874
+ width: var(--active-item-width);
875
+ height: 2px;
876
+ opacity: 0;
877
+ }
878
+
879
+ [data-slot="navigation-menu-indicator"][data-visible] {
880
+ opacity: 1;
881
+ }
882
+ ```
883
+
884
+ ## Viewport Placement
885
+
886
+ The viewport opens below the open trigger, lined up with its start, or beside it in a vertical menu. `side` and `align` change that, `sideOffset` is the gap (4 pixels by default), `alignOffset` shifts it along the trigger, and `collisionPadding` is the space kept from the window's edges. It flips and shifts to stay on screen.
887
+
888
+ `anchor` positions it against something else: an element, a CSS selector, a point like `{ x, y }`, or anything with `getBoundingClientRect()`. Anchoring to the whole bar gives one dropdown that stays put while its contents change.
889
+
890
+ ```html
891
+ <ui-navigation-menu-viewport
892
+ side="bottom"
893
+ align="center"
894
+ side-offset="8"
895
+ collision-padding="16"
896
+ anchor="#site-header"
897
+ ></ui-navigation-menu-viewport>
898
+ ```
899
+
900
+ ## Without a Viewport
901
+
902
+ Leave the viewport out and each content is its own popup, placed against its trigger. Contents then take the same placement props as the viewport: `side`, `align`, `sideOffset`, `alignOffset`, `collisionPadding` and `anchor`. Style each content as a popup rather than positioning it inside one.
903
+
904
+ ```html
905
+ <ui-navigation-menu aria-label="Main">
906
+ <ui-navigation-menu-list>
907
+ <ui-navigation-menu-item value="guides">
908
+ <ui-navigation-menu-trigger
909
+ ><button>Guides</button></ui-navigation-menu-trigger
910
+ >
911
+ <ui-navigation-menu-content side="bottom" align="start" side-offset="8">
912
+ ...
913
+ </ui-navigation-menu-content>
914
+ </ui-navigation-menu-item>
915
+ </ui-navigation-menu-list>
916
+ </ui-navigation-menu>
917
+ ```
918
+
919
+ ## Controlled
920
+
921
+ The element's `value` property is live: read it, set it to an item's `value` to open that item, or set it to `null` to close every panel. The `value` attribute sets where it starts. Listen for `valuechange` to follow the changes.
922
+
923
+ Items take a `value`, which is generated when left out. Every change carries a [reason](#reasons) in `event.detail.reason`, like `trigger-hover`, `trigger-press`, `link-press` or `outside-press`. Call `event.preventDefault()` to stop it.
924
+
925
+ ```html
926
+ <ui-navigation-menu aria-label="Main"> ... </ui-navigation-menu>
927
+
928
+ <script type="module">
929
+ const menu = document.querySelector("ui-navigation-menu")
930
+ menu.addEventListener("valuechange", (event) => (value = event.detail.value))
931
+ menu.value = "components"
932
+ </script>
933
+ ```
934
+
935
+ ## Opening from Anywhere
936
+
937
+ The element's `api` property is its [controller](#controller): it opens, closes and focuses the menu from anywhere, without controlling it.
938
+
939
+ ```html
940
+ <button type="button">Browse Components</button>
941
+ <ui-navigation-menu aria-label="Main"> ... </ui-navigation-menu>
942
+
943
+ <script type="module">
944
+ const { api: menu } = document.querySelector("ui-navigation-menu")
945
+ document
946
+ .querySelector("button")
947
+ .addEventListener("click", () => menu.setValue("components"))
948
+ </script>
949
+ ```
950
+
951
+ ## Keep Mounted
952
+
953
+ Closed contents leave the page by default. `keepMounted` keeps them in it, hidden, so the server's HTML and crawlers have every link. `hiddenUntilFound` also lets find in page reach them: closed content gets `hidden="until-found"`, and a match opens its item with the `find-in-page` reason. It implies `keepMounted`.
954
+
955
+ Closed content stays in its item, with its own classes, and moves into the viewport when it opens. Put padding on an element inside it rather than on the content, so the hidden box has no size where it sits.
956
+
957
+ ```html
958
+ <ui-navigation-menu-content hidden-until-found>...</ui-navigation-menu-content>
959
+ ```
960
+
961
+ ## Close on Select
962
+
963
+ Pressing a link closes the menu by default, like after a route change. With `closeOnSelect` set to false on the menu, it stays open, and a link's own `closeOnSelect` wins over the menu's.
964
+
965
+ ```html
966
+ <ui-navigation-menu aria-label="Main" close-on-select="false">
967
+ ...
968
+ <ui-navigation-menu-link close-on-select>
969
+ <a href="/docs/components">Browse All</a>
970
+ </ui-navigation-menu-link>
971
+ </ui-navigation-menu>
972
+ ```
973
+
974
+ ## Container
975
+
976
+ Panels render in place and show in the top layer, so no ancestor clips or covers them. `container` renders the viewport, or each content without one, somewhere else, named by a CSS selector, for iframes, shadow roots or a specific stacking context. Set it on the menu, or on the viewport or a content.
977
+
978
+ ```html
979
+ <ui-navigation-menu aria-label="Main" container="#menu-host">
980
+ ...
981
+ </ui-navigation-menu>
982
+ ```
983
+
984
+ ## Custom Rendering
985
+
986
+ ```ts title="custom-rendering.ts"
987
+ import "@ui-sdk/web/navigation-menu"
988
+ import { Minus, Plus, createElement } from "lucide"
989
+
990
+ const sections = [
991
+ {
992
+ value: "guides",
993
+ label: "Guides",
994
+ links: [
995
+ {
996
+ title: "Introduction",
997
+ href: "#",
998
+ description: "Unstyled components with one core for every framework.",
999
+ },
1000
+ {
1001
+ title: "Installation",
1002
+ href: "#",
1003
+ description:
1004
+ "Add the package for your framework and render your first part.",
1005
+ },
1006
+ {
1007
+ title: "Styling",
1008
+ href: "#",
1009
+ description:
1010
+ "Style parts with data attributes, CSS variables and any CSS.",
1011
+ },
1012
+ ],
1013
+ },
1014
+ {
1015
+ value: "components",
1016
+ label: "Components",
1017
+ links: [
1018
+ {
1019
+ title: "Alert Dialog",
1020
+ href: "#",
1021
+ description:
1022
+ "Asks for a decision before anything else on the page can be used.",
1023
+ },
1024
+ {
1025
+ title: "Hover Card",
1026
+ href: "#",
1027
+ description:
1028
+ "A preview of what a link points to, for people who pause on it.",
1029
+ },
1030
+ {
1031
+ title: "Progress",
1032
+ href: "#",
1033
+ description: "Shows how far along a task is, like an upload.",
1034
+ },
1035
+ {
1036
+ title: "Scroll Area",
1037
+ href: "#",
1038
+ description: "Scrolls its content with scrollbars you can style.",
1039
+ },
1040
+ ],
1041
+ },
1042
+ ]
1043
+
1044
+ function mount(root: HTMLElement) {
1045
+ root.innerHTML = `
1046
+ <ui-navigation-menu aria-label="Main">
1047
+ <ui-navigation-menu-list>
1048
+ ${sections
1049
+ .map(
1050
+ (section) => `
1051
+ <ui-navigation-menu-item value="${section.value}">
1052
+ <ui-navigation-menu-trigger>
1053
+ <button>
1054
+ ${section.label}
1055
+ ${createElement(Plus, { "aria-hidden": "true" }).outerHTML}
1056
+ ${createElement(Minus, { "aria-hidden": "true" }).outerHTML}
1057
+ </button>
1058
+ </ui-navigation-menu-trigger>
1059
+ <ui-navigation-menu-content as-child>
1060
+ <section aria-label="${section.label}">
1061
+ <p data-slot="hint">
1062
+ <span data-hide="motion">Opened</span>
1063
+ <span data-show="from-end">Moved in from-end</span>
1064
+ <span data-show="from-start">Moved in from-start</span>
1065
+ </p>
1066
+ <ul>
1067
+ ${section.links
1068
+ .map(
1069
+ (link, index) => `
1070
+ <li>
1071
+ <ui-navigation-menu-link${index === 0 ? " current" : ""}>
1072
+ <a href="${link.href}">
1073
+ <span>${link.title}</span>
1074
+ <span data-slot="hint" data-show="current">You're here</span>
1075
+ </a>
1076
+ </ui-navigation-menu-link>
1077
+ </li>`
1078
+ )
1079
+ .join("")}
1080
+ </ul>
1081
+ </section>
1082
+ </ui-navigation-menu-content>
1083
+ </ui-navigation-menu-item>`
1084
+ )
1085
+ .join("")}
1086
+ <ui-navigation-menu-item value="pricing">
1087
+ <ui-navigation-menu-trigger disabled>
1088
+ <button>
1089
+ Pricing
1090
+ <span data-slot="badge" data-show="disabled">Soon</span>
1091
+ </button>
1092
+ </ui-navigation-menu-trigger>
1093
+ <ui-navigation-menu-content>Plans for teams.</ui-navigation-menu-content>
1094
+ </ui-navigation-menu-item>
1095
+ </ui-navigation-menu-list>
1096
+ <ui-navigation-menu-viewport side-offset="8"></ui-navigation-menu-viewport>
1097
+ </ui-navigation-menu>
1098
+ `
1099
+ }
1100
+
1101
+ export { mount }
1102
+ ```
1103
+
1104
+ Open Guides to see its render state, then compare the current link and disabled trigger.
1105
+
1106
+ Elements have no render functions. The trigger and link wrap your own elements, and `as-child` renders a content as its first child, like a `<section>`. State shows as data attributes on each element, so its children can show and hide from them. Here the content shows its `data-motion`, and a disabled trigger shows a badge.
1107
+
1108
+ `disabled` on a trigger stops it from opening its panel. Each part's state is listed under [State](#state).
1109
+
1110
+ ## Styling
1111
+
1112
+ Parts are unstyled. Style them through the attributes they render, which are the same in every framework. Each part has a `data-slot`, such as `navigation-menu-trigger`, and its state attributes are listed under [Data Attributes](#data-attributes).
1113
+
1114
+ ### Contents in the Viewport
1115
+
1116
+ The viewport is one popup that holds whichever content is open. For it to take that content's size:
1117
+
1118
+ - Place contents at the viewport's corner, with their own width: `position: absolute; top: 0; inset-inline-start: 0`, and a width like `400px` or `max-content`. Contents of different widths make the viewport resize as it switches.
1119
+ - Put padding on the contents, not the viewport. `--viewport-width` and `--viewport-height` are the open content's size plus the viewport's own border.
1120
+ - Size the viewport with those variables and clip it, so contents sliding in and out stay inside.
1121
+ - Copy `--available-height` to an inheriting variable such as `--panel-height`, then use it to cap the content and let long panels scroll. The positioning variables themselves do not inherit.
1122
+
1123
+ In plain CSS:
1124
+
1125
+ ```css
1126
+ [data-slot="navigation-menu-viewport"] {
1127
+ --panel-height: var(--available-height);
1128
+ width: var(--viewport-width);
1129
+ height: var(--viewport-height);
1130
+ overflow: hidden;
1131
+ transition:
1132
+ width 200ms,
1133
+ height 200ms,
1134
+ left 200ms,
1135
+ top 200ms,
1136
+ opacity 200ms;
1137
+ }
1138
+
1139
+ [data-slot="navigation-menu-viewport"] > [data-slot="navigation-menu-content"] {
1140
+ position: absolute;
1141
+ top: 0;
1142
+ inset-inline-start: 0;
1143
+ max-height: var(--available-height, var(--panel-height));
1144
+ overflow-y: auto;
1145
+ }
1146
+
1147
+ [data-slot="navigation-menu-content"][data-motion="from-end"][data-starting-style],
1148
+ [data-slot="navigation-menu-content"][data-motion="to-end"][data-ending-style] {
1149
+ translate: 48px 0;
1150
+ }
1151
+
1152
+ [data-slot="navigation-menu-content"][data-motion="from-start"][data-starting-style],
1153
+ [data-slot="navigation-menu-content"][data-motion="to-start"][data-ending-style] {
1154
+ translate: -48px 0;
1155
+ }
1156
+
1157
+ [data-slot="navigation-menu-content"]:dir(
1158
+ rtl
1159
+ )[data-motion="from-end"][data-starting-style],
1160
+ [data-slot="navigation-menu-content"]:dir(
1161
+ rtl
1162
+ )[data-motion="to-end"][data-ending-style] {
1163
+ translate: -48px 0;
1164
+ }
1165
+
1166
+ [data-slot="navigation-menu-content"]:dir(
1167
+ rtl
1168
+ )[data-motion="from-start"][data-starting-style],
1169
+ [data-slot="navigation-menu-content"]:dir(
1170
+ rtl
1171
+ )[data-motion="to-start"][data-ending-style] {
1172
+ translate: 48px 0;
1173
+ }
1174
+
1175
+ @media (prefers-reduced-motion: reduce) {
1176
+ [data-slot="navigation-menu-viewport"],
1177
+ [data-slot="navigation-menu-content"] {
1178
+ transition: none;
1179
+ }
1180
+ }
1181
+ ```
1182
+
1183
+ ### `<ui-navigation-menu-content>`
1184
+ - `--anchor-width`: Without a viewport: width of the anchor, usually the trigger. Use it to match widths.
1185
+ - `--anchor-height`: Without a viewport: height of the anchor.
1186
+ - `--available-width`: Without a viewport: space beside the popup, up to the viewport edge. Not inherited either.
1187
+ - `--available-height`: Without a viewport: space between the anchor and the viewport edge. Use it for max-height. Not inherited, so a child that needs it copies it on the popup, like `[--list-height:var(--available-height)]`, and reads `max-h-(--list-height)`. The first popup registers both names for the whole page with `CSS.registerProperty`, so they don't inherit anywhere. A page that needs them to inherit calls it first, with `inherits: true`. An `@property` rule can't, since a registration in script wins.
1188
+ - `--transform-origin`: Without a viewport: the point on the anchor the popup opens from. Use it to scale from the right place.
1189
+ - `--arrow-offset`: Without a viewport: where the anchor's center falls along the popup's edge, kept `arrowPadding` from the corners. Position a ::before arrow with it.
1190
+
1191
+ ### `<ui-navigation-menu-viewport>`
1192
+ - `--anchor-width`: Width of the anchor, usually the trigger. Use it to match widths.
1193
+ - `--anchor-height`: Height of the anchor.
1194
+ - `--available-width`: Space beside the popup, up to the viewport edge. Not inherited either.
1195
+ - `--available-height`: Space between the anchor and the viewport edge. Use it for max-height. Not inherited, so a child that needs it copies it on the popup, like `[--list-height:var(--available-height)]`, and reads `max-h-(--list-height)`. The first popup registers both names for the whole page with `CSS.registerProperty`, so they don't inherit anywhere. A page that needs them to inherit calls it first, with `inherits: true`. An `@property` rule can't, since a registration in script wins.
1196
+ - `--transform-origin`: The point on the anchor the popup opens from. Use it to scale from the right place.
1197
+ - `--arrow-offset`: Where the anchor's center falls along the popup's edge, kept `arrowPadding` from the corners. Position a ::before arrow with it.
1198
+ - `--viewport-width`: The open content's width plus the viewport's own border. Transition width for a viewport that resizes as it switches.
1199
+ - `--viewport-height`: The open content's height plus the viewport's own border.
1200
+
1201
+ ### `<ui-navigation-menu-indicator>`
1202
+ - `--active-item-left`: Distance from its positioned ancestor's left edge to the open trigger, or the current page's link.
1203
+ - `--active-item-right`: Distance from its positioned ancestor's right edge to the item.
1204
+ - `--active-item-top`: Distance from its positioned ancestor's top edge to the item.
1205
+ - `--active-item-bottom`: Distance from its positioned ancestor's bottom edge to the item.
1206
+ - `--active-item-width`: Width of the item it sits under.
1207
+ - `--active-item-height`: Height of the item it sits under.
1208
+
1209
+ ## Data Attributes
1210
+
1211
+ ### `<ui-navigation-menu>`
1212
+ `[data-slot="navigation-menu"]`
1213
+
1214
+ - `data-orientation` (`horizontal`, `vertical`): Which arrow keys move between the items, and where panels open.
1215
+ - `data-nested`: Present on a menu inside another menu's content.
1216
+
1217
+ ### `<ui-navigation-menu-list>`
1218
+ `[data-slot="navigation-menu-list"]`
1219
+
1220
+ - `data-orientation` (`horizontal`, `vertical`): Which arrow keys move between the items, and where panels open.
1221
+ - `data-has-icons`: Present while any link inside holds an icon part, so items without one can line up.
1222
+
1223
+ ### `<ui-navigation-menu-item>`
1224
+ `[data-slot="navigation-menu-item"]`
1225
+
1226
+ - `data-closed`: Present while the item's panel is closed, including while it animates out.
1227
+ - `data-open`: Present while the item's panel is open.
1228
+
1229
+ ### `<ui-navigation-menu-trigger>`
1230
+ `[data-slot="navigation-menu-trigger"]`
1231
+
1232
+ - `data-closed`: Present while the item's panel is closed, including while it animates out.
1233
+ - `data-disabled`: Present when the trigger is disabled.
1234
+ - `data-open`: Present while the item's panel is open.
1235
+
1236
+ ### `<ui-navigation-menu-content>`
1237
+ `[data-slot="navigation-menu-content"]`
1238
+
1239
+ - `data-orientation` (`horizontal`, `vertical`): Which arrow keys move between the items, and where panels open.
1240
+ - `data-closed`: Present while the item's panel is closed, including while it animates out.
1241
+ - `data-open`: Present while the item's panel is open.
1242
+ - `data-starting-style`: Present for the first frame. Style the state you animate from.
1243
+ - `data-ending-style`: Present while it animates out. Style the state you animate to.
1244
+ - `data-motion` (`from-start`, `from-end`, `to-start`, `to-end`): Which way the content moves while another item takes over. Order is logical, so in right-to-left layouts the end is on the left.
1245
+ - `data-side` (`top`, `right`, `bottom`, `left`): Where the viewport, or a content without one, ended up after flipping away from edges.
1246
+ - `data-align` (`start`, `center`, `end`): How it lines up with the trigger.
1247
+
1248
+ ### `<ui-navigation-menu-link>`
1249
+ `[data-slot="navigation-menu-link"]`
1250
+
1251
+ - `data-has-icon`: Present on a link while it holds an icon part.
1252
+
1253
+ ### `<ui-navigation-menu-link-icon>`
1254
+ `[data-slot="navigation-menu-link-icon"]`
1255
+
1256
+ - `data-mirror`: Present on an icon that flips in right-to-left text, with `mirror`.
1257
+
1258
+ ### `<ui-navigation-menu-viewport>`
1259
+ `[data-slot="navigation-menu-viewport"]`
1260
+
1261
+ - `data-orientation` (`horizontal`, `vertical`): Which arrow keys move between the items, and where panels open.
1262
+ - `data-closed`: Present while every panel is closed, including while it animates out.
1263
+ - `data-open`: Present while a panel is open.
1264
+ - `data-starting-style`: Present for the first frame. Style the state you animate from.
1265
+ - `data-ending-style`: Present while it animates out. Style the state you animate to.
1266
+ - `data-side` (`top`, `right`, `bottom`, `left`): Where the viewport, or a content without one, ended up after flipping away from edges.
1267
+ - `data-align` (`start`, `center`, `end`): How it lines up with the trigger.
1268
+
1269
+ ### `<ui-navigation-menu-indicator>`
1270
+ `[data-slot="navigation-menu-indicator"]`
1271
+
1272
+ - `data-orientation` (`horizontal`, `vertical`): Which arrow keys move between the items, and where panels open.
1273
+ - `data-closed`: Present while every panel is closed.
1274
+ - `data-open`: Present while a panel is open, so it sits under a trigger.
1275
+ - `data-visible`: Present while there's a trigger or a current page's link to sit under.
1276
+
1277
+ ## API Reference
1278
+
1279
+ Props are attributes in kebab case, like `close-delay` and `side-offset`: `"false"` is false, and numbers are numbers. `anchor` and `container` take CSS selectors. Callbacks are DOM events.
1280
+
1281
+ ### NavigationMenu
1282
+
1283
+ - `container?` (`DocumentFragment | Element | null`): Renders the popup into this element instead of in place, like a container inside a shadow root or an iframe. On web components, a CSS selector. Nested popups stay inside their parent.
1284
+ - `value?` (`T | null`): The open item's value. Pass it to control the menu. `null` closes every panel.
1285
+ - `defaultValue?` (`T | null`): The item open at first, when uncontrolled.
1286
+ - `valuechange event`: Runs when an item wants to open or the open one to close. `details.reason` says why, and `details.cancel()` stops it.
1287
+ - `openchangecomplete event`: Runs once the menu has finished opening or closing, after any animation. Switching items doesn't count.
1288
+ - `delay?` (`number`): Milliseconds the pointer rests on a trigger before its panel opens. Moving to another trigger while one is open switches at once.
1289
+ - `closeDelay?` (`number`): Milliseconds before the open panel closes once the pointer leaves its trigger and the panel.
1290
+ - `openOnHover?` (`boolean`): Whether resting the pointer on a trigger opens its panel. Pressing a trigger always does, so `false` makes a click-only menu.
1291
+ - `closeOnSelect?` (`boolean`): Whether pressing a link closes the menu, like after a client-side route change. A link's own `closeOnSelect` wins. A press with a modifier key, which opens a new tab, never closes it.
1292
+ - `orientation?` (`NavigationMenuOrientation`): Which arrow keys move between the top-level items, and the side panels open on: below a horizontal menu, beside a vertical one.
1293
+ - `controller?` (`object`): Opens and closes panels from anywhere. Create one with `createNavigationMenuController()`.
1294
+ - `debug?` (`boolean`): Logs every state change with its reason to the console, in development builds.
1295
+
1296
+ ### NavigationMenuItem
1297
+
1298
+ - `value?` (`string`): Identifies the item, for `value` and `valuechange`. Generated when left out.
1299
+
1300
+ ### NavigationMenuTrigger
1301
+
1302
+ - `disabled?` (`boolean`): Stops the trigger from opening its panel. A native button can't be focused or pressed.
1303
+
1304
+ ### NavigationMenuContent
1305
+
1306
+ - `side?` (`Side`): Which side of the anchor the popup opens on. Flips to the other side when there isn't room. Which side of the trigger it opens on: below a horizontal menu, at the inline end of a vertical one.
1307
+ - `align?` (`Align`): How the popup lines up with the anchor along that side. `start` and `end` follow the reading direction. How it lines up with the trigger along that side.
1308
+ - `sideOffset?` (`number`): Gap between the anchor and the popup, in pixels. Gap between the trigger and it, in pixels.
1309
+ - `alignOffset?` (`number`): Shift along the anchor, in pixels. Shift along the trigger, in pixels.
1310
+ - `collisionPadding?` (`number`): Space to keep from the viewport edges, in pixels.
1311
+ - `arrowPadding?` (`number`): Space to keep between the arrow and the popup's corners, in pixels.
1312
+ - `anchor?` (`string | Anchor | null`): Positions the popup against this instead of its trigger: an element, a CSS selector, a point like `{ x, y }`, or anything with `getBoundingClientRect()`. It's read on every check, so it can move. A controller's `open({ anchor })` wins for that open.
1313
+ - `container?` (`DocumentFragment | Element | null`): Renders the popup into this element instead of in place, like a container inside a shadow root or an iframe. On web components, a CSS selector. Nested popups stay inside their parent.
1314
+ - `keepMounted?` (`boolean`): Whether the content stays in the page, hidden, while its item is closed, so crawlers and the server's HTML have it. Closed content stays in its item; it moves into the viewport when it opens.
1315
+ - `hiddenUntilFound?` (`boolean`): Whether closed content stays findable with `hidden="until-found"`, so find in page opens its item. Implies `keepMounted`.
1316
+
1317
+ ### NavigationMenuLink
1318
+
1319
+ - `current?` (`boolean`): Whether the link is the current page. Sets `aria-current="page"` and `data-current`.
1320
+ - `closeOnSelect?` (`boolean`): Whether pressing the link closes the menu. Overrides the menu's `closeOnSelect`.
1321
+
1322
+ ### NavigationMenuLinkIcon
1323
+
1324
+ - `mirror?` (`boolean`): Flips the icon horizontally in right-to-left text, for icons that point a way, like arrows and chevrons. Icons that picture a thing, like a clock or a check, stay as they are.
1325
+
1326
+ ### NavigationMenuViewport
1327
+
1328
+ - `side?` (`Side`): Which side of the anchor the popup opens on. Flips to the other side when there isn't room. Which side of the trigger it opens on: below a horizontal menu, at the inline end of a vertical one.
1329
+ - `align?` (`Align`): How the popup lines up with the anchor along that side. `start` and `end` follow the reading direction. How it lines up with the trigger along that side.
1330
+ - `sideOffset?` (`number`): Gap between the anchor and the popup, in pixels. Gap between the trigger and it, in pixels.
1331
+ - `alignOffset?` (`number`): Shift along the anchor, in pixels. Shift along the trigger, in pixels.
1332
+ - `collisionPadding?` (`number`): Space to keep from the viewport edges, in pixels.
1333
+ - `arrowPadding?` (`number`): Space to keep between the arrow and the popup's corners, in pixels.
1334
+ - `anchor?` (`string | Anchor | null`): Positions the popup against this instead of its trigger: an element, a CSS selector, a point like `{ x, y }`, or anything with `getBoundingClientRect()`. It's read on every check, so it can move. A controller's `open({ anchor })` wins for that open.
1335
+ - `container?` (`DocumentFragment | Element | null`): Renders the popup into this element instead of in place, like a container inside a shadow root or an iframe. On web components, a CSS selector. Nested popups stay inside their parent.
1336
+
1337
+ `ui-navigation-menu-list` takes no props of its own.
1338
+
1339
+ ### NavigationMenuIndicator
1340
+
1341
+ The indicator takes no props of its own.
1342
+
1343
+ ### State
1344
+
1345
+ The same state shows as data attributes on each element.
1346
+
1347
+ `ui-navigation-menu`:
1348
+
1349
+ - `value` (`T | null`): The open item's value, or null.
1350
+ - `open` (`boolean`): Whether a panel is open.
1351
+ - `orientation` (`NavigationMenuOrientation`): The menu's `orientation` option, or `"horizontal"` when it's unset or invalid.
1352
+ - `nested` (`boolean`): Whether the menu sits inside another one's content.
1353
+
1354
+ `ui-navigation-menu-list`:
1355
+
1356
+ - `open` (`boolean`): Whether a panel is open.
1357
+ - `orientation` (`NavigationMenuOrientation`): The menu's `orientation` option, or `"horizontal"` when it's unset or invalid.
1358
+
1359
+ `ui-navigation-menu-item`:
1360
+
1361
+ - `open` (`boolean`): Whether this item's panel is open.
1362
+
1363
+ `ui-navigation-menu-trigger`:
1364
+
1365
+ - `open` (`boolean`): Whether this trigger's panel is open.
1366
+ - `disabled` (`boolean`): Set by the trigger's `disabled` prop, which stops it opening its panel.
1367
+
1368
+ `ui-navigation-menu-content`:
1369
+
1370
+ - `open` (`boolean`): Whether this content's item is open. False while it animates out.
1371
+ - `motion` (`NavigationMenuMotion | null`): Which way it moves when another item takes over or gives way, or null when the menu opened or closed as a whole.
1372
+ - `orientation` (`NavigationMenuOrientation`): The menu's `orientation` option, or `"horizontal"` when it's unset or invalid.
1373
+
1374
+ `ui-navigation-menu-link`:
1375
+
1376
+ - `current` (`boolean`): Whether the link is the current page.
1377
+
1378
+ `ui-navigation-menu-link-icon`:
1379
+
1380
+ - `mirror` (`boolean`): Whether it flips in right-to-left text.
1381
+
1382
+ `ui-navigation-menu-viewport`:
1383
+
1384
+ - `open` (`boolean`): Whether a panel is open. False while it animates out.
1385
+ - `orientation` (`NavigationMenuOrientation`): The menu's `orientation` option, or `"horizontal"` when it's unset or invalid.
1386
+
1387
+ `ui-navigation-menu-indicator`:
1388
+
1389
+ - `open` (`boolean`): Whether a panel is open.
1390
+ - `orientation` (`NavigationMenuOrientation`): The menu's `orientation` option, or `"horizontal"` when it's unset or invalid.
1391
+
1392
+ ### Reasons
1393
+
1394
+ `event.detail.reason` says why an item opened or closed.
1395
+
1396
+ - `trigger-hover` (`PointerEvent`): The pointer rested on a trigger, or left it, with hover opening.
1397
+ - `trigger-press` (`MouseEvent | KeyboardEvent | TouchEvent`): Click, tap, Enter or Space on the part that opens, closes or toggles. Arrow keys that open a menu or submenu count too. A tap on a combobox's input that opens a sheet reports its `touchend`.
1398
+ - `escape-key` (`KeyboardEvent`): Escape, or a submenu's close key (ArrowLeft in left-to-right layouts).
1399
+ - `close-press` (`MouseEvent | KeyboardEvent | SubmitEvent`): A Close part was pressed, or a form with `method="dialog"` was submitted.
1400
+ - `outside-press` (`MouseEvent`): A press outside the layer.
1401
+ - `focus-out` (`KeyboardEvent | FocusEvent`): Focus left the layer, or Tab moved it out.
1402
+ - `link-press` (`MouseEvent | KeyboardEvent`): A link was pressed, which closes the navigation menu it is in, like after a route change.
1403
+ - `find-in-page` (`Event`): The browser's find in page matched hidden content.
1404
+ - `parent-close` (`none`): The parent layer closed.
1405
+ - `imperative` (`none`): A controller or `api` call, or a property set on a web component.
1406
+
1407
+ ### Controller
1408
+
1409
+ The element's `api` property has the same methods.
1410
+
1411
+ Every write method takes an optional `{ reason, event }`, passed to `valuechange`.
1412
+
1413
+ - `getValue()` (`() => T | null`): The open item's value, or null when every panel is closed.
1414
+ - `setValue(value, options?)` (`(value: T | null, options?: NavigationMenuChangeOptions | undefined) => boolean`): Opens an item's panel, or closes the open one with `null`. Returns false when canceled.
1415
+ - `isOpen()` (`() => boolean`): Whether a panel is open.
1416
+ - `close(options?)` (`(options?: NavigationMenuChangeOptions | undefined) => boolean`): Closes the open panel. Returns false when canceled or already closed.
1417
+ - `focus(value?)` (`(value?: T | undefined) => void`): Moves focus to an item's trigger: the open one, or the first one, without a value.
1418
+
1419
+ ### Events
1420
+
1421
+ Events fire on their own element and don't bubble, with the new state in `event.detail.value` and the `reason` and `event` beside it. Calling `preventDefault()` on `valuechange` cancels the change. Type the element as `UiNavigationMenuElement` for typed listeners.
1422
+
1423
+ | Member | On | Description |
1424
+ | --------------------------- | -------------------- | ---------------------------------------------------------------------- |
1425
+ | `value` property, attribute | `ui-navigation-menu` | The open item's value. The attribute sets where it starts, and is live |
1426
+ | `api` property | `ui-navigation-menu` | The [controller](#controller) |
1427
+ | `valuechange` event | `ui-navigation-menu` | `detail: { value, reason, event }` |
1428
+ | `openchangecomplete` event | `ui-navigation-menu` | `detail: { value: open }`, after the animation |
1429
+
1430
+ ## Accessibility
1431
+
1432
+ The menu follows the [disclosure navigation pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/): a landmark with a list of buttons that show and hide groups of links.
1433
+
1434
+ ### Keyboard
1435
+
1436
+ Tab moves through the triggers and top-level links in order, like any links on a page. Enter or Space on a trigger opens its panel and leaves focus on the trigger, and Tab from an open trigger moves into its panel, wherever the panel renders. Tab past the panel's last link moves on to the next item, and Shift+Tab from its first link returns to the trigger. Escape closes the panel and returns focus to its trigger, and focus leaving the navigation closes it.
1437
+
1438
+ The arrow keys add to Tab, as the pattern allows: Left and Right move between the top-level items (Up and Down in a vertical menu), Home and End go to the first and last, Down (the inline end in a vertical menu) opens a trigger's panel and moves into it, and in a panel Up and Down, or Left and Right, move between the links and Home and End go to the first and last. Nothing wraps, and keys pressed with a modifier, like Alt+Left for Back, are left to the browser.
1439
+
1440
+ Touch screens have no hover: a tap on a trigger opens its panel and another tap closes it.
1441
+
1442
+ ### Tested Behavior
1443
+
1444
+ Every line below is an automated test.
1445
+
1446
+ **Pointer**
1447
+
1448
+ - Resting the pointer on a trigger opens its panel.
1449
+ - The panel waits for its delay before opening.
1450
+ - Moving to another trigger while a panel is open switches at once.
1451
+ - Crossing another trigger on the way to the open panel keeps the panel.
1452
+ - Resting on another trigger on the way to the open panel switches to it.
1453
+ - Moving the pointer away closes the panel after its close delay.
1454
+ - Moving the pointer into the panel keeps it open.
1455
+ - Opening on hover leaves focus where it was.
1456
+ - Clicking a trigger opens its panel, and clicking it again closes it.
1457
+ - Clicking a trigger the pointer just opened keeps it open.
1458
+ - A press outside closes the panel.
1459
+ - A tap opens a panel and another tap closes it.
1460
+ - Escape closes a panel the pointer opened, which stays closed while the pointer rests.
1461
+ - Sweeping across every trigger leaves the menu working.
1462
+
1463
+ **Keyboard**
1464
+
1465
+ - Tab moves through the triggers and links in order while closed.
1466
+ - Enter opens a panel and leaves focus on its trigger.
1467
+ - Space toggles a panel.
1468
+ - Enter on another trigger switches panels.
1469
+ - Tab from an open trigger moves into its panel.
1470
+ - Tab past the panel's last link moves on to the next trigger.
1471
+ - Shift+Tab from the panel's first link returns to its trigger.
1472
+ - Shift+Tab from the item after an open panel goes back into the panel.
1473
+ - Escape inside a panel closes it and returns focus to its trigger.
1474
+ - Escape on an open trigger closes its panel.
1475
+ - Focus leaving the navigation closes the panel.
1476
+ - Right and Left arrows move between the top-level items.
1477
+ - Arrows stop at the first and last items.
1478
+ - ArrowDown on a trigger opens its panel.
1479
+ - Down and Up arrows move between the links in a panel.
1480
+ - An editable element in a panel keeps Down, and a tab stop that isn't editable passes it on.
1481
+ - A vertical menu moves between items with Down and Up.
1482
+
1483
+ **Links**
1484
+
1485
+ - A link in a panel follows its href and closes the menu.
1486
+ - Enter on a link follows it.
1487
+ - A link with closing turned off leaves the menu open.
1488
+ - The current page's link says so.
1489
+
1490
+ **Viewport**
1491
+
1492
+ - The viewport opens below the open trigger, lined up with its start.
1493
+ - The viewport takes the open panel's size.
1494
+ - The viewport moves to the newly open trigger.
1495
+
1496
+ **Kept content**
1497
+
1498
+ - Closed content kept mounted stays in the page, hidden.
1499
+ - The viewport follows the open panel when its size changes.
1500
+
1501
+ **Animation**
1502
+
1503
+ - The viewport and its content animate in from their starting style.
1504
+ - The viewport stays in the page while it animates out.
1505
+ - A menu opening from closed has no motion.
1506
+ - With reduced motion, panels switch and close without waiting.
1507
+ - The viewport appears in place, without sliding in from the corner.
1508
+ - Exit keyframes keyed on data-closed play on the content that gave way.
1509
+ - The content that gave way animates out while the next one comes in.
1510
+
1511
+ **Reasons**
1512
+
1513
+ - Changes report why the menu opened, switched and closed.
1514
+
1515
+ **Nested**
1516
+
1517
+ - A menu inside a panel opens its own panel.
1518
+ - A link in a nested panel closes every menu.
1519
+
1520
+ Runs in React, Vue and web components.
1521
+
1522
+ Beyond the pattern's baseline:
1523
+
1524
+ **Keyboard**
1525
+
1526
+ - ArrowDown on a trigger opens its panel and moves focus to its first link.
1527
+ - ArrowDown on an open trigger moves into its panel.
1528
+ - Home and End move to the first and last top-level items.
1529
+ - The same keys pressed without waiting still land.
1530
+
1531
+ **Pointer**
1532
+
1533
+ - With hover opening off, resting the pointer opens nothing and a click still does.
1534
+ - A click long after the pointer opened a panel still keeps it open.
1535
+ - An outside press reports itself, not a focus change.
1536
+ - The panel stays open while focus is in it, even when the pointer leaves.
1537
+
1538
+ **Links**
1539
+
1540
+ - A link pressed with a modifier key, which opens a new tab, leaves the menu open.
1541
+
1542
+ **Without a viewport**
1543
+
1544
+ - Each panel opens as its own popup below its trigger.
1545
+ - Panels switch, close on Escape and return focus like with one.
1546
+
1547
+ **Motion**
1548
+
1549
+ - Content coming from a later item enters from the end, and the one it replaced leaves toward the start.
1550
+ - Content coming from an earlier item enters from the start.
1551
+
1552
+ **Indicator**
1553
+
1554
+ - The indicator rests on the current page's link while closed.
1555
+ - The indicator moves under the open trigger.
1556
+ - The indicator follows an item added later as it grows. Web components only.
1557
+
1558
+ **Find in page**
1559
+
1560
+ - Find in page opens the item whose closed panel matched.
1561
+
1562
+ **Nested**
1563
+
1564
+ - A nested menu adds no second navigation landmark.
1565
+ - Escape closes only the nested panel, then the outer one.
1566
+
1567
+ Runs in React, Vue and web components.
1568
+
1569
+ Screen readers hear the same thing in each tested framework, recorded by a virtual screen reader:
1570
+
1571
+ - Announcements while opening a panel and tabbing through it.
1572
+
1573
+ Runs in React, Vue and web components.