@boostdev/design-system-components 2.8.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (534) hide show
  1. package/AGENTS.md +54 -46
  2. package/README.md +34 -20
  3. package/dist/client.cjs +1317 -779
  4. package/dist/client.css +1029 -812
  5. package/dist/client.d.cts +88 -26
  6. package/dist/client.d.ts +88 -26
  7. package/dist/client.js +1278 -732
  8. package/dist/index.cjs +1317 -779
  9. package/dist/index.css +1029 -812
  10. package/dist/index.d.cts +88 -26
  11. package/dist/index.d.ts +88 -26
  12. package/dist/index.js +1278 -732
  13. package/dist/web-components/chunk-2OLVQUBU.js +193 -0
  14. package/dist/web-components/{chunk-OF6FFGOT.js → chunk-3AY2YKCM.js} +77 -21
  15. package/dist/web-components/chunk-3WAV6MDN.js +212 -0
  16. package/dist/web-components/{chunk-UUKSDCYS.js → chunk-47AT5KZD.js} +87 -15
  17. package/dist/web-components/chunk-4A2YYTYN.js +93 -0
  18. package/dist/web-components/{chunk-Q4T5BJT6.js → chunk-62NK3ZSC.js} +18 -3
  19. package/dist/web-components/chunk-66M6ETWD.js +77 -0
  20. package/dist/web-components/{chunk-NCPQ7AF3.js → chunk-6G22I2CK.js} +18 -3
  21. package/dist/web-components/{chunk-QDST57JC.js → chunk-7ZOJ7N4E.js} +14 -4
  22. package/dist/web-components/{chunk-QLT6MTZF.js → chunk-AIBOHNDU.js} +10 -0
  23. package/dist/web-components/{chunk-X3NQMBAQ.js → chunk-AP4RDUL2.js} +14 -3
  24. package/dist/web-components/{chunk-QWPACK5L.js → chunk-BPYKZVDK.js} +11 -1
  25. package/dist/web-components/{chunk-YLVTEY23.js → chunk-BTCMJQKB.js} +53 -21
  26. package/dist/web-components/chunk-CUKQ2L5B.js +245 -0
  27. package/dist/web-components/chunk-DIZDCL7Z.js +14 -0
  28. package/dist/web-components/{chunk-PC3IOQAH.js → chunk-DV5T43ZC.js} +27 -7
  29. package/dist/web-components/{chunk-2QA62WCQ.js → chunk-EF7IVYTY.js} +10 -0
  30. package/dist/web-components/{chunk-L3XHOMN6.js → chunk-F3ZDNHZZ.js} +26 -1
  31. package/dist/web-components/chunk-FEDABOR5.js +248 -0
  32. package/dist/web-components/{chunk-6I2DBFQ7.js → chunk-HGFCJXOQ.js} +36 -5
  33. package/dist/web-components/{chunk-SOQQ7RVJ.js → chunk-IPGHUCJT.js} +23 -8
  34. package/dist/web-components/chunk-ITD2MV5K.js +392 -0
  35. package/dist/web-components/{chunk-ARMKC4BQ.js → chunk-JH6LQKLK.js} +77 -5
  36. package/dist/web-components/chunk-JMCXAAFK.js +120 -0
  37. package/dist/web-components/{chunk-KUI55GFB.js → chunk-JUPCZVVW.js} +50 -18
  38. package/dist/web-components/chunk-JV5QT2OU.js +146 -0
  39. package/dist/web-components/{chunk-3GFWLSLS.js → chunk-KA3VPKMY.js} +29 -6
  40. package/dist/web-components/{chunk-CR5PGUHV.js → chunk-KJGRFDFI.js} +26 -11
  41. package/dist/web-components/{chunk-PY62FTRW.js → chunk-KZPRFY5P.js} +68 -33
  42. package/dist/web-components/{chunk-X46KL5BK.js → chunk-LDIPCTRN.js} +18 -1
  43. package/dist/web-components/chunk-M3ECULRH.js +174 -0
  44. package/dist/web-components/chunk-NBN2ENWJ.js +90 -0
  45. package/dist/web-components/chunk-NIN3JO6Q.js +189 -0
  46. package/dist/web-components/{chunk-2FGATTGT.js → chunk-NTQ4TCUT.js} +76 -55
  47. package/dist/web-components/chunk-OY74D4X6.js +302 -0
  48. package/dist/web-components/{chunk-3S42DZ7W.js → chunk-P24JYC5V.js} +7 -0
  49. package/dist/web-components/{chunk-BKIHQYJR.js → chunk-PXPPKCLT.js} +17 -2
  50. package/dist/web-components/{chunk-TQ7IQ4GZ.js → chunk-QEF2IRID.js} +71 -29
  51. package/dist/web-components/chunk-QZAIRJ75.js +111 -0
  52. package/dist/web-components/{chunk-6I3CFC26.js → chunk-RSZ3BWBK.js} +20 -5
  53. package/dist/web-components/chunk-RVF7W653.js +101 -0
  54. package/dist/web-components/chunk-SEO2F5KJ.js +234 -0
  55. package/dist/web-components/{chunk-VMMVWLXL.js → chunk-SFZ6Q4BP.js} +10 -0
  56. package/dist/web-components/{chunk-LY62SAVN.js → chunk-SYNLFKD3.js} +12 -2
  57. package/dist/web-components/chunk-TMWXRHGF.js +192 -0
  58. package/dist/web-components/{chunk-65JXQOBA.js → chunk-U3YZE6R6.js} +51 -7
  59. package/dist/web-components/chunk-UO7ROSHD.js +424 -0
  60. package/dist/web-components/chunk-VANAKB6D.js +152 -0
  61. package/dist/web-components/{chunk-PXZWYO6Z.js → chunk-VED2DFJ6.js} +108 -29
  62. package/dist/web-components/chunk-W6H5ID2U.js +161 -0
  63. package/dist/web-components/{chunk-FSZRP43R.js → chunk-WFVKU4GX.js} +21 -10
  64. package/dist/web-components/{chunk-OQTRTMAB.js → chunk-WIBAN7KP.js} +10 -0
  65. package/dist/web-components/chunk-XI7KRAH7.js +54 -0
  66. package/dist/web-components/{chunk-ORC56MDY.js → chunk-XIZWAWMJ.js} +45 -6
  67. package/dist/web-components/{chunk-FXURM6D5.js → chunk-YFAXGRPR.js} +10 -0
  68. package/dist/web-components/globals.js +1 -1
  69. package/dist/web-components/index.d.ts +77 -707
  70. package/dist/web-components/index.js +339 -2066
  71. package/dist/web-components/interaction/bds-accordion-item.js +1 -1
  72. package/dist/web-components/interaction/bds-accordion.js +1 -1
  73. package/dist/web-components/interaction/bds-button.d.ts +10 -2
  74. package/dist/web-components/interaction/bds-button.js +1 -1
  75. package/dist/web-components/interaction/bds-collapsible.d.ts +7 -2
  76. package/dist/web-components/interaction/bds-collapsible.js +1 -1
  77. package/dist/web-components/interaction/bds-dialog.d.ts +11 -3
  78. package/dist/web-components/interaction/bds-dialog.js +1 -1
  79. package/dist/web-components/interaction/bds-drawer.d.ts +7 -1
  80. package/dist/web-components/interaction/bds-drawer.js +1 -1
  81. package/dist/web-components/interaction/bds-dropdown-menu-item.d.ts +51 -0
  82. package/dist/web-components/interaction/bds-dropdown-menu-item.js +7 -0
  83. package/dist/web-components/interaction/bds-dropdown-menu.d.ts +70 -0
  84. package/dist/web-components/interaction/bds-dropdown-menu.js +7 -0
  85. package/dist/web-components/interaction/bds-popover.d.ts +14 -3
  86. package/dist/web-components/interaction/bds-popover.js +1 -1
  87. package/dist/web-components/interaction/bds-skip-link.js +1 -1
  88. package/dist/web-components/interaction/bds-tab-panel.js +1 -1
  89. package/dist/web-components/interaction/bds-tab.js +1 -1
  90. package/dist/web-components/interaction/bds-tabs.d.ts +1 -0
  91. package/dist/web-components/interaction/bds-tabs.js +1 -1
  92. package/dist/web-components/interaction/bds-toast-provider.js +1 -1
  93. package/dist/web-components/interaction/bds-tooltip.d.ts +19 -3
  94. package/dist/web-components/interaction/bds-tooltip.js +1 -1
  95. package/dist/web-components/interaction/form/bds-checkbox-group.d.ts +83 -0
  96. package/dist/web-components/interaction/form/bds-checkbox-group.js +7 -0
  97. package/dist/web-components/interaction/form/bds-checkbox.d.ts +6 -0
  98. package/dist/web-components/interaction/form/bds-checkbox.js +1 -1
  99. package/dist/web-components/interaction/form/bds-combobox.d.ts +18 -0
  100. package/dist/web-components/interaction/form/bds-combobox.js +1 -1
  101. package/dist/web-components/interaction/form/bds-field-group.d.ts +89 -0
  102. package/dist/web-components/interaction/form/bds-field-group.js +7 -0
  103. package/dist/web-components/interaction/form/bds-file-input.d.ts +1 -1
  104. package/dist/web-components/interaction/form/bds-file-input.js +1 -1
  105. package/dist/web-components/interaction/form/bds-form-input.d.ts +92 -0
  106. package/dist/web-components/interaction/form/bds-form-input.js +7 -0
  107. package/dist/web-components/interaction/form/bds-number-input.d.ts +18 -1
  108. package/dist/web-components/interaction/form/bds-number-input.js +1 -1
  109. package/dist/web-components/interaction/form/bds-radio-group.d.ts +81 -0
  110. package/dist/web-components/interaction/form/bds-radio-group.js +7 -0
  111. package/dist/web-components/interaction/form/bds-radio.d.ts +34 -6
  112. package/dist/web-components/interaction/form/bds-radio.js +1 -1
  113. package/dist/web-components/interaction/form/bds-segmented-control.d.ts +4 -4
  114. package/dist/web-components/interaction/form/bds-segmented-control.js +1 -1
  115. package/dist/web-components/interaction/form/bds-select.d.ts +22 -1
  116. package/dist/web-components/interaction/form/bds-select.js +1 -1
  117. package/dist/web-components/interaction/form/bds-slider.d.ts +42 -10
  118. package/dist/web-components/interaction/form/bds-slider.js +1 -1
  119. package/dist/web-components/interaction/form/bds-switch.d.ts +20 -0
  120. package/dist/web-components/interaction/form/bds-switch.js +1 -1
  121. package/dist/web-components/interaction/form/bds-textarea.d.ts +23 -3
  122. package/dist/web-components/interaction/form/bds-textarea.js +1 -1
  123. package/dist/web-components/layout/bds-grid-item.d.ts +63 -0
  124. package/dist/web-components/layout/bds-grid-item.js +7 -0
  125. package/dist/web-components/layout/bds-grid.d.ts +108 -0
  126. package/dist/web-components/layout/bds-grid.js +7 -0
  127. package/dist/web-components/ui/bds-alert.d.ts +16 -2
  128. package/dist/web-components/ui/bds-alert.js +1 -1
  129. package/dist/web-components/ui/bds-avatar.d.ts +2 -1
  130. package/dist/web-components/ui/bds-avatar.js +1 -1
  131. package/dist/web-components/ui/bds-badge.js +1 -1
  132. package/dist/web-components/ui/bds-breadcrumb.d.ts +3 -1
  133. package/dist/web-components/ui/bds-breadcrumb.js +1 -1
  134. package/dist/web-components/ui/bds-button-container.d.ts +52 -0
  135. package/dist/web-components/ui/bds-button-container.js +7 -0
  136. package/dist/web-components/ui/bds-button-group.d.ts +21 -0
  137. package/dist/web-components/ui/bds-button-group.js +8 -0
  138. package/dist/web-components/ui/bds-calendar.d.ts +121 -0
  139. package/dist/web-components/ui/bds-calendar.js +7 -0
  140. package/dist/web-components/ui/bds-card.d.ts +12 -3
  141. package/dist/web-components/ui/bds-card.js +1 -1
  142. package/dist/web-components/ui/bds-carousel.d.ts +98 -0
  143. package/dist/web-components/ui/bds-carousel.js +7 -0
  144. package/dist/web-components/ui/bds-description-list.js +1 -1
  145. package/dist/web-components/ui/bds-icon-wrapper.d.ts +0 -9
  146. package/dist/web-components/ui/bds-icon-wrapper.js +1 -1
  147. package/dist/web-components/ui/bds-link.js +1 -1
  148. package/dist/web-components/ui/bds-loading.js +1 -1
  149. package/dist/web-components/ui/bds-notification-banner.js +1 -1
  150. package/dist/web-components/ui/bds-pagination.d.ts +47 -0
  151. package/dist/web-components/ui/bds-pagination.js +7 -0
  152. package/dist/web-components/ui/bds-progress-circle.d.ts +3 -1
  153. package/dist/web-components/ui/bds-progress-circle.js +1 -1
  154. package/dist/web-components/ui/bds-progress.d.ts +3 -1
  155. package/dist/web-components/ui/bds-progress.js +1 -1
  156. package/dist/web-components/ui/bds-rating.js +1 -1
  157. package/dist/web-components/ui/bds-section-header.d.ts +18 -4
  158. package/dist/web-components/ui/bds-section-header.js +1 -1
  159. package/dist/web-components/ui/bds-separator.js +1 -1
  160. package/dist/web-components/ui/bds-skeleton.js +1 -1
  161. package/dist/web-components/ui/bds-table.d.ts +52 -0
  162. package/dist/web-components/ui/bds-table.js +7 -0
  163. package/dist/web-components/ui/bds-typography.d.ts +1 -0
  164. package/dist/web-components/ui/bds-typography.js +1 -1
  165. package/package.json +60 -3
  166. package/src/components/interaction/Button/Button.module.css +23 -14
  167. package/src/components/interaction/Button/Button.spec.tsx +20 -0
  168. package/src/components/interaction/Button/Button.tsx +2 -2
  169. package/src/components/interaction/Command/Command.mdx +5 -2
  170. package/src/components/interaction/Command/Command.module.css +23 -7
  171. package/src/components/interaction/Command/Command.spec.tsx +35 -0
  172. package/src/components/interaction/Command/Command.tsx +15 -4
  173. package/src/components/interaction/Command/index.ts +1 -1
  174. package/src/components/interaction/Dialog/Dialog.mdx +4 -4
  175. package/src/components/interaction/Dialog/Dialog.module.css +20 -0
  176. package/src/components/interaction/Dialog/Dialog.spec.tsx +20 -0
  177. package/src/components/interaction/Dialog/Dialog.tsx +8 -3
  178. package/src/components/interaction/Dialog/index.ts +1 -0
  179. package/src/components/interaction/Drawer/Drawer.mdx +5 -2
  180. package/src/components/interaction/Drawer/Drawer.module.css +34 -3
  181. package/src/components/interaction/Drawer/Drawer.spec.tsx +67 -1
  182. package/src/components/interaction/Drawer/Drawer.tsx +30 -12
  183. package/src/components/interaction/Drawer/index.ts +1 -0
  184. package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +2 -0
  185. package/src/components/interaction/DropdownMenu/DropdownMenu.spec.tsx +34 -0
  186. package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +15 -5
  187. package/src/components/interaction/DropdownMenu/index.ts +1 -0
  188. package/src/components/interaction/Popover/Popover.mdx +6 -2
  189. package/src/components/interaction/Popover/Popover.module.css +26 -18
  190. package/src/components/interaction/Popover/Popover.spec.tsx +38 -0
  191. package/src/components/interaction/Popover/Popover.tsx +19 -5
  192. package/src/components/interaction/Popover/index.ts +1 -0
  193. package/src/components/interaction/Rating/Rating.mdx +1 -1
  194. package/src/components/interaction/Rating/Rating.module.css +2 -2
  195. package/src/components/interaction/Rating/Rating.tsx +1 -1
  196. package/src/components/interaction/Rating/index.ts +1 -0
  197. package/src/components/interaction/Toast/Toast.mdx +3 -0
  198. package/src/components/interaction/Toast/Toast.module.css +35 -5
  199. package/src/components/interaction/Toast/Toast.spec.tsx +69 -1
  200. package/src/components/interaction/Toast/Toast.tsx +15 -9
  201. package/src/components/interaction/form/Checkbox/Checkbox.mdx +3 -0
  202. package/src/components/interaction/form/Checkbox/Checkbox.module.css +13 -6
  203. package/src/components/interaction/form/Checkbox/Checkbox.spec.tsx +6 -0
  204. package/src/components/interaction/form/Checkbox/Checkbox.tsx +1 -1
  205. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +16 -1
  206. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css +24 -8
  207. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.spec.tsx +32 -2
  208. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +14 -2
  209. package/src/components/interaction/form/Combobox/Combobox.mdx +11 -4
  210. package/src/components/interaction/form/Combobox/Combobox.module.css +6 -6
  211. package/src/components/interaction/form/Combobox/Combobox.spec.tsx +47 -0
  212. package/src/components/interaction/form/Combobox/Combobox.tsx +10 -5
  213. package/src/components/interaction/form/FieldGroup/FieldGroup.mdx +9 -10
  214. package/src/components/interaction/form/FieldGroup/FieldGroup.module.css +57 -23
  215. package/src/components/interaction/form/FieldGroup/FieldGroup.spec.tsx +129 -7
  216. package/src/components/interaction/form/FieldGroup/FieldGroup.tsx +72 -12
  217. package/src/components/interaction/form/FileInput/FileInput.mdx +9 -1
  218. package/src/components/interaction/form/FileInput/FileInput.module.css +8 -8
  219. package/src/components/interaction/form/FileInput/FileInput.spec.tsx +89 -1
  220. package/src/components/interaction/form/FileInput/FileInput.tsx +6 -2
  221. package/src/components/interaction/form/FormInput/FormInput.mdx +9 -5
  222. package/src/components/interaction/form/FormInput/FormInput.module.css +17 -8
  223. package/src/components/interaction/form/FormInput/FormInput.spec.tsx +20 -0
  224. package/src/components/interaction/form/FormInput/FormInput.tsx +7 -2
  225. package/src/components/interaction/form/NumberInput/NumberInput.mdx +5 -1
  226. package/src/components/interaction/form/NumberInput/NumberInput.module.css +6 -6
  227. package/src/components/interaction/form/NumberInput/NumberInput.spec.tsx +51 -0
  228. package/src/components/interaction/form/NumberInput/NumberInput.tsx +36 -8
  229. package/src/components/interaction/form/Radio/Radio.mdx +8 -1
  230. package/src/components/interaction/form/Radio/Radio.module.css +20 -7
  231. package/src/components/interaction/form/Radio/Radio.spec.tsx +15 -0
  232. package/src/components/interaction/form/Radio/Radio.tsx +10 -4
  233. package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +16 -1
  234. package/src/components/interaction/form/RadioGroup/RadioGroup.module.css +24 -8
  235. package/src/components/interaction/form/RadioGroup/RadioGroup.spec.tsx +32 -2
  236. package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +14 -2
  237. package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +9 -3
  238. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +25 -12
  239. package/src/components/interaction/form/SegmentedControl/SegmentedControl.spec.tsx +86 -5
  240. package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +44 -4
  241. package/src/components/interaction/form/Select/Select.mdx +10 -4
  242. package/src/components/interaction/form/Select/Select.module.css +4 -4
  243. package/src/components/interaction/form/Select/Select.spec.tsx +34 -0
  244. package/src/components/interaction/form/Select/Select.tsx +21 -9
  245. package/src/components/interaction/form/Slider/Slider.mdx +4 -1
  246. package/src/components/interaction/form/Slider/Slider.module.css +32 -8
  247. package/src/components/interaction/form/Slider/Slider.spec.tsx +29 -0
  248. package/src/components/interaction/form/Slider/Slider.tsx +9 -7
  249. package/src/components/interaction/form/Switch/Switch.mdx +2 -2
  250. package/src/components/interaction/form/Switch/Switch.module.css +12 -13
  251. package/src/components/interaction/form/Switch/Switch.spec.tsx +20 -0
  252. package/src/components/interaction/form/Switch/Switch.tsx +2 -1
  253. package/src/components/interaction/form/Textarea/Textarea.mdx +5 -0
  254. package/src/components/interaction/form/Textarea/Textarea.module.css +5 -5
  255. package/src/components/interaction/form/Textarea/Textarea.spec.tsx +13 -0
  256. package/src/components/interaction/form/Textarea/Textarea.tsx +3 -1
  257. package/src/components/interaction/form/atoms/Message.module.css +1 -1
  258. package/src/components/interaction/form/atoms/Message.spec.tsx +29 -0
  259. package/src/components/interaction/form/atoms/Message.tsx +1 -1
  260. package/src/components/layout/ButtonGroup/ButtonGroup.mdx +11 -0
  261. package/src/components/layout/ButtonGroup/ButtonGroup.module.css +1 -5
  262. package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +14 -0
  263. package/src/components/layout/Card/Card.mdx +6 -1
  264. package/src/components/layout/Card/Card.module.css +26 -5
  265. package/src/components/layout/Card/Card.spec.tsx +7 -0
  266. package/src/components/layout/Card/Card.tsx +4 -3
  267. package/src/components/layout/Grid/Grid.spec.tsx +44 -0
  268. package/src/components/layout/Grid/Grid.tsx +5 -5
  269. package/src/components/layout/Grid/GridItem.tsx +6 -6
  270. package/src/components/layout/Grid/masonry.ts +31 -5
  271. package/src/components/layout/Grid/useIsomorphicLayoutEffect.ts +11 -0
  272. package/src/components/layout/IconWrapper/IconWrapper.mdx +8 -2
  273. package/src/components/layout/IconWrapper/IconWrapper.module.css +9 -6
  274. package/src/components/layout/SectionHeader/SectionHeader.mdx +4 -1
  275. package/src/components/layout/SectionHeader/SectionHeader.module.css +10 -9
  276. package/src/components/ui/Accordion/Accordion.mdx +4 -1
  277. package/src/components/ui/Accordion/Accordion.module.css +2 -1
  278. package/src/components/ui/Alert/Alert.mdx +21 -4
  279. package/src/components/ui/Alert/Alert.module.css +9 -4
  280. package/src/components/ui/Alert/Alert.spec.tsx +20 -0
  281. package/src/components/ui/Avatar/Avatar.mdx +8 -3
  282. package/src/components/ui/Avatar/Avatar.module.css +13 -8
  283. package/src/components/ui/Badge/Badge.mdx +3 -1
  284. package/src/components/ui/Breadcrumb/Breadcrumb.mdx +3 -0
  285. package/src/components/ui/Breadcrumb/Breadcrumb.module.css +4 -0
  286. package/src/components/ui/Breadcrumb/Breadcrumb.spec.tsx +12 -0
  287. package/src/components/ui/Breadcrumb/Breadcrumb.tsx +11 -3
  288. package/src/components/ui/Calendar/Calendar.mdx +6 -0
  289. package/src/components/ui/Calendar/Calendar.module.css +68 -19
  290. package/src/components/ui/Calendar/Calendar.spec.tsx +139 -24
  291. package/src/components/ui/Calendar/Calendar.stories.tsx +14 -0
  292. package/src/components/ui/Calendar/Calendar.tsx +143 -154
  293. package/src/components/ui/Calendar/CalendarRange.mdx +39 -0
  294. package/src/components/ui/Calendar/CalendarRange.spec.tsx +119 -0
  295. package/src/components/ui/Calendar/CalendarRange.stories.tsx +51 -0
  296. package/src/components/ui/Calendar/CalendarRange.tsx +213 -0
  297. package/src/components/ui/Calendar/MonthGrid.tsx +141 -0
  298. package/src/components/ui/Calendar/calendar-core.spec.ts +176 -0
  299. package/src/components/ui/Calendar/calendar-core.ts +164 -0
  300. package/src/components/ui/Calendar/index.ts +1 -0
  301. package/src/components/ui/Carousel/Carousel.mdx +8 -3
  302. package/src/components/ui/Carousel/Carousel.module.css +7 -5
  303. package/src/components/ui/Carousel/Carousel.spec.tsx +67 -0
  304. package/src/components/ui/Carousel/Carousel.tsx +19 -7
  305. package/src/components/ui/Collapsible/Collapsible.mdx +17 -6
  306. package/src/components/ui/Collapsible/Collapsible.module.css +7 -2
  307. package/src/components/ui/Collapsible/Collapsible.spec.tsx +40 -0
  308. package/src/components/ui/Collapsible/Collapsible.tsx +19 -2
  309. package/src/components/ui/DescriptionList/DescriptionList.mdx +2 -0
  310. package/src/components/ui/Link/Link.mdx +4 -2
  311. package/src/components/ui/Loading/Loading.mdx +6 -3
  312. package/src/components/ui/Loading/Loading.module.css +7 -7
  313. package/src/components/ui/Loading/Loading.spec.tsx +12 -0
  314. package/src/components/ui/Loading/Loading.tsx +8 -3
  315. package/src/components/ui/NotificationBanner/NotificationBanner.mdx +4 -0
  316. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +5 -0
  317. package/src/components/ui/NotificationBanner/NotificationBanner.spec.tsx +20 -0
  318. package/src/components/ui/Pagination/Pagination.mdx +29 -7
  319. package/src/components/ui/Pagination/Pagination.module.css +15 -14
  320. package/src/components/ui/Progress/Progress.mdx +6 -2
  321. package/src/components/ui/Progress/Progress.module.css +1 -1
  322. package/src/components/ui/Progress/Progress.spec.tsx +17 -0
  323. package/src/components/ui/Progress/Progress.tsx +2 -2
  324. package/src/components/ui/ProgressCircle/ProgressCircle.mdx +8 -4
  325. package/src/components/ui/ProgressCircle/ProgressCircle.module.css +2 -2
  326. package/src/components/ui/ProgressCircle/ProgressCircle.spec.tsx +11 -1
  327. package/src/components/ui/ProgressCircle/ProgressCircle.tsx +1 -1
  328. package/src/components/ui/Separator/Separator.mdx +7 -0
  329. package/src/components/ui/Separator/Separator.module.css +1 -0
  330. package/src/components/ui/Skeleton/Skeleton.mdx +17 -0
  331. package/src/components/ui/Skeleton/Skeleton.module.css +12 -5
  332. package/src/components/ui/SkipLink/SkipLink.mdx +19 -0
  333. package/src/components/ui/SkipLink/SkipLink.module.css +8 -7
  334. package/src/components/ui/SkipLink/SkipLink.spec.tsx +28 -0
  335. package/src/components/ui/Table/Table.mdx +14 -0
  336. package/src/components/ui/Table/Table.module.css +13 -8
  337. package/src/components/ui/Table/Table.spec.tsx +56 -0
  338. package/src/components/ui/Table/Table.tsx +44 -6
  339. package/src/components/ui/Tabs/Tabs.mdx +16 -0
  340. package/src/components/ui/Tabs/Tabs.module.css +3 -3
  341. package/src/components/ui/Tabs/Tabs.spec.tsx +20 -0
  342. package/src/components/ui/Tabs/Tabs.tsx +5 -1
  343. package/src/components/ui/Tooltip/Tooltip.mdx +9 -0
  344. package/src/components/ui/Tooltip/Tooltip.module.css +32 -12
  345. package/src/components/ui/Tooltip/Tooltip.spec.tsx +56 -0
  346. package/src/components/ui/Tooltip/Tooltip.tsx +26 -7
  347. package/src/components/ui/Typography/Typography.mdx +7 -0
  348. package/src/components/ui/Typography/Typography.module.css +5 -5
  349. package/src/index.ts +7 -3
  350. package/src/stories/DesignSystem/Borders.mdx +14 -9
  351. package/src/stories/DesignSystem/Colors.mdx +43 -41
  352. package/src/stories/DesignSystem/Overview.mdx +4 -3
  353. package/src/stories/Introduction.mdx +5 -5
  354. package/src/web-components/globals.ts +7 -0
  355. package/src/web-components/index.ts +2 -0
  356. package/src/web-components/interaction/BdsAccordion.mdx +10 -1
  357. package/src/web-components/interaction/BdsButton.mdx +15 -4
  358. package/src/web-components/interaction/BdsButton.stories.tsx +1 -1
  359. package/src/web-components/interaction/BdsCollapsible.mdx +3 -3
  360. package/src/web-components/interaction/BdsDialog.mdx +12 -7
  361. package/src/web-components/interaction/BdsDialog.stories.tsx +2 -2
  362. package/src/web-components/interaction/BdsDrawer.mdx +9 -6
  363. package/src/web-components/interaction/BdsDropdownMenu.mdx +20 -1
  364. package/src/web-components/interaction/BdsPopover.mdx +7 -3
  365. package/src/web-components/interaction/BdsSkipLink.mdx +14 -1
  366. package/src/web-components/interaction/BdsTabs.mdx +18 -1
  367. package/src/web-components/interaction/BdsToastProvider.mdx +21 -3
  368. package/src/web-components/interaction/BdsTooltip.mdx +4 -4
  369. package/src/web-components/interaction/bds-accordion-item.ts +10 -0
  370. package/src/web-components/interaction/bds-accordion.spec.ts +41 -0
  371. package/src/web-components/interaction/bds-accordion.ts +28 -2
  372. package/src/web-components/interaction/bds-button.spec.ts +81 -0
  373. package/src/web-components/interaction/bds-button.ts +55 -8
  374. package/src/web-components/interaction/bds-collapsible.spec.ts +93 -2
  375. package/src/web-components/interaction/bds-collapsible.ts +49 -7
  376. package/src/web-components/interaction/bds-dialog.spec.ts +49 -0
  377. package/src/web-components/interaction/bds-dialog.ts +68 -11
  378. package/src/web-components/interaction/bds-drawer.spec.ts +93 -4
  379. package/src/web-components/interaction/bds-drawer.ts +54 -21
  380. package/src/web-components/interaction/bds-dropdown-menu-item.ts +12 -1
  381. package/src/web-components/interaction/bds-dropdown-menu.spec.ts +63 -0
  382. package/src/web-components/interaction/bds-dropdown-menu.ts +45 -7
  383. package/src/web-components/interaction/bds-popover.spec.ts +80 -6
  384. package/src/web-components/interaction/bds-popover.ts +83 -32
  385. package/src/web-components/interaction/bds-skip-link.spec.ts +18 -0
  386. package/src/web-components/interaction/bds-skip-link.ts +29 -8
  387. package/src/web-components/interaction/bds-tab-panel.ts +10 -0
  388. package/src/web-components/interaction/bds-tab.spec.ts +25 -0
  389. package/src/web-components/interaction/bds-tab.ts +20 -5
  390. package/src/web-components/interaction/bds-tabs.spec.ts +92 -5
  391. package/src/web-components/interaction/bds-tabs.ts +42 -1
  392. package/src/web-components/interaction/bds-toast-provider.spec.ts +43 -0
  393. package/src/web-components/interaction/bds-toast-provider.ts +50 -21
  394. package/src/web-components/interaction/bds-tooltip.spec.ts +71 -0
  395. package/src/web-components/interaction/bds-tooltip.ts +100 -5
  396. package/src/web-components/interaction/form/BdsCheckbox.mdx +20 -1
  397. package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +8 -0
  398. package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +4 -3
  399. package/src/web-components/interaction/form/BdsCombobox.mdx +39 -2
  400. package/src/web-components/interaction/form/BdsCombobox.stories.tsx +31 -0
  401. package/src/web-components/interaction/form/BdsFieldGroup.mdx +8 -3
  402. package/src/web-components/interaction/form/BdsFieldGroup.stories.tsx +1 -1
  403. package/src/web-components/interaction/form/BdsFileInput.mdx +26 -5
  404. package/src/web-components/interaction/form/BdsFormInput.mdx +16 -1
  405. package/src/web-components/interaction/form/BdsNumberInput.mdx +34 -2
  406. package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +27 -3
  407. package/src/web-components/interaction/form/BdsRadio.mdx +39 -12
  408. package/src/web-components/interaction/form/BdsRadio.stories.tsx +26 -0
  409. package/src/web-components/interaction/form/BdsRadioGroup.mdx +9 -4
  410. package/src/web-components/interaction/form/BdsSegmentedControl.mdx +37 -11
  411. package/src/web-components/interaction/form/BdsSelect.mdx +22 -4
  412. package/src/web-components/interaction/form/BdsSelect.stories.tsx +33 -0
  413. package/src/web-components/interaction/form/BdsSlider.mdx +36 -19
  414. package/src/web-components/interaction/form/BdsSlider.stories.tsx +24 -0
  415. package/src/web-components/interaction/form/BdsSwitch.mdx +38 -11
  416. package/src/web-components/interaction/form/BdsSwitch.stories.tsx +16 -0
  417. package/src/web-components/interaction/form/BdsTextarea.mdx +42 -16
  418. package/src/web-components/interaction/form/BdsTextarea.stories.tsx +21 -0
  419. package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +99 -0
  420. package/src/web-components/interaction/form/bds-checkbox-group.ts +95 -9
  421. package/src/web-components/interaction/form/bds-checkbox.spec.ts +55 -0
  422. package/src/web-components/interaction/form/bds-checkbox.ts +30 -5
  423. package/src/web-components/interaction/form/bds-combobox.spec.ts +79 -0
  424. package/src/web-components/interaction/form/bds-combobox.ts +101 -16
  425. package/src/web-components/interaction/form/bds-field-group.spec.ts +158 -0
  426. package/src/web-components/interaction/form/bds-field-group.ts +167 -23
  427. package/src/web-components/interaction/form/bds-file-input.spec.ts +73 -5
  428. package/src/web-components/interaction/form/bds-file-input.ts +71 -34
  429. package/src/web-components/interaction/form/bds-form-input.spec.ts +22 -0
  430. package/src/web-components/interaction/form/bds-form-input.ts +27 -16
  431. package/src/web-components/interaction/form/bds-number-input.spec.ts +49 -0
  432. package/src/web-components/interaction/form/bds-number-input.ts +118 -29
  433. package/src/web-components/interaction/form/bds-radio-group.spec.ts +57 -0
  434. package/src/web-components/interaction/form/bds-radio-group.ts +60 -12
  435. package/src/web-components/interaction/form/bds-radio.spec.ts +123 -0
  436. package/src/web-components/interaction/form/bds-radio.ts +135 -22
  437. package/src/web-components/interaction/form/bds-segmented-control.spec.ts +55 -0
  438. package/src/web-components/interaction/form/bds-segmented-control.ts +81 -59
  439. package/src/web-components/interaction/form/bds-select.spec.ts +57 -0
  440. package/src/web-components/interaction/form/bds-select.ts +95 -7
  441. package/src/web-components/interaction/form/bds-slider.spec.ts +83 -0
  442. package/src/web-components/interaction/form/bds-slider.ts +154 -26
  443. package/src/web-components/interaction/form/bds-switch.spec.ts +68 -0
  444. package/src/web-components/interaction/form/bds-switch.ts +87 -20
  445. package/src/web-components/interaction/form/bds-textarea.spec.ts +71 -0
  446. package/src/web-components/interaction/form/bds-textarea.ts +108 -7
  447. package/src/web-components/layout/BdsGrid.mdx +6 -2
  448. package/src/web-components/layout/BdsGrid.stories.tsx +4 -0
  449. package/src/web-components/layout/bds-grid-item.spec.ts +21 -0
  450. package/src/web-components/layout/bds-grid-item.ts +17 -3
  451. package/src/web-components/layout/bds-grid.spec.ts +98 -0
  452. package/src/web-components/layout/bds-grid.ts +139 -19
  453. package/src/web-components/ui/BdsAlert.mdx +6 -5
  454. package/src/web-components/ui/BdsAlert.stories.tsx +6 -6
  455. package/src/web-components/ui/BdsAvatar.mdx +4 -2
  456. package/src/web-components/ui/BdsBreadcrumb.mdx +3 -2
  457. package/src/web-components/ui/BdsButtonContainer.mdx +64 -0
  458. package/src/web-components/ui/BdsButtonContainer.stories.tsx +84 -0
  459. package/src/web-components/ui/BdsCalendar.mdx +29 -4
  460. package/src/web-components/ui/BdsCalendar.stories.tsx +16 -18
  461. package/src/web-components/ui/BdsCalendarRange.mdx +71 -0
  462. package/src/web-components/ui/BdsCalendarRange.stories.tsx +31 -0
  463. package/src/web-components/ui/BdsCard.mdx +7 -3
  464. package/src/web-components/ui/BdsCard.stories.tsx +17 -1
  465. package/src/web-components/ui/BdsCarousel.mdx +40 -12
  466. package/src/web-components/ui/BdsDescriptionList.mdx +6 -1
  467. package/src/web-components/ui/BdsIconWrapper.mdx +3 -2
  468. package/src/web-components/ui/BdsLink.mdx +14 -2
  469. package/src/web-components/ui/BdsNotificationBanner.mdx +18 -0
  470. package/src/web-components/ui/BdsPagination.mdx +40 -1
  471. package/src/web-components/ui/BdsProgress.mdx +8 -2
  472. package/src/web-components/ui/BdsProgressCircle.mdx +5 -2
  473. package/src/web-components/ui/BdsRating.mdx +7 -0
  474. package/src/web-components/ui/BdsSectionHeader.mdx +22 -3
  475. package/src/web-components/ui/BdsSectionHeader.stories.tsx +15 -13
  476. package/src/web-components/ui/BdsTable.mdx +23 -2
  477. package/src/web-components/ui/BdsTable.stories.tsx +8 -2
  478. package/src/web-components/ui/BdsTypography.mdx +17 -1
  479. package/src/web-components/ui/bds-alert.spec.ts +17 -2
  480. package/src/web-components/ui/bds-alert.ts +31 -5
  481. package/src/web-components/ui/bds-avatar.spec.ts +29 -0
  482. package/src/web-components/ui/bds-avatar.ts +29 -11
  483. package/src/web-components/ui/bds-badge.spec.ts +5 -0
  484. package/src/web-components/ui/bds-badge.ts +11 -1
  485. package/src/web-components/ui/bds-breadcrumb.spec.ts +30 -0
  486. package/src/web-components/ui/bds-breadcrumb.ts +26 -4
  487. package/src/web-components/ui/bds-button-container.spec.ts +39 -0
  488. package/src/web-components/ui/bds-button-container.ts +123 -0
  489. package/src/web-components/ui/bds-button-group.spec.ts +14 -20
  490. package/src/web-components/ui/bds-button-group.ts +13 -64
  491. package/src/web-components/ui/bds-calendar-range.spec.ts +123 -0
  492. package/src/web-components/ui/bds-calendar-range.ts +307 -0
  493. package/src/web-components/ui/bds-calendar.spec.ts +176 -0
  494. package/src/web-components/ui/bds-calendar.ts +186 -344
  495. package/src/web-components/ui/bds-card.spec.ts +32 -0
  496. package/src/web-components/ui/bds-card.ts +34 -9
  497. package/src/web-components/ui/bds-carousel.spec.ts +147 -0
  498. package/src/web-components/ui/bds-carousel.ts +109 -32
  499. package/src/web-components/ui/bds-description-list.spec.ts +15 -0
  500. package/src/web-components/ui/bds-description-list.ts +37 -3
  501. package/src/web-components/ui/bds-icon-wrapper.spec.ts +12 -0
  502. package/src/web-components/ui/bds-icon-wrapper.ts +15 -14
  503. package/src/web-components/ui/bds-link.spec.ts +17 -0
  504. package/src/web-components/ui/bds-link.ts +26 -11
  505. package/src/web-components/ui/bds-loading.ts +10 -0
  506. package/src/web-components/ui/bds-notification-banner.spec.ts +13 -0
  507. package/src/web-components/ui/bds-notification-banner.ts +18 -1
  508. package/src/web-components/ui/bds-pagination.spec.ts +44 -0
  509. package/src/web-components/ui/bds-pagination.ts +39 -19
  510. package/src/web-components/ui/bds-progress-circle.spec.ts +21 -0
  511. package/src/web-components/ui/bds-progress-circle.ts +17 -5
  512. package/src/web-components/ui/bds-progress.spec.ts +21 -0
  513. package/src/web-components/ui/bds-progress.ts +17 -4
  514. package/src/web-components/ui/bds-rating.spec.ts +6 -0
  515. package/src/web-components/ui/bds-rating.ts +12 -2
  516. package/src/web-components/ui/bds-section-header.spec.ts +39 -8
  517. package/src/web-components/ui/bds-section-header.ts +47 -13
  518. package/src/web-components/ui/bds-separator.ts +10 -0
  519. package/src/web-components/ui/bds-skeleton.ts +10 -0
  520. package/src/web-components/ui/bds-table.spec.ts +63 -0
  521. package/src/web-components/ui/bds-table.ts +88 -28
  522. package/src/web-components/ui/bds-typography.spec.ts +47 -0
  523. package/src/web-components/ui/bds-typography.ts +28 -10
  524. package/src/web-components/ui/calendar-styles.ts +94 -0
  525. package/dist/web-components/chunk-7YURXHTS.js +0 -147
  526. package/dist/web-components/chunk-A3RLT3AS.js +0 -49
  527. package/dist/web-components/chunk-AGKJTMJI.js +0 -56
  528. package/dist/web-components/chunk-BQLZ7I4G.js +0 -56
  529. package/dist/web-components/chunk-DZOEPVFT.js +0 -107
  530. package/dist/web-components/chunk-JYYRJS2D.js +0 -118
  531. package/dist/web-components/chunk-Y4MAXTMA.js +0 -80
  532. package/dist/web-components/chunk-Y7V7LM6R.js +0 -98
  533. package/src/web-components/ui/BdsButtonGroup.mdx +0 -69
  534. package/src/web-components/ui/BdsButtonGroup.stories.tsx +0 -66
