@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,948 @@
1
+ # Tabs
2
+
3
+ Switch between related panels with one tab selected at a time.
4
+
5
+ Use Tabs when several views share the same space, such as settings sections or a preview and its source. Each tab opens the panel with the same value.
6
+
7
+ <ComponentExample
8
+ name="tabs/basic"
9
+ inspect
10
+ caption="Choose Password to show its panel. Use the left and right arrow keys to move between the tabs."
11
+ />
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @ui-sdk/web @ui-sdk/core
17
+ ```
18
+
19
+ Importing the component defines its elements. On the server the import does nothing.
20
+
21
+ ```ts
22
+ import "@ui-sdk/web/tabs"
23
+ ```
24
+
25
+ ## Composition
26
+
27
+ Use the following composition to build a `ui-tabs`:
28
+
29
+ ```text
30
+ ui-tabs
31
+ ├── ui-tabs-list
32
+ │ ├── ui-tabs-tab
33
+ │ │ └── button
34
+ │ └── ui-tabs-tab
35
+ │ └── button
36
+ ├── ui-tabs-panel
37
+ └── ui-tabs-panel
38
+ ```
39
+
40
+ Give each tab and its panel the same unique `value`. Give the list an accessible name. Put a native button inside each `ui-tabs-tab`. Set `default-value` to choose the initial tab; otherwise, the first enabled tab is selected.
41
+
42
+ ## Usage
43
+
44
+ Import the parts and put them together:
45
+
46
+ ```html
47
+ <script type="module">
48
+ import "@ui-sdk/web/tabs"
49
+ </script>
50
+
51
+ <ui-tabs default-value="account">
52
+ <ui-tabs-list aria-label="Settings">
53
+ <ui-tabs-tab value="account"
54
+ ><button type="button">Account</button></ui-tabs-tab
55
+ >
56
+ <ui-tabs-tab value="password"
57
+ ><button type="button">Password</button></ui-tabs-tab
58
+ >
59
+ </ui-tabs-list>
60
+ <ui-tabs-panel value="account">Manage your profile.</ui-tabs-panel>
61
+ <ui-tabs-panel value="password">Choose a new password.</ui-tabs-panel>
62
+ </ui-tabs>
63
+ ```
64
+
65
+ ## Build Your Own
66
+
67
+ `@ui-sdk/web/tabs` comes with ready-made `ui-tabs` elements. If you'd like to build your own, with your own element names, start from the `tabs` primitive in `@ui-sdk/core`:
68
+
69
+ ```ts title="segments.ts"
70
+ import { tabs } from "@ui-sdk/core/tabs"
71
+ import { createParts, wrappedButton } from "@ui-sdk/web"
72
+
73
+ createParts(tabs, {
74
+ root: { tag: "segment-group", display: "block" },
75
+ list: { tag: "segment-list", display: "block" },
76
+ tab: { tag: "segment-tab", target: wrappedButton, display: "contents" },
77
+ panel: { tag: "segment-panel", display: "block" },
78
+ })
79
+ ```
80
+
81
+ ```html
82
+ <segment-group default-value="day">
83
+ <segment-list aria-label="Range">
84
+ <segment-tab value="day"><button type="button">Day</button></segment-tab>
85
+ <segment-tab value="week"><button type="button">Week</button></segment-tab>
86
+ </segment-list>
87
+ <segment-panel value="day">128 visitors today.</segment-panel>
88
+ <segment-panel value="week">1,024 visitors this week.</segment-panel>
89
+ </segment-group>
90
+ ```
91
+
92
+ `wrappedButton` puts each tab's behavior and attributes on the button inside it. Add `"tab-icon"` and `indicator` elements to get those parts too. Each element keeps the behavior, state and data attributes of its ready-made counterpart. See [Rendering](../rendering.md) and [Custom Parts](../custom-parts.md) for more.
93
+
94
+ ## Vertical
95
+
96
+ Set `orientation="vertical"` to use Up and Down Arrow instead. Style the list as a column beside the panels to match.
97
+
98
+ ```ts title="vertical.ts"
99
+ import "@ui-sdk/web/tabs"
100
+
101
+ function mount(root: HTMLElement) {
102
+ root.innerHTML = `
103
+ <ui-tabs default-value="general" orientation="vertical">
104
+ <ui-tabs-list aria-label="Preferences">
105
+ <ui-tabs-tab value="general">
106
+ <button type="button">General</button>
107
+ </ui-tabs-tab>
108
+ <ui-tabs-tab value="security">
109
+ <button type="button">Security</button>
110
+ </ui-tabs-tab>
111
+ <ui-tabs-tab value="integrations">
112
+ <button type="button">Integrations</button>
113
+ </ui-tabs-tab>
114
+ </ui-tabs-list>
115
+ <ui-tabs-panel value="general">Choose your language, time zone and the page you start on.</ui-tabs-panel>
116
+ <ui-tabs-panel value="security">Manage passkeys, signed-in sessions and recovery codes.</ui-tabs-panel>
117
+ <ui-tabs-panel value="integrations">Connect your calendar and chat apps to get reminders.</ui-tabs-panel>
118
+ </ui-tabs>
119
+ `
120
+ }
121
+
122
+ export { mount }
123
+ ```
124
+
125
+ Focus General, then press Down Arrow to choose Security. Up Arrow moves back through the vertical list.
126
+
127
+ ## Disabled
128
+
129
+ Add `disabled` to a tab to prevent selection. Arrow keys can still focus it so people can discover it.
130
+
131
+ ```ts title="disabled.ts"
132
+ import "@ui-sdk/web/tabs"
133
+
134
+ function mount(root: HTMLElement) {
135
+ root.innerHTML = `
136
+ <ui-tabs default-value="overview">
137
+ <ui-tabs-list aria-label="Dashboard">
138
+ <ui-tabs-tab value="overview">
139
+ <button type="button">Overview</button>
140
+ </ui-tabs-tab>
141
+ <ui-tabs-tab value="analytics">
142
+ <button type="button">Analytics</button>
143
+ </ui-tabs-tab>
144
+ <ui-tabs-tab value="reports" disabled>
145
+ <button type="button">Reports</button>
146
+ </ui-tabs-tab>
147
+ </ui-tabs-list>
148
+ <ui-tabs-panel value="overview">Revenue, visitors and sign-ups for the last 30 days.</ui-tabs-panel>
149
+ <ui-tabs-panel value="analytics">Where your visitors come from and the pages they read most.</ui-tabs-panel>
150
+ <ui-tabs-panel value="reports">Monthly reports are available on the Pro plan.</ui-tabs-panel>
151
+ </ui-tabs>
152
+ `
153
+ }
154
+
155
+ export { mount }
156
+ ```
157
+
158
+ Use the arrow keys to reach Reports. It can receive focus, but pressing it leaves the current panel selected.
159
+
160
+ ## Icons
161
+
162
+ Put decorative icons in `ui-tabs-tab-icon` so they stay out of the tab’s accessible name. Add `mirror` to directional icons, such as Reply, to flip them in right-to-left text. An icon-only tab still needs an `aria-label`.
163
+
164
+ ```ts title="icons.ts"
165
+ import "@ui-sdk/web/tabs"
166
+ import { Archive, createElement, Mail, Reply } from "lucide"
167
+
168
+ function mount(root: HTMLElement) {
169
+ root.innerHTML = `
170
+ <ui-tabs default-value="inbox">
171
+ <ui-tabs-list aria-label="Mail">
172
+ <ui-tabs-tab value="inbox">
173
+ <button type="button">
174
+ <ui-tabs-tab-icon>${createElement(Mail, { "aria-hidden": "true" }).outerHTML}</ui-tabs-tab-icon>
175
+ Inbox
176
+ </button>
177
+ </ui-tabs-tab>
178
+ <ui-tabs-tab value="replies">
179
+ <button type="button">
180
+ <ui-tabs-tab-icon mirror>${createElement(Reply, { "aria-hidden": "true" }).outerHTML}</ui-tabs-tab-icon>
181
+ Replies
182
+ </button>
183
+ </ui-tabs-tab>
184
+ <ui-tabs-tab value="archive">
185
+ <button type="button">
186
+ <ui-tabs-tab-icon>${createElement(Archive, { "aria-hidden": "true" }).outerHTML}</ui-tabs-tab-icon>
187
+ Archive
188
+ </button>
189
+ </ui-tabs-tab>
190
+ </ui-tabs-list>
191
+ <ui-tabs-panel value="inbox">3 new messages from Inès Moreau and Kofi Mensah.</ui-tabs-panel>
192
+ <ui-tabs-panel value="replies">Wren Takahashi replied to your message about the launch.</ui-tabs-panel>
193
+ <ui-tabs-panel value="archive">Messages you archived stay here and in search.</ui-tabs-panel>
194
+ </ui-tabs>
195
+ `
196
+ }
197
+
198
+ export { mount }
199
+ ```
200
+
201
+ Choose Replies. Its directional icon mirrors when the surrounding page uses right-to-left text.
202
+
203
+ ## Manual Activation
204
+
205
+ Set `activation="manual"` when opening a panel takes time, such as a view that fetches data. Arrow keys move focus; Enter or Space selects the focused tab.
206
+
207
+ ```html
208
+ <ui-tabs activation="manual"></ui-tabs>
209
+ ```
210
+
211
+ A panel becomes a tab stop only when it has no focusable content. A panel with a field lets Tab go straight to the field.
212
+
213
+ ```ts title="manual.ts"
214
+ import "@ui-sdk/web/field"
215
+ import "@ui-sdk/web/input"
216
+ import "@ui-sdk/web/tabs"
217
+
218
+ function mount(root: HTMLElement) {
219
+ root.innerHTML = `
220
+ <ui-tabs default-value="preview" activation="manual">
221
+ <ui-tabs-list aria-label="Snippet">
222
+ <ui-tabs-tab value="preview">
223
+ <button type="button">Preview</button>
224
+ </ui-tabs-tab>
225
+ <ui-tabs-tab value="code">
226
+ <button type="button">Code</button>
227
+ </ui-tabs-tab>
228
+ </ui-tabs-list>
229
+ <ui-tabs-panel value="preview">Welcome back, Hollis. You have 3 unread messages.</ui-tabs-panel>
230
+ <ui-tabs-panel value="code">
231
+ <ui-field>
232
+ <ui-field-label>Source</ui-field-label>
233
+ <ui-input>
234
+ <input value='&lt;Welcome name="Hollis" unread={3} /&gt;' />
235
+ </ui-input>
236
+ </ui-field>
237
+ </ui-tabs-panel>
238
+ </ui-tabs>
239
+ `
240
+ }
241
+
242
+ export { mount }
243
+ ```
244
+
245
+ Focus Preview, then press Right Arrow. Code receives focus; press Enter or Space to show its panel. Tab then moves into the Source field.
246
+
247
+ ## Loop
248
+
249
+ Arrow keys normally wrap around at either end. Set `loop="false"` to stop there instead.
250
+
251
+ ```ts title="loop.ts"
252
+ import "@ui-sdk/web/tabs"
253
+
254
+ const ranges = [
255
+ {
256
+ value: "day",
257
+ label: "Day",
258
+ content: "128 visitors today, up 12% from yesterday.",
259
+ },
260
+ {
261
+ value: "week",
262
+ label: "Week",
263
+ content: "1,024 visitors this week, up 8% from last week.",
264
+ },
265
+ {
266
+ value: "month",
267
+ label: "Month",
268
+ content: "4,210 visitors this month, down 3% from last month.",
269
+ },
270
+ {
271
+ value: "year",
272
+ label: "Year",
273
+ content: "48,930 visitors this year, up 21% from last year.",
274
+ },
275
+ ]
276
+
277
+ function mount(root: HTMLElement) {
278
+ root.innerHTML = `
279
+ <ui-tabs default-value="day" loop="false">
280
+ <ui-tabs-list aria-label="Range">
281
+ ${ranges
282
+ .map(
283
+ (range) => `
284
+ <ui-tabs-tab value="${range.value}">
285
+ <button type="button">${range.label}</button>
286
+ </ui-tabs-tab>`
287
+ )
288
+ .join("")}
289
+ </ui-tabs-list>
290
+ ${ranges.map((range) => `<ui-tabs-panel value="${range.value}">${range.content}</ui-tabs-panel>`).join("")}
291
+ </ui-tabs>
292
+ `
293
+ }
294
+
295
+ export { mount }
296
+ ```
297
+
298
+ Focus Year and press Right Arrow. Focus stays at the end because looping is off.
299
+
300
+ ## Indicator
301
+
302
+ `ui-tabs-indicator` measures the selected tab. Position it inside a relative tab list and use its [CSS variables](#styling-tabsindicator) to make a marker that follows the selection. It measures again when a tab or the list changes size.
303
+
304
+ ```css
305
+ [data-slot="tabs-list"] {
306
+ position: relative;
307
+ }
308
+
309
+ [data-slot="tabs-indicator"] {
310
+ position: absolute;
311
+ top: var(--active-tab-top);
312
+ left: var(--active-tab-left);
313
+ width: var(--active-tab-width);
314
+ height: var(--active-tab-height);
315
+ transition-property: top, left, width, height;
316
+ transition-duration: 200ms;
317
+
318
+ &[data-activation-direction="none"] {
319
+ transition: none;
320
+ }
321
+ }
322
+ ```
323
+
324
+ Add transitions for the properties you animate. Skip them while `data-activation-direction="none"` to avoid animating the first placement.
325
+
326
+ ```ts title="indicator.ts"
327
+ import "@ui-sdk/web/tabs"
328
+
329
+ const orientations = [
330
+ { value: "horizontal", label: "Horizontal" },
331
+ { value: "vertical", label: "Vertical" },
332
+ ]
333
+
334
+ function mount(root: HTMLElement) {
335
+ root.innerHTML = `
336
+ <div>
337
+ <div data-slot="controls" role="group" aria-label="Orientation">
338
+ ${orientations
339
+ .map(
340
+ (option) => `
341
+ <button type="button" data-size="sm" value="${option.value}" aria-pressed="${option.value === "horizontal"}">
342
+ ${option.label}
343
+ </button>`
344
+ )
345
+ .join("")}
346
+ </div>
347
+ <ui-tabs default-value="music" orientation="horizontal">
348
+ <ui-tabs-list aria-label="Library">
349
+ <ui-tabs-tab value="music">
350
+ <button type="button">Music</button>
351
+ </ui-tabs-tab>
352
+ <ui-tabs-tab value="podcasts">
353
+ <button type="button">Podcasts</button>
354
+ </ui-tabs-tab>
355
+ <ui-tabs-tab value="audiobooks">
356
+ <button type="button">Audiobooks</button>
357
+ </ui-tabs-tab>
358
+ <ui-tabs-indicator></ui-tabs-indicator>
359
+ </ui-tabs-list>
360
+ <ui-tabs-panel value="music">312 songs in 24 albums, synced to this device.</ui-tabs-panel>
361
+ <ui-tabs-panel value="podcasts">8 shows you follow, with 3 new episodes.</ui-tabs-panel>
362
+ <ui-tabs-panel value="audiobooks">2 books in progress, 6 hours left in total.</ui-tabs-panel>
363
+ </ui-tabs>
364
+ </div>
365
+ `
366
+ const tabs = root.querySelector("ui-tabs")!
367
+ const buttons = root.querySelectorAll<HTMLButtonElement>(
368
+ '[data-slot="controls"] button'
369
+ )
370
+ for (const button of buttons) {
371
+ button.addEventListener("click", () => {
372
+ tabs.setAttribute("orientation", button.value)
373
+ for (const other of buttons) {
374
+ other.setAttribute("aria-pressed", String(other === button))
375
+ }
376
+ })
377
+ }
378
+ }
379
+
380
+ export { mount }
381
+ ```
382
+
383
+ Choose Podcasts, then choose Vertical. The same indicator measures and follows the selected tab in either layout.
384
+
385
+ ## Keep Mounted
386
+
387
+ Web components keep the supplied markup when a panel closes. Add `keep-mounted` to keep the hidden panel’s role and its `aria-controls` relationship too.
388
+
389
+ ```ts title="keep-mounted.ts"
390
+ import "@ui-sdk/web/field"
391
+ import "@ui-sdk/web/textarea"
392
+ import "@ui-sdk/web/tabs"
393
+
394
+ function mount(root: HTMLElement) {
395
+ root.innerHTML = `
396
+ <ui-tabs default-value="reply">
397
+ <ui-tabs-list aria-label="Comment">
398
+ <ui-tabs-tab value="reply">
399
+ <button type="button">Reply</button>
400
+ </ui-tabs-tab>
401
+ <ui-tabs-tab value="formatting">
402
+ <button type="button">Formatting</button>
403
+ </ui-tabs-tab>
404
+ <ui-tabs-indicator></ui-tabs-indicator>
405
+ </ui-tabs-list>
406
+ <ui-tabs-panel value="reply" keep-mounted>
407
+ <ui-field>
408
+ <ui-field-label>Message</ui-field-label>
409
+ <ui-textarea>
410
+ <textarea rows="3" placeholder="Write a reply…"></textarea>
411
+ </ui-textarea>
412
+ </ui-field>
413
+ </ui-tabs-panel>
414
+ <ui-tabs-panel value="formatting">
415
+ <p>Use **bold**, _italics_ and \`code\` to format your reply.</p>
416
+ <p>Paste a link on its own line to show a preview.</p>
417
+ </ui-tabs-panel>
418
+ </ui-tabs>
419
+ `
420
+ }
421
+
422
+ export { mount }
423
+ ```
424
+
425
+ Write a message, switch to Formatting, then return to Reply. Your draft is still there.
426
+
427
+ ## Find in Page
428
+
429
+ Add `hidden-until-found` to the tabs or a panel to keep closed content available to the browser’s Find command. A match selects its tab with the reason `find-in-page`. This also keeps the panel mounted.
430
+
431
+ Write `hidden="until-found"` on the initially hidden panels too if they should be findable before the elements are defined.
432
+
433
+ ```ts title="find-in-page.ts"
434
+ import "@ui-sdk/web/tabs"
435
+
436
+ const policies = [
437
+ {
438
+ value: "shipping",
439
+ label: "Shipping",
440
+ text: "Orders leave our warehouse within two working days. Express delivery arrives the next morning.",
441
+ },
442
+ {
443
+ value: "returns",
444
+ label: "Returns",
445
+ text: "Send anything back within 30 days. Opened electronics carry a restocking fee of 10 percent.",
446
+ },
447
+ {
448
+ value: "warranty",
449
+ label: "Warranty",
450
+ text: "Every product has a two-year warranty. Batteries are covered for one year from delivery.",
451
+ },
452
+ ]
453
+
454
+ function mount(root: HTMLElement) {
455
+ root.innerHTML = `
456
+ <ui-tabs default-value="shipping" hidden-until-found>
457
+ <ui-tabs-list aria-label="Policies">
458
+ ${policies
459
+ .map(
460
+ (policy) => `
461
+ <ui-tabs-tab value="${policy.value}">
462
+ <button type="button">${policy.label}</button>
463
+ </ui-tabs-tab>`
464
+ )
465
+ .join("")}
466
+ </ui-tabs-list>
467
+ ${policies
468
+ .map(
469
+ (policy, index) =>
470
+ `<ui-tabs-panel value="${policy.value}"${index > 0 ? ' hidden="until-found"' : ""}>${policy.text}</ui-tabs-panel>`
471
+ )
472
+ .join("")}
473
+ </ui-tabs>
474
+ `
475
+ }
476
+
477
+ export { mount }
478
+ ```
479
+
480
+ Use your browser’s Find command to search for “restocking fee”. Returns opens to reveal the match.
481
+
482
+ ## Animation
483
+
484
+ Panels expose `data-starting-style` as they enter and `data-ending-style` as they leave. The previous panel stays mounted through its exit animation, then unmounts or hides if it is kept mounted.
485
+
486
+ Use `data-activation-direction` to choose a slide direction. Put the panels in the same grid cell so they overlap during the transition, as this example does.
487
+
488
+ ```ts title="animation.ts"
489
+ import "@ui-sdk/web/tabs"
490
+
491
+ function mount(root: HTMLElement) {
492
+ root.innerHTML = `
493
+ <ui-tabs default-value="overview">
494
+ <ui-tabs-list aria-label="Project">
495
+ <ui-tabs-tab value="overview">
496
+ <button type="button">Overview</button>
497
+ </ui-tabs-tab>
498
+ <ui-tabs-tab value="activity">
499
+ <button type="button">Activity</button>
500
+ </ui-tabs-tab>
501
+ <ui-tabs-tab value="settings">
502
+ <button type="button">Settings</button>
503
+ </ui-tabs-tab>
504
+ <ui-tabs-indicator></ui-tabs-indicator>
505
+ </ui-tabs-list>
506
+ <div data-slot="stack">
507
+ <ui-tabs-panel value="overview">The launch is on track for Friday, with 4 of 6 tasks done.</ui-tabs-panel>
508
+ <ui-tabs-panel value="activity">Inès Moreau deployed to production and Kofi Mensah left a review.</ui-tabs-panel>
509
+ <ui-tabs-panel value="settings">Rename the project, transfer it to another team or archive it.</ui-tabs-panel>
510
+ </div>
511
+ </ui-tabs>
512
+ `
513
+ }
514
+
515
+ export { mount }
516
+ ```
517
+
518
+ Move between Overview, Activity and Settings. The panels slide in the direction you moved.
519
+
520
+ ## Controlled
521
+
522
+ Read and set the element’s `value` property, or call its `api.select(value)` method. Either one selects a tab and fires `valuechange`, like choosing a tab does. [State and Events](../state-and-events.md) covers the shared pattern. Call `event.preventDefault()` on `valuechange` to reject a change.
523
+
524
+ `valuechange` receives the new value and a [reason](#reasons), such as `item-press`, `list-navigation`, or `imperative`, in `event.detail`. It does not fire for the initial selection. The element’s [`api`](#controller) lets another control select a tab too.
525
+
526
+ ```ts title="controlled.ts"
527
+ import "@ui-sdk/web/tabs"
528
+ import { log } from "./log"
529
+
530
+ function mount(root: HTMLElement) {
531
+ root.innerHTML = `
532
+ <div>
533
+ <div data-slot="controls">
534
+ <button type="button">Show Sent Mail</button>
535
+ </div>
536
+ <ui-tabs default-value="inbox">
537
+ <ui-tabs-list aria-label="Mail">
538
+ <ui-tabs-tab value="inbox">
539
+ <button type="button">Inbox</button>
540
+ </ui-tabs-tab>
541
+ <ui-tabs-tab value="drafts">
542
+ <button type="button">Drafts</button>
543
+ </ui-tabs-tab>
544
+ <ui-tabs-tab value="sent">
545
+ <button type="button">Sent</button>
546
+ </ui-tabs-tab>
547
+ <ui-tabs-indicator></ui-tabs-indicator>
548
+ </ui-tabs-list>
549
+ <ui-tabs-panel value="inbox">3 unread messages from Hollis Grant and Inès Moreau.</ui-tabs-panel>
550
+ <ui-tabs-panel value="drafts">2 drafts, the last one edited yesterday at 4:12 PM.</ui-tabs-panel>
551
+ <ui-tabs-panel value="sent">12 messages sent this week, all delivered.</ui-tabs-panel>
552
+ </ui-tabs>
553
+ </div>
554
+ `
555
+ const tabs = root.querySelector("ui-tabs")!
556
+ tabs.addEventListener("valuechange", (event) => {
557
+ const { value, reason } = event.detail
558
+ log(root, `value: ${value} (${reason})`)
559
+ })
560
+ root
561
+ .querySelector('[data-slot="controls"] button')!
562
+ .addEventListener("click", () => tabs.api.select("sent"))
563
+ }
564
+
565
+ export { mount }
566
+ ```
567
+
568
+ Choose Drafts, then press Show Sent Mail. The console logs each new value with its reason.
569
+
570
+ ## Links
571
+
572
+ Render a tab as a link when selection should navigate. It keeps its tab role; arrow keys follow the link when they select it. Modified clicks, such as Cmd-click, keep their normal browser behavior without selecting the tab.
573
+
574
+ ```html
575
+ <ui-tabs-tab value="activity">
576
+ <a href="#activity">Activity</a>
577
+ </ui-tabs-tab>
578
+ ```
579
+
580
+ ```ts title="links.ts"
581
+ import "@ui-sdk/web/tabs"
582
+
583
+ const pages = [
584
+ {
585
+ value: "overview",
586
+ href: "#project-overview",
587
+ label: "Overview",
588
+ content:
589
+ "Hollis Grant started this project in March. It has 14 open issues.",
590
+ },
591
+ {
592
+ value: "activity",
593
+ href: "#project-activity",
594
+ label: "Activity",
595
+ content:
596
+ "Wren Takahashi merged 3 pull requests and closed 5 issues this week.",
597
+ },
598
+ {
599
+ value: "members",
600
+ href: "#project-members",
601
+ label: "Members",
602
+ content:
603
+ "Hollis Grant owns the project. Wren Takahashi and 3 others can edit it.",
604
+ },
605
+ ]
606
+
607
+ function mount(root: HTMLElement) {
608
+ root.innerHTML = `
609
+ <ui-tabs default-value="overview">
610
+ <ui-tabs-list aria-label="Project">
611
+ ${pages
612
+ .map(
613
+ (page) => `
614
+ <ui-tabs-tab value="${page.value}">
615
+ <a href="${page.href}">${page.label}</a>
616
+ </ui-tabs-tab>`
617
+ )
618
+ .join("")}
619
+ <ui-tabs-indicator></ui-tabs-indicator>
620
+ </ui-tabs-list>
621
+ ${pages.map((page) => `<ui-tabs-panel value="${page.value}">${page.content}</ui-tabs-panel>`).join("")}
622
+ </ui-tabs>
623
+ `
624
+ }
625
+
626
+ export { mount }
627
+ ```
628
+
629
+ Choose Activity. Its panel opens and the URL hash changes. Modified clicks keep the link’s usual browser behavior.
630
+
631
+ ## Query String
632
+
633
+ Use the URL as the source of the selected value, and validate it against your tab values. Read the parameter on the server when possible, then write it as the `value` attribute. Write changes through your router so browser navigation and the rendered page stay in sync. For a plain client page, keep the rest of the URL when updating it:
634
+
635
+ ```ts
636
+ function saveSelectedTab(value: string | null) {
637
+ const url = new URL(location.href)
638
+ if (value === null) {
639
+ url.searchParams.delete("tab")
640
+ } else {
641
+ url.searchParams.set("tab", value)
642
+ }
643
+ history.replaceState(null, "", url)
644
+ }
645
+ ```
646
+
647
+ Call this from `valuechange` after accepting the value. Read it again on `popstate` if your page uses browser history directly; remove that listener when the view unmounts.
648
+
649
+ ## URL Hash
650
+
651
+ A hash such as `#activity` works on a client-rendered page. Read it after mount and on `hashchange`, then select the matching tab. Setting `location.hash` adds a history entry. The server cannot read the hash, so give the page an initial tab while it loads.
652
+
653
+ ## Route Tabs
654
+
655
+ When each tab is a route, render [links](#links) and set the element’s `value` from the current path. Let navigation update the value. You do not also need a change handler that navigates to the same route. If the server renders the markup, set the initial `value` and hide the other panels there too.
656
+
657
+ ## Scroll into View
658
+
659
+ If the list scrolls, selecting a tab through the `api`, the `value` or the URL scrolls it into view inside that list. Pointer and arrow-key selection focus the tab, so the browser reveals it as part of focusing it.
660
+
661
+ ## Custom Rendering
662
+
663
+ Put a native button or link inside `ui-tabs-tab`, as in the [links](#links) example; it receives the tab behavior, and anything else inside it is yours. [Parts and Composition](../parts-and-composition.md) covers the shared rendering API. Each part’s state is listed under [State](#state).
664
+
665
+ ```ts title="render.ts"
666
+ import "@ui-sdk/web/tabs"
667
+
668
+ const folders = [
669
+ {
670
+ value: "inbox",
671
+ label: "Inbox",
672
+ count: 12,
673
+ content: "12 conversations, 3 of them unread.",
674
+ },
675
+ {
676
+ value: "unread",
677
+ label: "Unread",
678
+ count: 3,
679
+ content: "Hollis Grant, Inès Moreau and Kofi Mensah wrote to you.",
680
+ },
681
+ {
682
+ value: "archived",
683
+ label: "Archived",
684
+ count: 0,
685
+ content: "Nothing archived yet. Archived mail stays searchable.",
686
+ },
687
+ ]
688
+
689
+ function mount(root: HTMLElement) {
690
+ root.innerHTML = `
691
+ <ui-tabs default-value="inbox">
692
+ <ui-tabs-list aria-label="Mail">
693
+ ${folders
694
+ .map(
695
+ (folder) => `
696
+ <ui-tabs-tab value="${folder.value}">
697
+ <button type="button">
698
+ ${folder.label}
699
+ ${folder.count > 0 ? `<span data-slot="badge">${folder.count}</span>` : ""}
700
+ </button>
701
+ </ui-tabs-tab>`
702
+ )
703
+ .join("")}
704
+ </ui-tabs-list>
705
+ ${folders.map((folder) => `<ui-tabs-panel value="${folder.value}">${folder.content}</ui-tabs-panel>`).join("")}
706
+ </ui-tabs>
707
+ `
708
+ }
709
+
710
+ export { mount }
711
+ ```
712
+
713
+ Each tab renders its own button with an unread count after the label. Choose Unread to see who wrote to you.
714
+
715
+ ## Styling
716
+
717
+ Parts are unstyled. Use `data-selected` and `data-disabled` to style a tab, and `data-selected` on a panel that shows. `data-orientation` on every part lets one stylesheet lay out horizontal and vertical tabs. Icon parts also set `data-has-icon` on the tab and `data-has-icons` on the list, which can help align mixed text and icon tabs.
718
+
719
+ The indicator’s position and size are measured from the tab list into the variables below. Use them for an underline, a background, or a marker beside a vertical list.
720
+
721
+ Each part has a `data-slot`, such as `tabs-tab`, and its state attributes are listed under [Data Attributes](#data-attributes). A tab's attributes land on its button.
722
+
723
+ ### `<ui-tabs-indicator>`
724
+ - `--active-tab-left`: Distance from the tab list's left edge to the selected tab.
725
+ - `--active-tab-right`: Distance from the tab list's right edge to the selected tab.
726
+ - `--active-tab-top`: Distance from the tab list's top edge to the selected tab.
727
+ - `--active-tab-bottom`: Distance from the tab list's bottom edge to the selected tab.
728
+ - `--active-tab-width`: Width of the selected tab.
729
+ - `--active-tab-height`: Height of the selected tab.
730
+
731
+ ## Data Attributes
732
+
733
+ ### `<ui-tabs>`
734
+ `[data-slot="tabs"]`
735
+
736
+ - `data-orientation` (`horizontal`, `vertical`): Which arrow keys move between tabs.
737
+ - `data-activation-direction` (`none`, `left`, `right`, `up`, `down`): Which way the last selection moved on screen. Use it to slide panels in from the right side.
738
+
739
+ ### `<ui-tabs-list>`
740
+ `[data-slot="tabs-list"]`
741
+
742
+ - `data-orientation` (`horizontal`, `vertical`): Which arrow keys move between tabs.
743
+ - `data-activation-direction` (`none`, `left`, `right`, `up`, `down`): Which way the last selection moved on screen. Use it to slide panels in from the right side.
744
+ - `data-has-icons`: Present while any tab inside holds an icon part, so items without one can line up.
745
+
746
+ ### `<ui-tabs-tab>`
747
+ `[data-slot="tabs-tab"]`
748
+
749
+ - `data-selected`: Present on the selected tab and its panel.
750
+ - `data-orientation` (`horizontal`, `vertical`): Which arrow keys move between tabs.
751
+ - `data-activation-direction` (`none`, `left`, `right`, `up`, `down`): Which way the last selection moved on screen. Use it to slide panels in from the right side.
752
+ - `data-disabled`: Present when the tab can't be selected.
753
+ - `data-has-icon`: Present on a tab while it holds an icon part.
754
+
755
+ ### `<ui-tabs-tab-icon>`
756
+ `[data-slot="tabs-tab-icon"]`
757
+
758
+ - `data-mirror`: Present on an icon that flips in right-to-left text, with `mirror`.
759
+
760
+ ### `<ui-tabs-indicator>`
761
+ `[data-slot="tabs-indicator"]`
762
+
763
+ - `data-orientation` (`horizontal`, `vertical`): Which arrow keys move between tabs.
764
+ - `data-activation-direction` (`none`, `left`, `right`, `up`, `down`): Which way the last selection moved on screen. Use it to slide panels in from the right side.
765
+
766
+ ### `<ui-tabs-panel>`
767
+ `[data-slot="tabs-panel"]`
768
+
769
+ - `data-selected`: Present on the selected tab and its panel.
770
+ - `data-orientation` (`horizontal`, `vertical`): Which arrow keys move between tabs.
771
+ - `data-activation-direction` (`none`, `left`, `right`, `up`, `down`): Which way the last selection moved on screen. Use it to slide panels in from the right side.
772
+ - `data-starting-style`: Present for a panel's first frame. Style the state you animate from.
773
+ - `data-ending-style`: Present while the previous panel animates out. Style the state you animate to.
774
+
775
+ ## API Reference
776
+
777
+ Props are attributes in kebab case, like `default-value`, `keep-mounted` and `hidden-until-found`: `"false"` is false. Callbacks are DOM events.
778
+
779
+ ### Tabs
780
+
781
+ - `value?` (`T | null`): The selected tab's value. Pass it to control the tabs. `null` selects nothing.
782
+ - `defaultValue?` (`T | null`): The tab selected at first, when uncontrolled. Defaults to the first enabled tab, which never calls `valuechange`.
783
+ - `valuechange event`: Runs when the selection wants to change. `details.reason` says why, and `details.cancel()` stops it.
784
+ - `orientation?` (`TabsOrientation`): Which arrow keys move between tabs: left and right, or up and down.
785
+ - `activation?` (`"automatic" | "manual"`): `"automatic"` selects a tab as soon as arrow keys focus it. `"manual"` waits for Enter or Space.
786
+ - `loop?` (`boolean`): Whether arrow keys wrap from the last tab to the first.
787
+ - `hiddenUntilFound?` (`boolean`): Keeps hidden panels in the page as `hidden="until-found"`, so the browser's find in page can match their text and select their tab. Implies `keepMounted`.
788
+ - `controller?` (`{ getValue: () => T | null; setValue: (value: T | null, options?: TabsChangeOptions) => boolean; select: (value: T, options?: TabsChangeOptions) => boolean; focus: () => void; }`): Selects tabs from anywhere. Create one with `createTabsController()`.
789
+ - `debug?` (`boolean`): Logs every state change with its reason to the console, in development builds.
790
+
791
+ ### TabsList
792
+
793
+ The list takes no options of its own, only the attributes you give it.
794
+
795
+ ### TabsTab
796
+
797
+ - `value` (`string`): Identifies the tab and pairs it with the panel of the same value.
798
+ - `disabled?` (`boolean`): Keeps the tab focusable but stops it from being selected, as the tabs pattern asks.
799
+
800
+ ### TabsTabIcon
801
+
802
+ - `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.
803
+
804
+ ### TabsIndicator
805
+
806
+ The indicator takes no props of its own. Its [CSS variables](#styling-tabsindicator) hold the selected tab's box.
807
+
808
+ ### TabsPanel
809
+
810
+ - `value` (`string`): The value of the tab this panel belongs to.
811
+ - `keepMounted?` (`boolean`): Whether the panel stays in the page, hidden, while its tab isn't selected.
812
+ - `hiddenUntilFound?` (`boolean`): Whether the panel stays findable with `hidden="until-found"` while its tab isn't selected. Overrides the tabs'.
813
+
814
+ ### State
815
+
816
+ The same state shows as data attributes on each element.
817
+
818
+ `ui-tabs`:
819
+
820
+ - `value` (`T | null`): The selected tab's value.
821
+ - `orientation` (`TabsOrientation`): The tabs' `orientation` option, or `"horizontal"` when it's unset or invalid.
822
+ - `direction` (`Direction`): Which way the last selection moved.
823
+
824
+ `ui-tabs-list`:
825
+
826
+ - `orientation` (`TabsOrientation`): The tabs' `orientation` option, or `"horizontal"` when it's unset or invalid.
827
+
828
+ `ui-tabs-tab`:
829
+
830
+ - `selected` (`boolean`): Whether this tab's value is the tabs' `value`.
831
+ - `disabled` (`boolean`): Whether this tab can't be selected.
832
+
833
+ `ui-tabs-tab-icon`:
834
+
835
+ - `mirror` (`boolean`): Whether it flips in right-to-left text.
836
+
837
+ `ui-tabs-panel`:
838
+
839
+ - `selected` (`boolean`): Whether this panel's tab is selected. False while it animates out.
840
+
841
+ `ui-tabs-indicator`:
842
+
843
+ - `orientation` (`TabsOrientation`): The tabs' `orientation` option, or `"horizontal"` when it's unset or invalid.
844
+ - `direction` (`Direction`): Which way the last selection moved.
845
+
846
+ ### Reasons
847
+
848
+ `event.detail.reason` says why the selection changed.
849
+
850
+ - `item-press` (`MouseEvent | KeyboardEvent`): A member of a collection was chosen: a menu item, a tab, a radio or a checkbox item.
851
+ - `list-navigation` (`KeyboardEvent`): Arrow, Home or End keys moved the selection, typing picked an item, or Ctrl or ⌘ with A selected every item of a listbox.
852
+ - `find-in-page` (`Event`): The browser's find in page matched hidden content.
853
+ - `item-removed` (`none`): The selected item left the page, and another was picked.
854
+ - `imperative` (`none`): A controller or `api` call, or a property set on a web component.
855
+
856
+ ### Controller
857
+
858
+ The element's `api` property has the same methods. The `value` property reads and sets the selection.
859
+
860
+ Every write method takes an optional `{ reason, event }`, fires `valuechange` like a press, and returns `false` when the change is canceled.
861
+
862
+ - `getValue()` (`() => T | null`): The selected tab's value, or null when nothing is selected.
863
+ - `setValue(value, options?)` (`(value: T | null, options?: TabsChangeOptions | undefined) => boolean`): Selects a tab, or nothing with `null`. Disabled tabs never select. Returns false when canceled.
864
+ - `select(value, options?)` (`(value: T, options?: TabsChangeOptions | undefined) => boolean`): Selects a tab, the same as `setValue`.
865
+ - `focus()` (`() => void`): Moves focus to the selected tab, or the tab that holds the tab stop.
866
+
867
+ ### Events
868
+
869
+ Events fire on their own element and don't bubble, with the new value in `event.detail.value` and the `reason` and `event` beside it. Calling `preventDefault()` on `valuechange` keeps the current tab. `valuechange` never fires on connect.
870
+
871
+ | Member | On | Description |
872
+ | --------------------------- | --------- | ---------------------------------------------- |
873
+ | `value` property, attribute | `ui-tabs` | The selected tab. Setting either selects a tab |
874
+ | `api` property | `ui-tabs` | The [controller](#controller) |
875
+ | `valuechange` event | `ui-tabs` | `detail: { value, reason, event, source }` |
876
+
877
+ ## Accessibility
878
+
879
+ Tabs follow the [WAI-ARIA tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/). The list is a `tablist`, each tab's button controls its panel, and each panel is labelled by its tab.
880
+
881
+ ### Keyboard
882
+
883
+ Tab enters the list at the selected tab and leaves it on the next Tab. Left and Right Arrow move between tabs and select them, or only move focus with `activation="manual"`, where Enter or Space selects. Up and Down Arrow do the same in a vertical list. Home and End move to the first and last tabs. The list reads `dir="rtl"` from the page or any ancestor, and Left and Right follow the reading direction, with no provider.
884
+
885
+ ### Tested Behavior
886
+
887
+ Every line below is an automated test.
888
+
889
+ **Selection**
890
+
891
+ - The default tab is selected and shows its panel.
892
+ - Clicking a tab selects it and shows its panel.
893
+ - Clicking a disabled tab does nothing.
894
+ - Clicking the selected tab keeps it selected.
895
+ - A change can be cancelled.
896
+ - Without a default value, the first enabled tab is selected.
897
+ - Without a default value, the first render already selects the first enabled tab.
898
+
899
+ **Keyboard navigation**
900
+
901
+ - Tab moves focus to the selected tab.
902
+ - ArrowRight and ArrowLeft move focus and select the tab.
903
+ - Disabled tabs are focusable but never selected.
904
+ - Focus wraps at both ends.
905
+ - Home and End move to the first and last tabs.
906
+ - ArrowUp and ArrowDown do nothing in a horizontal tab list.
907
+ - Arrow keys with a modifier are left to the browser.
908
+ - Keys pressed in quick succession are never lost.
909
+ - Tab moves focus from the tab list into the selected panel.
910
+ - Shift+Tab from the panel returns to the selected tab.
911
+
912
+ **Manual activation**
913
+
914
+ - Arrow keys move focus without selecting.
915
+ - Enter selects the focused tab.
916
+ - Space selects the focused tab.
917
+ - Enter on a disabled tab does nothing.
918
+
919
+ **Vertical**
920
+
921
+ - The tab list reports its orientation.
922
+ - ArrowDown and ArrowUp move between tabs.
923
+ - ArrowLeft and ArrowRight do nothing in a vertical tab list.
924
+
925
+ **Panels**
926
+
927
+ - The selected tab controls its panel, and the panel is labelled by its tab.
928
+ - With keepMounted, hidden panels stay in the page.
929
+ - With keepMounted, a class that sets display doesn't show hidden panels.
930
+
931
+ Runs in React, Vue and web components.
932
+
933
+ Beyond the pattern's baseline:
934
+
935
+ **Enhancements**
936
+
937
+ - In manual mode, re-entering the tab list focuses the selected tab.
938
+ - A panel that holds a field is not a tab stop of its own.
939
+ - A controller selects a tab from anywhere.
940
+ - An infinite exit animation still unmounts the panel.
941
+
942
+ Runs in React, Vue and web components.
943
+
944
+ Screen readers hear the same thing in each tested framework, recorded by a virtual screen reader:
945
+
946
+ - Announcements while using the tabs with a keyboard.
947
+
948
+ Runs in React, Vue and web components.