@@ -17,6 +17,16 @@ import { LitElement, css, html } from 'lit';
17
17
  */
18
18
  export class BdsTab extends LitElement {
19
19
  static styles = css`
20
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
21
+ here; otherwise elements with an explicit size + padding size as
22
+ content-box and layouts drift from the React (light-DOM) versions. */
23
+ :host,
24
+ *,
25
+ *::before,
26
+ *::after {
27
+ box-sizing: border-box;
28
+ }
29
+
20
30
  :host {
21
31
  display: inline-flex;
22
32
  }
@@ -29,8 +39,13 @@ export class BdsTab extends LitElement {
29
39
  font-family: var(--bds-font_family--body);
30
40
  font-size: var(--bds-font_size--body);
31
41
  font-weight: var(--bds-font_weight--semibold);
32
- color: var(--tab_color, var(--bds-color_on-bg));
33
- border-block-end: 2px solid var(--tab_border-color, transparent);
42
+
43
+ /* Layered cascade: public overrides (--tab_color, --tab_border-color) win over
44
+ per-state defaults. Active/hover write to the inner --tab_*--state slot —
45
+ mirrors the React Tabs.module.css pattern so a consumer's --tab_color
46
+ override is never shadowed by the selected/hover rules below. */
47
+ color: var(--tab_color, var(--tab_color--state, var(--bds-color_on-bg)));
48
+ border-block-end: 2px solid var(--tab_border-color, var(--tab_border-color--state, transparent));
34
49
  margin-block-end: -1px;
35
50
  cursor: pointer;
36
51
  transition: var(--bds-animation_transition);
@@ -38,8 +53,8 @@ export class BdsTab extends LitElement {
38
53
  }
39
54
 
40
55
  :host([selected]) .tab {
41
- --tab_color: var(--bds-color_interactive_on-bg);
42
- --tab_border-color: var(--bds-color_interactive);
56
+ --tab_color--state: var(--tab_color--active, var(--bds-color_interactive_on-bg));
57
+ --tab_border-color--state: var(--tab_border-color--active, var(--bds-color_interactive));
43
58
  }
44
59
 
45
60
  .tab:disabled {
@@ -55,7 +70,7 @@ export class BdsTab extends LitElement {
55
70
 
56
71
  @media (hover: hover) and (pointer: fine) {
57
72
  .tab:not(:disabled, [aria-selected='true']):hover {
58
- --tab_color: var(--bds-color_interactive_on-bg);
73
+ --tab_color--state: var(--tab_color--hover, var(--bds-color_interactive_on-bg));
59
74
  }
60
75
  }
61
76
  `;
@@ -36,6 +36,23 @@ describe('bds-tabs', () => {
36
36
  cleanup(el);
37
37
  });
38
38
 
39
+ it('does not select a disabled first tab — activates the first enabled tab instead', async () => {
40
+ const el = await fixture(`
41
+ <bds-tabs>
42
+ <bds-tab slot="tab" disabled>Tab 1</bds-tab>
43
+ <bds-tab slot="tab">Tab 2</bds-tab>
44
+ <bds-tab-panel slot="panel">Panel 1</bds-tab-panel>
45
+ <bds-tab-panel slot="panel">Panel 2</bds-tab-panel>
46
+ </bds-tabs>
47
+ `);
48
+ // Wait for deferred _applyState
49
+ await Promise.resolve();
50
+ const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
51
+ expect(tabs[0].selected).toBe(false);
52
+ expect(tabs[1].selected).toBe(true);
53
+ cleanup(el);
54
+ });
55
+
39
56
  it('first panel is visible, others are hidden by default', async () => {
40
57
  const el = await fixture(html);
41
58
  await Promise.resolve();
@@ -77,9 +94,10 @@ describe('bds-tabs', () => {
77
94
  it('ArrowRight moves to next enabled tab', async () => {
78
95
  const el = await fixture(html);
79
96
  await Promise.resolve();
80
- el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
81
- await Promise.resolve();
82
97
  const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
98
+ // Real usage: the keydown originates from the focused (currently active) tab.
99
+ tabs[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
100
+ await Promise.resolve();
83
101
  expect(tabs[1].selected).toBe(true);
84
102
  cleanup(el);
85
103
  });
@@ -89,7 +107,7 @@ describe('bds-tabs', () => {
89
107
  await Promise.resolve();
90
108
  const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
91
109
  tabs[2].disabled = true; // ensure disabled is set (Lit property may not reflect from HTML attribute in jsdom)
92
- el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true }));
110
+ tabs[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true }));
93
111
  // disabled tab is skipped; enabled tabs are 0 and 1 → wraps to last enabled (index 1)
94
112
  expect(tabs[1].selected).toBe(true);
95
113
  cleanup(el);
@@ -102,7 +120,7 @@ describe('bds-tabs', () => {
102
120
  await Promise.all(tabs.map(t => t.updateComplete));
103
121
  // Navigate to tab 1 first
104
122
  tabs[1].dispatchEvent(new CustomEvent('bds-tab-select', { bubbles: true, composed: true }));
105
- el.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', bubbles: true }));
123
+ tabs[1].dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', bubbles: true }));
106
124
  expect(tabs[0].selected).toBe(true);
107
125
  cleanup(el);
108
126
  });
@@ -112,9 +130,78 @@ describe('bds-tabs', () => {
112
130
  await Promise.resolve();
113
131
  const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
114
132
  tabs[2].disabled = true; // ensure disabled is set
115
- el.dispatchEvent(new KeyboardEvent('keydown', { key: 'End', bubbles: true }));
133
+ tabs[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'End', bubbles: true }));
116
134
  // Last enabled tab is index 1 (tab 3 is disabled)
117
135
  expect(tabs[1].selected).toBe(true);
118
136
  cleanup(el);
119
137
  });
138
+
139
+ it('does not hijack Arrow/Home/End keys typed outside a <bds-tab> (e.g. host-level dispatch)', async () => {
140
+ const el = await fixture(html);
141
+ await Promise.resolve();
142
+ const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
143
+ // Dispatched directly on the <bds-tabs> host — not on a <bds-tab> — must be ignored.
144
+ el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
145
+ await Promise.resolve();
146
+ expect(tabs[0].selected).toBe(true);
147
+ expect(tabs[1].selected).toBe(false);
148
+ cleanup(el);
149
+ });
150
+
151
+ it('does not hijack ArrowLeft pressed inside a tab-panel input (caret movement stays with the input)', async () => {
152
+ const el = await fixture(`
153
+ <bds-tabs>
154
+ <bds-tab slot="tab">Tab 1</bds-tab>
155
+ <bds-tab slot="tab">Tab 2</bds-tab>
156
+ <bds-tab-panel slot="panel"><input type="text" /></bds-tab-panel>
157
+ <bds-tab-panel slot="panel">Panel 2</bds-tab-panel>
158
+ </bds-tabs>
159
+ `);
160
+ await Promise.resolve();
161
+ const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
162
+ const input = el.querySelector('input')!;
163
+ input.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true }));
164
+ await Promise.resolve();
165
+ expect(tabs[0].selected).toBe(true);
166
+ expect(tabs[1].selected).toBe(false);
167
+ cleanup(el);
168
+ });
169
+
170
+ it('wires role/id/aria-controls on tabs and role/id/aria-labelledby on panels', async () => {
171
+ const el = await fixture(html);
172
+ await Promise.resolve();
173
+ const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
174
+ const panels = [...el.querySelectorAll<BdsTabPanel>('bds-tab-panel')];
175
+
176
+ tabs.forEach((tab, i) => {
177
+ expect(tab.getAttribute('role')).toBe('tab');
178
+ expect(tab.id).toBeTruthy();
179
+ expect(tab.getAttribute('aria-controls')).toBe(panels[i].id);
180
+ });
181
+
182
+ panels.forEach((panel, i) => {
183
+ expect(panel.getAttribute('role')).toBe('tabpanel');
184
+ expect(panel.id).toBeTruthy();
185
+ expect(panel.getAttribute('aria-labelledby')).toBe(tabs[i].id);
186
+ });
187
+
188
+ cleanup(el);
189
+ });
190
+
191
+ it('preserves a consumer-supplied id instead of overwriting it', async () => {
192
+ const el = await fixture(`
193
+ <bds-tabs>
194
+ <bds-tab slot="tab" id="custom-tab-id">Tab 1</bds-tab>
195
+ <bds-tab-panel slot="panel" id="custom-panel-id">Panel 1</bds-tab-panel>
196
+ </bds-tabs>
197
+ `);
198
+ await Promise.resolve();
199
+ const tab = el.querySelector<BdsTab>('bds-tab')!;
200
+ const panel = el.querySelector<BdsTabPanel>('bds-tab-panel')!;
201
+ expect(tab.id).toBe('custom-tab-id');
202
+ expect(panel.id).toBe('custom-panel-id');
203
+ expect(tab.getAttribute('aria-controls')).toBe('custom-panel-id');
204
+ expect(panel.getAttribute('aria-labelledby')).toBe('custom-tab-id');
205
+ cleanup(el);
206
+ });
120
207
  });
@@ -26,6 +26,16 @@ import type { BdsTabPanel } from './bds-tab-panel';
26
26
  */
27
27
  export class BdsTabs extends LitElement {
28
28
  static styles = css`
29
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
30
+ here; otherwise elements with an explicit size + padding size as
31
+ content-box and layouts drift from the React (light-DOM) versions. */
32
+ :host,
33
+ *,
34
+ *::before,
35
+ *::after {
36
+ box-sizing: border-box;
37
+ }
38
+
29
39
  :host {
30
40
  display: flex;
31
41
  flex-direction: column;
@@ -40,6 +50,7 @@ export class BdsTabs extends LitElement {
40
50
 
41
51
  private _activeIndex = 0;
42
52
  private _mo: MutationObserver | null = null;
53
+ private readonly _uid = Math.random().toString(36).slice(2, 8);
43
54
 
44
55
  connectedCallback() {
45
56
  super.connectedCallback();
@@ -70,7 +81,10 @@ export class BdsTabs extends LitElement {
70
81
  private get _enabledIndexes(): number[] {
71
82
  return this._tabs
72
83
  .map((tab, i) => ({ tab, i }))
73
- .filter(({ tab }) => !tab.disabled)
84
+ // Check the attribute too: on first render the reflected `disabled`
85
+ // property may not be set yet when _applyState runs, but the authored
86
+ // `disabled` attribute is already present.
87
+ .filter(({ tab }) => !tab.disabled && !tab.hasAttribute('disabled'))
74
88
  .map(({ i }) => i);
75
89
  }
76
90
 
@@ -83,8 +97,28 @@ export class BdsTabs extends LitElement {
83
97
  this._activeIndex = Math.max(0, tabs.length - 1);
84
98
  }
85
99
 
100
+ // A disabled tab must never be the active one. If the current active index
101
+ // is not an enabled tab (e.g. the default index-0 tab is <bds-tab disabled>),
102
+ // move selection to the first enabled tab — matching the React Tabs behaviour.
103
+ if (this._enabledIndexes.length && !this._enabledIndexes.includes(this._activeIndex)) {
104
+ this._activeIndex = this._enabledIndexes[0];
105
+ }
106
+
107
+ // Assign role/id/aria-* on the light-DOM host elements (not the shadow-internal
108
+ // button/slot) — <bds-tab> and <bds-tab-panel> share the light-DOM tree with
109
+ // <bds-tabs>, so id-based aria-controls/aria-labelledby references resolve.
110
+ // Shadow-internal ids would not be valid IDREF targets from outside their root.
86
111
  tabs.forEach((tab, i) => {
112
+ const panel = panels[i];
87
113
  tab.selected = (i === this._activeIndex);
114
+ tab.setAttribute('role', 'tab');
115
+ if (!tab.id) tab.id = `bds-tabs-${this._uid}-tab-${i}`;
116
+ if (panel) {
117
+ if (!panel.id) panel.id = `bds-tabs-${this._uid}-panel-${i}`;
118
+ tab.setAttribute('aria-controls', panel.id);
119
+ panel.setAttribute('role', 'tabpanel');
120
+ panel.setAttribute('aria-labelledby', tab.id);
121
+ }
88
122
  });
89
123
 
90
124
  panels.forEach((panel, i) => {
@@ -106,6 +140,13 @@ export class BdsTabs extends LitElement {
106
140
  };
107
141
 
108
142
  private _handleKeydown = (e: KeyboardEvent) => {
143
+ // Only handle Arrow/Home/End when the key originates from a <bds-tab> (i.e. a
144
+ // tab button has focus). Without this guard the host-level listener hijacked
145
+ // these keys anywhere in the light DOM, including tab-panel content such as
146
+ // text inputs, sliders, or carousels.
147
+ const origin = e.target as Element | null;
148
+ if (!origin?.closest('bds-tab')) return;
149
+
109
150
  const enabled = this._enabledIndexes;
110
151
  if (!enabled.length) return;
111
152
 
@@ -119,4 +119,47 @@ describe('bds-toast-provider', () => {
119
119
  expect(el.shadowRoot!.querySelectorAll('.toast').length).toBe(2);
120
120
  cleanup(el);
121
121
  });
122
+
123
+ it('updated() does not throw when showPopover() reports an already-open popover (regression)', async () => {
124
+ // jsdom does not implement the Popover API, so showPopover/hidePopover are
125
+ // undefined on real elements in tests — mock them to reproduce the real-browser
126
+ // InvalidStateError that fires when showPopover() is called on an already-open
127
+ // popover (e.g. a 2nd/3rd toast joining the queue while the popover is showing).
128
+ const el = await fixture('<bds-toast-provider></bds-toast-provider>') as BdsToastProvider;
129
+ el.showToast('First', 'info');
130
+ await el.updateComplete;
131
+ const container = el.shadowRoot!.querySelector('[popover="manual"]') as HTMLElement & {
132
+ showPopover?: () => void;
133
+ };
134
+ container.showPopover = vi.fn(() => {
135
+ throw new DOMException('Invalid state', 'InvalidStateError');
136
+ });
137
+ expect(() => (el as unknown as { updated: () => void }).updated()).not.toThrow();
138
+ expect(container.showPopover).toHaveBeenCalledTimes(1);
139
+ cleanup(el);
140
+ });
141
+
142
+ it('variant rules write --toast_color--variant, not the public --toast_color hook (cascade parity)', () => {
143
+ const cssText = BdsToastProvider.styles.cssText;
144
+ // Variants must only set the inner slot so a consumer-set --toast_color still
145
+ // wins the cascade (element-level declarations otherwise beat inherited values).
146
+ expect(cssText).toMatch(/\.toast\.--variant_error\s*{[^}]*--toast_color--variant:\s*var\(--toast_color--error, var\(--bds-color_error\)\)/);
147
+ expect(cssText).not.toMatch(/\.toast\.--variant_error\s*{[^}]*--toast_color:\s*var\(--bds-color_error\)/);
148
+ expect(cssText).toMatch(/background-color:\s*var\(--toast_color,\s*var\(--toast_color--variant,\s*var\(--bds-color_bg\)\)\);/);
149
+ });
150
+
151
+ it('close button expands its hit area to >=44px via the ::after pseudo-element (touch target)', () => {
152
+ const cssText = BdsToastProvider.styles.cssText;
153
+ expect(cssText).toMatch(/\.close::after\s*{[^}]*inset:\s*-0\.75rem;/);
154
+ });
155
+
156
+ it('container/toast/close use logical properties, not physical ones', () => {
157
+ const cssText = BdsToastProvider.styles.cssText;
158
+ expect(cssText).toMatch(/inset-block-end:\s*var\(--bds-space_xl,\s*2rem\);/);
159
+ expect(cssText).toMatch(/inset-inline-end:\s*var\(--bds-space_xl,\s*2rem\);/);
160
+ expect(cssText).not.toMatch(/\n\s*bottom:/);
161
+ expect(cssText).not.toMatch(/\n\s*right:/);
162
+ expect(cssText).not.toMatch(/\bwidth:\s*1\.25rem/);
163
+ expect(cssText).not.toMatch(/\bheight:\s*1\.25rem/);
164
+ });
122
165
  });
@@ -38,6 +38,16 @@ interface ToastEntry {
38
38
  */
39
39
  export class BdsToastProvider extends LitElement {
40
40
  static styles = css`
41
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
42
+ here; otherwise elements with an explicit size + padding size as
43
+ content-box and layouts drift from the React (light-DOM) versions. */
44
+ :host,
45
+ *,
46
+ *::before,
47
+ *::after {
48
+ box-sizing: border-box;
49
+ }
50
+
41
51
  :host {
42
52
  display: contents;
43
53
  }
@@ -46,8 +56,8 @@ export class BdsToastProvider extends LitElement {
46
56
  /* Reset UA popover defaults */
47
57
  position: fixed;
48
58
  inset: unset;
49
- bottom: var(--bds-space_xl, 2rem);
50
- right: var(--bds-space_xl, 2rem);
59
+ inset-block-end: var(--bds-space_xl, 2rem);
60
+ inset-inline-end: var(--bds-space_xl, 2rem);
51
61
  display: flex;
52
62
  flex-direction: column;
53
63
  gap: var(--bds-space_m, 1rem);
@@ -58,34 +68,36 @@ export class BdsToastProvider extends LitElement {
58
68
 
59
69
  .toast {
60
70
  padding: var(--bds-space_m) var(--bds-space_l);
61
- background-color: var(--toast_color, var(--bds-color_bg));
62
- color: var(--toast_on-color, var(--bds-color_on-bg));
63
- border-radius: var(--bds-border_radius--s);
64
- box-shadow: var(--bds-shadow_s);
71
+ /* Layered cascade: public overrides (--toast_color, --toast_on-color) win
72
+ over per-variant defaults. Variants write to the inner --toast_*--variant slot. */
73
+ background-color: var(--toast_color, var(--toast_color--variant, var(--bds-color_bg)));
74
+ color: var(--toast_on-color, var(--toast_on-color--variant, var(--bds-color_on-bg)));
75
+ border-radius: var(--toast_radius, var(--bds-border_radius--s));
76
+ box-shadow: var(--toast_shadow, var(--bds-shadow_s));
65
77
  display: flex;
66
78
  align-items: center;
67
79
  gap: var(--bds-space_m);
68
- min-width: 300px;
80
+ min-inline-size: min(300px, calc(100svw - 2 * var(--bds-space_xl)));
69
81
  }
70
82
 
71
83
  .toast.--variant_success {
72
- --toast_color: var(--bds-color_success);
73
- --toast_on-color: var(--bds-color_on-success);
84
+ --toast_color--variant: var(--toast_color--success, var(--bds-color_success));
85
+ --toast_on-color--variant: var(--toast_on-color--success, var(--bds-color_on-success));
74
86
  }
75
87
 
76
88
  .toast.--variant_warning {
77
- --toast_color: var(--bds-color_warning);
78
- --toast_on-color: var(--bds-color_on-warning);
89
+ --toast_color--variant: var(--toast_color--warning, var(--bds-color_warning));
90
+ --toast_on-color--variant: var(--toast_on-color--warning, var(--bds-color_on-warning));
79
91
  }
80
92
 
81
93
  .toast.--variant_info {
82
- --toast_color: var(--bds-color_bg--subtle);
83
- --toast_on-color: var(--bds-color_on-bg--subtle);
94
+ --toast_color--variant: var(--toast_color--info, var(--bds-color_bg--subtle));
95
+ --toast_on-color--variant: var(--toast_on-color--info, var(--bds-color_on-bg--subtle));
84
96
  }
85
97
 
86
98
  .toast.--variant_error {
87
- --toast_color: var(--bds-color_error);
88
- --toast_on-color: var(--bds-color_on-error);
99
+ --toast_color--variant: var(--toast_color--error, var(--bds-color_error));
100
+ --toast_on-color--variant: var(--toast_on-color--error, var(--bds-color_on-error));
89
101
  }
90
102
 
91
103
  .message {
@@ -93,22 +105,35 @@ export class BdsToastProvider extends LitElement {
93
105
  }
94
106
 
95
107
  .close {
108
+ position: relative;
96
109
  display: flex;
97
110
  align-items: center;
98
111
  justify-content: center;
99
112
  flex-shrink: 0;
100
- width: 1.25rem;
101
- height: 1.25rem;
113
+ inline-size: 1.25rem;
114
+ block-size: 1.25rem;
102
115
  padding: 0;
103
116
  background: none;
104
117
  border: none;
105
118
  cursor: pointer;
106
- color: var(--toast_on-color);
119
+ color: currentcolor;
120
+ }
121
+
122
+ /* Expands the hit area to >=44px (WCAG 2.5.5) without growing the visible icon. */
123
+ .close::after {
124
+ content: '';
125
+ position: absolute;
126
+ inset: -0.75rem;
127
+ }
128
+
129
+ .close:focus-visible {
130
+ outline: var(--bds-outline_default);
131
+ outline-offset: var(--bds-outline_offset);
107
132
  }
108
133
 
109
134
  .close svg {
110
- width: 1rem;
111
- height: 1rem;
135
+ inline-size: 1rem;
136
+ block-size: 1rem;
112
137
  }
113
138
  `;
114
139
 
@@ -135,6 +160,10 @@ export class BdsToastProvider extends LitElement {
135
160
 
136
161
  // Sync the popover's top-layer visibility with the toast queue —
137
162
  // mirrors Toast.tsx's useEffect([toasts.length]).
163
+ // Both showPopover() and hidePopover() throw an InvalidStateError when called
164
+ // in a redundant state (e.g. showPopover() while a 2nd/3rd toast is queued and
165
+ // the popover is already open, or hidePopover() before any toast has shown) —
166
+ // catch silently in both directions.
138
167
  updated() {
139
168
  const container = this.shadowRoot?.querySelector<HTMLElement & {
140
169
  showPopover?: () => void;
@@ -142,7 +171,7 @@ export class BdsToastProvider extends LitElement {
142
171
  }>('[popover]');
143
172
  if (!container) return;
144
173
  if (this._toasts.length > 0) {
145
- container.showPopover?.();
174
+ try { container.showPopover?.(); } catch { /* already open */ }
146
175
  } else {
147
176
  try { container.hidePopover?.(); } catch { /* already hidden on initial mount */ }
148
177
  }
@@ -46,4 +46,75 @@ describe('bds-tooltip', () => {
46
46
  expect(el.querySelector('button')!.textContent).toBe('Help');
47
47
  cleanup(el);
48
48
  });
49
+
50
+ it('associates the trigger with the tooltip content via aria-describedby', async () => {
51
+ const el = await fixture('<bds-tooltip content="Helpful text"><button type="button">?</button></bds-tooltip>');
52
+ // Wait for the deferred connectedCallback wiring (Promise.resolve().then(...)).
53
+ await Promise.resolve();
54
+ await Promise.resolve();
55
+
56
+ const trigger = el.querySelector('button')!;
57
+ const describedById = trigger.getAttribute('aria-describedby');
58
+ expect(describedById).toBeTruthy();
59
+
60
+ const descEl = el.querySelector(`#${describedById}`);
61
+ expect(descEl).not.toBeNull();
62
+ expect(descEl!.textContent).toBe('Helpful text');
63
+ // The description node must live in the light DOM (same tree scope as the
64
+ // trigger) — aria-describedby cannot reference an id inside the shadow root.
65
+ expect(descEl!.getRootNode()).toBe(document);
66
+ cleanup(el);
67
+ });
68
+
69
+ it('keeps a consumer-provided aria-describedby alongside the generated one', async () => {
70
+ const el = await fixture(
71
+ '<bds-tooltip content="Tip"><button type="button" aria-describedby="external-hint">?</button></bds-tooltip>',
72
+ );
73
+ await Promise.resolve();
74
+ await Promise.resolve();
75
+
76
+ const trigger = el.querySelector('button')!;
77
+ const ids = trigger.getAttribute('aria-describedby')!.split(/\s+/);
78
+ expect(ids).toContain('external-hint');
79
+ expect(ids.length).toBe(2);
80
+ cleanup(el);
81
+ });
82
+
83
+ it('updates the hidden description text when content changes', async () => {
84
+ const el = await fixture('<bds-tooltip content="First"><button type="button">?</button></bds-tooltip>') as BdsTooltip;
85
+ await Promise.resolve();
86
+ await Promise.resolve();
87
+
88
+ el.content = 'Second';
89
+ await el.updateComplete;
90
+
91
+ const trigger = el.querySelector('button')!;
92
+ const describedById = trigger.getAttribute('aria-describedby')!;
93
+ const descEl = el.querySelector(`#${describedById}`);
94
+ expect(descEl!.textContent).toBe('Second');
95
+ cleanup(el);
96
+ });
97
+
98
+ it('does not add aria-describedby when content is empty', async () => {
99
+ const el = await fixture('<bds-tooltip><button type="button">?</button></bds-tooltip>');
100
+ await Promise.resolve();
101
+ await Promise.resolve();
102
+
103
+ const trigger = el.querySelector('button')!;
104
+ expect(trigger.hasAttribute('aria-describedby')).toBe(false);
105
+ cleanup(el);
106
+ });
107
+
108
+ it('hides the shadow-DOM tooltip bubble from assistive technology', async () => {
109
+ const el = await fixture('<bds-tooltip content="Helpful text"><button type="button">?</button></bds-tooltip>');
110
+ const tooltip = el.shadowRoot!.querySelector('[role="tooltip"]')!;
111
+ expect(tooltip.getAttribute('aria-hidden')).toBe('true');
112
+ cleanup(el);
113
+ });
114
+
115
+ it('does not hardcode a z-index fallback (token parity with React Tooltip)', () => {
116
+ const cssText = BdsTooltip.styles.cssText;
117
+ expect(cssText).toMatch(/z-index:\s*var\(--bds-z-index_popover\);/);
118
+ expect(cssText).not.toContain('200');
119
+ });
49
120
  });
@@ -16,9 +16,14 @@ export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
16
16
  * (default) — the trigger element (button, icon, etc.)
17
17
  *
18
18
  * Accessibility:
19
- * The `aria-describedby` relationship between trigger and tooltip panel
20
- * cannot cross shadow DOM boundaries; screen readers will announce the tooltip
21
- * via role="tooltip" proximity when it becomes visible.
19
+ * `aria-describedby` id references only resolve within the same tree scope, so a
20
+ * slotted (light-DOM) trigger can never reference an id that lives inside this
21
+ * element's shadow root. To keep the description reachable, a visually-hidden
22
+ * `<span>` holding `content` is appended as a light-DOM child of the host and its
23
+ * id is written into `aria-describedby` on the slotted trigger — both elements then
24
+ * share the light DOM's tree scope, so the reference resolves in every browser. The
25
+ * shadow-DOM tooltip bubble stays `aria-hidden="true"`; it is purely the visual
26
+ * presentation, shown on `:hover`/`:focus-within`.
22
27
  *
23
28
  * @example
24
29
  * <bds-tooltip content="More information" placement="bottom">
@@ -27,6 +32,16 @@ export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
27
32
  */
28
33
  export class BdsTooltip extends LitElement {
29
34
  static styles = css`
35
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
36
+ here; otherwise elements with an explicit size + padding size as
37
+ content-box and layouts drift from the React (light-DOM) versions. */
38
+ :host,
39
+ *,
40
+ *::before,
41
+ *::after {
42
+ box-sizing: border-box;
43
+ }
44
+
30
45
  :host {
31
46
  display: inline-flex;
32
47
  align-items: center;
@@ -35,7 +50,7 @@ export class BdsTooltip extends LitElement {
35
50
 
36
51
  .tooltip {
37
52
  position: absolute;
38
- z-index: var(--bds-z-index_popover, 200);
53
+ z-index: var(--bds-z-index_popover);
39
54
  padding: var(--bds-space_xxs) var(--bds-space_xs);
40
55
  border-radius: var(--tooltip_radius, var(--bds-border_radius--full));
41
56
  background-color: var(--tooltip_bg, var(--bds-color_on-bg));
@@ -101,18 +116,98 @@ export class BdsTooltip extends LitElement {
101
116
  declare content: string;
102
117
  declare placement: TooltipPlacement;
103
118
 
119
+ private readonly _uid = Math.random().toString(36).slice(2, 8);
120
+ /** Visually-hidden light-DOM node holding `content`, referenced by the trigger's aria-describedby. */
121
+ private _descEl: HTMLSpanElement | null = null;
122
+
104
123
  constructor() {
105
124
  super();
106
125
  this.content = '';
107
126
  this.placement = 'top';
108
127
  }
109
128
 
129
+ connectedCallback() {
130
+ super.connectedCallback();
131
+ // Deferred: on declarative parsing the trigger may not exist as a child yet
132
+ // when connectedCallback fires (upgrade happens before children are parsed).
133
+ Promise.resolve().then(this._syncDescription);
134
+ }
135
+
136
+ disconnectedCallback() {
137
+ super.disconnectedCallback();
138
+ this._teardownDescription();
139
+ }
140
+
141
+ updated(changed: Map<string, unknown>) {
142
+ super.updated(changed);
143
+ if (changed.has('content')) {
144
+ this._syncDescription();
145
+ }
146
+ }
147
+
148
+ private get _descId(): string {
149
+ return `bds-tooltip-desc-${this._uid}`;
150
+ }
151
+
152
+ /** Ensures the hidden description node exists (or is torn down) and re-wires the trigger. */
153
+ private _syncDescription = () => {
154
+ if (!this.isConnected) return;
155
+
156
+ if (!this.content) {
157
+ this._teardownDescription();
158
+ return;
159
+ }
160
+
161
+ let desc = this._descEl;
162
+ if (!desc) {
163
+ desc = document.createElement('span');
164
+ desc.id = this._descId;
165
+ desc.style.cssText =
166
+ 'position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;';
167
+ this.append(desc);
168
+ this._descEl = desc;
169
+ }
170
+
171
+ desc.textContent = this.content;
172
+ this._describeTrigger();
173
+ };
174
+
175
+ private _describeTrigger() {
176
+ const id = this._descId;
177
+ for (const el of Array.from(this.children)) {
178
+ if (el === this._descEl) continue;
179
+ const existing = el.getAttribute('aria-describedby');
180
+ const ids = existing ? existing.split(/\s+/).filter(Boolean) : [];
181
+ if (!ids.includes(id)) {
182
+ el.setAttribute('aria-describedby', [...ids, id].join(' '));
183
+ }
184
+ }
185
+ }
186
+
187
+ private _teardownDescription() {
188
+ const id = this._descId;
189
+ for (const el of Array.from(this.children)) {
190
+ if (el === this._descEl) continue;
191
+ const existing = el.getAttribute('aria-describedby');
192
+ if (!existing) continue;
193
+ const ids = existing.split(/\s+/).filter(part => part && part !== id);
194
+ if (ids.length) {
195
+ el.setAttribute('aria-describedby', ids.join(' '));
196
+ } else {
197
+ el.removeAttribute('aria-describedby');
198
+ }
199
+ }
200
+ this._descEl?.remove();
201
+ this._descEl = null;
202
+ }
203
+
110
204
  render() {
111
205
  return html`
112
- <slot></slot>
206
+ <slot @slotchange=${this._syncDescription}></slot>
113
207
  <span
114
208
  class="tooltip --placement_${this.placement}"
115
209
  role="tooltip"
210
+ aria-hidden="true"
116
211
  >${this.content}</span>
117
212
  `;
118
213
  }