@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
@@ -66,6 +66,69 @@ describe('bds-dropdown-menu', () => {
66
66
  expect(el.open).toBe(false);
67
67
  cleanup(el);
68
68
  });
69
+
70
+ it('wires aria-haspopup, aria-controls and aria-expanded onto the slotted trigger', async () => {
71
+ const el = await fixture(`
72
+ <bds-dropdown-menu>
73
+ <button slot="trigger" type="button">Options</button>
74
+ <bds-dropdown-menu-item value="edit">Edit</bds-dropdown-menu-item>
75
+ </bds-dropdown-menu>
76
+ `) as BdsDropdownMenu;
77
+ await new Promise(r => requestAnimationFrame(() => r(null)));
78
+ const trigger = el.querySelector('button[slot="trigger"]')!;
79
+ const panel = el.shadowRoot!.querySelector('.panel')!;
80
+
81
+ expect(trigger.getAttribute('aria-haspopup')).toBe('menu');
82
+ expect(trigger.getAttribute('aria-controls')).toBe(panel.id);
83
+ expect(panel.id).not.toBe('');
84
+ expect(trigger.getAttribute('aria-expanded')).toBe('false');
85
+
86
+ cleanup(el);
87
+ });
88
+
89
+ it('keeps aria-expanded on the trigger in sync with open/close', async () => {
90
+ const el = await fixture(`
91
+ <bds-dropdown-menu>
92
+ <button slot="trigger" type="button">Options</button>
93
+ <bds-dropdown-menu-item value="edit">Edit</bds-dropdown-menu-item>
94
+ </bds-dropdown-menu>
95
+ `) as BdsDropdownMenu;
96
+ await new Promise(r => requestAnimationFrame(() => r(null)));
97
+ const trigger = el.querySelector('button[slot="trigger"]')!;
98
+
99
+ el._open();
100
+ expect(trigger.getAttribute('aria-expanded')).toBe('true');
101
+
102
+ el._close();
103
+ expect(trigger.getAttribute('aria-expanded')).toBe('false');
104
+
105
+ cleanup(el);
106
+ });
107
+
108
+ it('focuses the first item on Home and the last item on End', async () => {
109
+ const el = await fixture(`
110
+ <bds-dropdown-menu>
111
+ <button slot="trigger" type="button">Options</button>
112
+ <bds-dropdown-menu-item value="a">A</bds-dropdown-menu-item>
113
+ <bds-dropdown-menu-item value="b">B</bds-dropdown-menu-item>
114
+ <bds-dropdown-menu-item value="c">C</bds-dropdown-menu-item>
115
+ </bds-dropdown-menu>
116
+ `) as BdsDropdownMenu;
117
+ const items = [...el.querySelectorAll('bds-dropdown-menu-item')] as BdsDropdownMenuItem[];
118
+ el._open();
119
+ await Promise.all(items.map(item => item.updateComplete));
120
+
121
+ const buttons = items.map(item => item.shadowRoot!.querySelector<HTMLButtonElement>('button')!);
122
+ const focusSpies = buttons.map(btn => vi.spyOn(btn, 'focus'));
123
+
124
+ el.dispatchEvent(new KeyboardEvent('keydown', { key: 'End', bubbles: true }));
125
+ expect(focusSpies[2]).toHaveBeenCalled();
126
+
127
+ el.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', bubbles: true }));
128
+ expect(focusSpies[0]).toHaveBeenCalled();
129
+
130
+ cleanup(el);
131
+ });
69
132
  });
70
133
 
71
134
  describe('bds-dropdown-menu-item', () => {
@@ -6,9 +6,12 @@ export type DropdownMenuPlacement = 'bottom-start' | 'bottom-end' | 'top-start'
6
6
  /**
7
7
  * `<bds-dropdown-menu>` — anchored dropdown menu with keyboard navigation.
8
8
  *
9
- * Uses the native Popover API for the floating panel (light-dismiss, top-layer).
10
- * The trigger slot element controls open/close. The component handles keyboard
11
- * navigation (Arrow keys, Escape, Tab) and focus management.
9
+ * The panel is an absolutely-positioned element inside the shadow root (not
10
+ * the native Popover API / top layer) it can be clipped by an ancestor's
11
+ * `overflow` or stacking context. Light-dismiss is implemented via a
12
+ * document-level `click` listener. The trigger slot element controls
13
+ * open/close. The component handles keyboard navigation (Arrow keys, Home,
14
+ * End, Escape, Tab) and focus management.
12
15
  *
13
16
  * Attributes:
14
17
  * open — controls open/closed state (reflected)
@@ -31,6 +34,16 @@ export type DropdownMenuPlacement = 'bottom-start' | 'bottom-end' | 'top-start'
31
34
  */
32
35
  export class BdsDropdownMenu extends LitElement {
33
36
  static styles = css`
37
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
38
+ here; otherwise elements with an explicit size + padding size as
39
+ content-box and layouts drift from the React (light-DOM) versions. */
40
+ :host,
41
+ *,
42
+ *::before,
43
+ *::after {
44
+ box-sizing: border-box;
45
+ }
46
+
34
47
  :host {
35
48
  display: inline-block;
36
49
  position: relative;
@@ -41,11 +54,17 @@ export class BdsDropdownMenu extends LitElement {
41
54
  inset-block-start: calc(100% + var(--bds-space_xxs));
42
55
  inset-inline-start: 0;
43
56
  min-inline-size: 10rem;
44
- background-color: var(--bds-color_bg);
45
- border-radius: var(--bds-border_radius--s);
46
- box-shadow: var(--bds-shadow_m);
57
+ border: var(--dropdown_border, none);
58
+ --bdc_color: currentcolor;
59
+ --bdc-outline_radius: var(--bds-border_radius--s);
60
+
61
+ border-radius: var(--dropdown_radius, var(--bdc-outline_radius));
62
+ background-color: var(--dropdown_color, var(--bds-color_bg));
63
+ color: var(--dropdown_on-color, var(--bds-color_on-bg));
64
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color),
65
+ var(--dropdown_shadow, var(--bds-shadow_m));
47
66
  padding: var(--bds-space_xxs);
48
- z-index: var(--bds-z-index_dropdown, 200);
67
+ z-index: var(--bds-z-index_dropdown);
49
68
  display: none;
50
69
  flex-direction: column;
51
70
  }
@@ -81,6 +100,13 @@ export class BdsDropdownMenu extends LitElement {
81
100
  declare open: boolean;
82
101
  declare placement: DropdownMenuPlacement;
83
102
 
103
+ // Stable id for aria-controls wiring from the slotted trigger to the panel
104
+ private readonly _uid = Math.random().toString(36).slice(2, 8);
105
+
106
+ private get _panelId(): string {
107
+ return `bds-dropdown-panel-${this._uid}`;
108
+ }
109
+
84
110
  constructor() {
85
111
  super();
86
112
  this.open = false;
@@ -115,6 +141,7 @@ export class BdsDropdownMenu extends LitElement {
115
141
  _open() {
116
142
  if (this.open) return;
117
143
  this.open = true;
144
+ this._trigger?.setAttribute('aria-expanded', 'true');
118
145
  this.dispatchEvent(new CustomEvent('bds-open', { bubbles: true, composed: true }));
119
146
  // Focus first item after render
120
147
  this.updateComplete.then(() => {
@@ -125,6 +152,7 @@ export class BdsDropdownMenu extends LitElement {
125
152
  _close() {
126
153
  if (!this.open) return;
127
154
  this.open = false;
155
+ this._trigger?.setAttribute('aria-expanded', 'false');
128
156
  this.dispatchEvent(new CustomEvent('bds-close', { bubbles: true, composed: true }));
129
157
  this._trigger?.focus();
130
158
  }
@@ -160,6 +188,12 @@ export class BdsDropdownMenu extends LitElement {
160
188
  e.preventDefault();
161
189
  const prev = (focused - 1 + items.length) % items.length;
162
190
  items[prev]?.shadowRoot?.querySelector<HTMLElement>('button')?.focus();
191
+ } else if (e.key === 'Home') {
192
+ e.preventDefault();
193
+ items[0]?.shadowRoot?.querySelector<HTMLElement>('button')?.focus();
194
+ } else if (e.key === 'End') {
195
+ e.preventDefault();
196
+ items[items.length - 1]?.shadowRoot?.querySelector<HTMLElement>('button')?.focus();
163
197
  } else if (e.key === 'Tab') {
164
198
  this._close();
165
199
  }
@@ -174,6 +208,9 @@ export class BdsDropdownMenu extends LitElement {
174
208
  if (trigger) {
175
209
  trigger.removeEventListener('click', this._handleTriggerClick);
176
210
  trigger.addEventListener('click', this._handleTriggerClick);
211
+ trigger.setAttribute('aria-haspopup', 'menu');
212
+ trigger.setAttribute('aria-controls', this._panelId);
213
+ trigger.setAttribute('aria-expanded', String(this.open));
177
214
  }
178
215
  }
179
216
 
@@ -181,6 +218,7 @@ export class BdsDropdownMenu extends LitElement {
181
218
  return html`
182
219
  <slot name="trigger" @slotchange=${this._onTriggerSlotChange}></slot>
183
220
  <div
221
+ id=${this._panelId}
184
222
  class="panel"
185
223
  role="menu"
186
224
  aria-hidden=${!this.open ? 'true' : 'false'}
@@ -101,19 +101,93 @@ describe('bds-popover', () => {
101
101
 
102
102
  it('injects a unique anchor-name CSS custom property on :host', async () => {
103
103
  const el = await fixture('<bds-popover><button slot="trigger">Open</button></bds-popover>');
104
- const anchorValue = el.style.getPropertyValue('--bds-popover-anchor');
105
- expect(anchorValue).toMatch(/^--bds-popover-[a-z0-9]+$/);
104
+ const anchorValue = el.style.getPropertyValue('--popover-anchor');
105
+ expect(anchorValue).toMatch(/^--popover-[a-z0-9]+$/);
106
106
  cleanup(el);
107
107
  });
108
108
 
109
- it('panel has role="region" and aria-label="Popover"', async () => {
109
+ it('panel omits role/aria-label by default (no aria-label attribute)', async () => {
110
110
  const el = await fixture('<bds-popover><button slot="trigger">Open</button></bds-popover>');
111
111
  const panel = el.shadowRoot!.querySelector('.panel');
112
+ expect(panel!.hasAttribute('role')).toBe(false);
113
+ expect(panel!.hasAttribute('aria-label')).toBe(false);
114
+ cleanup(el);
115
+ });
116
+
117
+ it('forwards host aria-label to the panel and sets role="region"', async () => {
118
+ const el = await fixture(
119
+ '<bds-popover aria-label="Notifications"><button slot="trigger">Open</button></bds-popover>',
120
+ );
121
+ const panel = el.shadowRoot!.querySelector('.panel');
112
122
  expect(panel!.getAttribute('role')).toBe('region');
113
- expect(panel!.getAttribute('aria-label')).toBe('Popover');
123
+ expect(panel!.getAttribute('aria-label')).toBe('Notifications');
114
124
  cleanup(el);
115
125
  });
116
126
 
127
+ describe('trigger ARIA wiring', () => {
128
+ it('sets aria-haspopup and aria-controls on the slotted trigger', async () => {
129
+ const el = await fixture('<bds-popover><button slot="trigger">Open</button></bds-popover>');
130
+ await new Promise(r => requestAnimationFrame(() => r(null)));
131
+ const trigger = el.querySelector('button')!;
132
+ expect(trigger.getAttribute('aria-haspopup')).toBe('true');
133
+ expect(trigger.getAttribute('aria-controls')).toBeTruthy();
134
+ const panel = el.shadowRoot!.querySelector('.panel')!;
135
+ expect(trigger.getAttribute('aria-controls')).toBe(panel.id);
136
+ cleanup(el);
137
+ });
138
+
139
+ it('sets aria-expanded="false" on the trigger by default', async () => {
140
+ const el = await fixture('<bds-popover><button slot="trigger">Open</button></bds-popover>');
141
+ await new Promise(r => requestAnimationFrame(() => r(null)));
142
+ const trigger = el.querySelector('button')!;
143
+ expect(trigger.getAttribute('aria-expanded')).toBe('false');
144
+ cleanup(el);
145
+ });
146
+
147
+ it('flips the trigger aria-expanded to "true" when opened', async () => {
148
+ const el = await fixture('<bds-popover><button slot="trigger">Open</button></bds-popover>') as BdsPopover;
149
+ await new Promise(r => requestAnimationFrame(() => r(null)));
150
+ const trigger = el.querySelector('button')!;
151
+ el.shadowRoot!.querySelector('slot[name="trigger"]')!
152
+ .dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
153
+ await el.updateComplete;
154
+ expect(trigger.getAttribute('aria-expanded')).toBe('true');
155
+ cleanup(el);
156
+ });
157
+ });
158
+
159
+ describe('focus restoration on close', () => {
160
+ it('restores focus to the trigger when Escape closes with focus inside the panel', async () => {
161
+ const el = await fixture(
162
+ '<bds-popover open><button slot="trigger">Open</button></bds-popover>',
163
+ ) as BdsPopover;
164
+ await new Promise(r => requestAnimationFrame(() => r(null)));
165
+ const trigger = el.querySelector('button[slot="trigger"]') as HTMLElement;
166
+ // Focus is inside the popover (on the trigger itself) when Escape fires.
167
+ trigger.focus();
168
+ expect(document.activeElement).toBe(trigger);
169
+ const focusSpy = vi.spyOn(trigger, 'focus');
170
+ document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
171
+ await el.updateComplete;
172
+ expect(focusSpy).toHaveBeenCalled();
173
+ cleanup(el);
174
+ });
175
+
176
+ it('does not steal focus when outside click closes the panel', async () => {
177
+ const outside = document.createElement('button');
178
+ document.body.appendChild(outside);
179
+ const el = await fixture('<bds-popover open><button slot="trigger">Open</button></bds-popover>') as BdsPopover;
180
+ await new Promise(r => requestAnimationFrame(() => r(null)));
181
+ outside.focus();
182
+ expect(document.activeElement).toBe(outside);
183
+ document.body.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
184
+ await el.updateComplete;
185
+ expect(document.activeElement).toBe(outside);
186
+ cleanup(el);
187
+ outside.remove();
188
+ });
189
+ });
190
+
117
191
  describe('Invoker Commands API (command event on host)', () => {
118
192
  it('opens on --open command', async () => {
119
193
  const el = await fixture('<bds-popover id="p1"><button slot="trigger">T</button></bds-popover>') as BdsPopover;
@@ -165,8 +239,8 @@ describe('bds-popover', () => {
165
239
  it('two instances get different anchor names', async () => {
166
240
  const a = await fixture('<bds-popover><button slot="trigger">A</button></bds-popover>');
167
241
  const b = await fixture('<bds-popover><button slot="trigger">B</button></bds-popover>');
168
- const anchorA = a.style.getPropertyValue('--bds-popover-anchor');
169
- const anchorB = b.style.getPropertyValue('--bds-popover-anchor');
242
+ const anchorA = a.style.getPropertyValue('--popover-anchor');
243
+ const anchorB = b.style.getPropertyValue('--popover-anchor');
170
244
  expect(anchorA).not.toBe(anchorB);
171
245
  cleanup(a);
172
246
  cleanup(b);
@@ -1,4 +1,4 @@
1
- import { LitElement, css, html } from 'lit';
1
+ import { LitElement, css, html, nothing } from 'lit';
2
2
 
3
3
  export type PopoverPlacement = 'bottom' | 'top' | 'left' | 'right';
4
4
 
@@ -10,11 +10,14 @@ export type PopoverPlacement = 'bottom' | 'top' | 'left' | 'right';
10
10
  * overflow the viewport (`@position-try` fallbacks).
11
11
  *
12
12
  * Attributes:
13
- * placement — "bottom" (default) | "top" | "left" | "right"
14
- * open — boolean; reflects whether the panel is visible
13
+ * placement — "bottom" (default) | "top" | "left" | "right"
14
+ * open — boolean; reflects whether the panel is visible
15
+ * aria-label — accessible name for the panel; when present the panel gets
16
+ * `role="region"` (mirrors the React implementation)
15
17
  *
16
18
  * Slots:
17
- * trigger — the element that opens/closes the panel on click
19
+ * trigger — the element that opens/closes the panel on click. Receives
20
+ * `aria-haspopup`, `aria-controls` and `aria-expanded` wiring.
18
21
  * (default) — panel content
19
22
  *
20
23
  * Events:
@@ -29,6 +32,16 @@ export type PopoverPlacement = 'bottom' | 'top' | 'left' | 'right';
29
32
  */
30
33
  export class BdsPopover extends LitElement {
31
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
+
32
45
  :host {
33
46
  display: inline-flex;
34
47
  position: relative;
@@ -39,19 +52,23 @@ export class BdsPopover extends LitElement {
39
52
  gets its own unique name without colliding with siblings. */
40
53
  .wrapper {
41
54
  display: inline-flex;
42
- anchor-name: var(--bds-popover-anchor);
55
+ anchor-name: var(--popover-anchor);
43
56
  }
44
57
 
45
58
  .panel {
46
59
  display: none;
47
- position: absolute;
48
- position-anchor: var(--bds-popover-anchor);
60
+
61
+ /* position: fixed (not absolute) — containing block is the viewport,
62
+ so the panel escapes ancestor overflow:hidden/scroll clipping and
63
+ stacking contexts, matching the React implementation. */
64
+ position: fixed;
65
+ position-anchor: var(--popover-anchor);
49
66
  margin: 0;
50
- overflow-y: auto;
67
+ overflow-block: auto;
51
68
  z-index: var(--popover_z-index, 100);
52
- min-width: 12rem;
53
- max-width: var(--popover_max-width, 20rem);
54
- max-height: var(--popover_max-height, 80dvh);
69
+ min-inline-size: 12rem;
70
+ max-inline-size: var(--popover_max-width, 20rem);
71
+ max-block-size: var(--popover_max-height, 80dvh);
55
72
  padding: var(--bds-space_m);
56
73
  border: var(--popover_border, none);
57
74
  --bdc_color: currentcolor;
@@ -72,86 +89,94 @@ export class BdsPopover extends LitElement {
72
89
  .panel[data-placement='bottom'] {
73
90
  top: anchor(bottom);
74
91
  left: anchor(left);
75
- margin-top: var(--bds-space_xs, 8px);
76
- position-try-fallbacks: --bds-popover-top, --bds-popover-right, --bds-popover-left;
92
+ margin-block-start: var(--bds-space_xs, 8px);
93
+ position-try-fallbacks: --popover-top, --popover-right, --popover-left;
77
94
  }
78
95
 
79
96
  .panel[data-placement='top'] {
80
97
  bottom: anchor(top);
81
98
  left: anchor(left);
82
- margin-bottom: var(--bds-space_xs, 8px);
83
- position-try-fallbacks: --bds-popover-bottom, --bds-popover-right, --bds-popover-left;
99
+ margin-block-end: var(--bds-space_xs, 8px);
100
+ position-try-fallbacks: --popover-bottom, --popover-right, --popover-left;
84
101
  }
85
102
 
86
103
  .panel[data-placement='right'] {
87
104
  top: anchor(top);
88
105
  left: anchor(right);
89
- margin-left: var(--bds-space_xs, 8px);
90
- position-try-fallbacks: --bds-popover-left, --bds-popover-bottom, --bds-popover-top;
106
+ margin-inline-start: var(--bds-space_xs, 8px);
107
+ position-try-fallbacks: --popover-left, --popover-bottom, --popover-top;
91
108
  }
92
109
 
93
110
  .panel[data-placement='left'] {
94
111
  top: anchor(top);
95
112
  right: anchor(left);
96
- margin-right: var(--bds-space_xs, 8px);
97
- position-try-fallbacks: --bds-popover-right, --bds-popover-bottom, --bds-popover-top;
113
+ margin-inline-end: var(--bds-space_xs, 8px);
114
+ position-try-fallbacks: --popover-right, --popover-bottom, --popover-top;
98
115
  }
99
116
 
100
117
  /* Fallback placements */
101
118
 
102
- @position-try --bds-popover-bottom {
119
+ @position-try --popover-bottom {
103
120
  top: anchor(bottom);
104
121
  right: unset;
105
122
  bottom: unset;
106
123
  left: anchor(left);
107
124
  margin: 0;
108
- margin-top: var(--bds-space_xs, 8px);
125
+ margin-block-start: var(--bds-space_xs, 8px);
109
126
  }
110
127
 
111
- @position-try --bds-popover-top {
128
+ @position-try --popover-top {
112
129
  top: unset;
113
130
  right: unset;
114
131
  bottom: anchor(top);
115
132
  left: anchor(left);
116
133
  margin: 0;
117
- margin-bottom: var(--bds-space_xs, 8px);
134
+ margin-block-end: var(--bds-space_xs, 8px);
118
135
  }
119
136
 
120
- @position-try --bds-popover-right {
137
+ @position-try --popover-right {
121
138
  top: anchor(top);
122
139
  right: unset;
123
140
  bottom: unset;
124
141
  left: anchor(right);
125
142
  margin: 0;
126
- margin-left: var(--bds-space_xs, 8px);
143
+ margin-inline-start: var(--bds-space_xs, 8px);
127
144
  }
128
145
 
129
- @position-try --bds-popover-left {
146
+ @position-try --popover-left {
130
147
  top: anchor(top);
131
148
  right: anchor(left);
132
149
  bottom: unset;
133
150
  left: unset;
134
151
  margin: 0;
135
- margin-right: var(--bds-space_xs, 8px);
152
+ margin-inline-end: var(--bds-space_xs, 8px);
136
153
  }
137
154
  `;
138
155
 
139
156
  static properties = {
140
157
  placement: { type: String, reflect: true },
141
158
  open: { type: Boolean, reflect: true },
159
+ ariaLabel: { type: String, attribute: 'aria-label' },
142
160
  };
143
161
 
144
162
  declare placement: PopoverPlacement;
145
163
  declare open: boolean;
164
+ declare ariaLabel: string | null;
146
165
 
147
166
  private _anchorName: string;
167
+ private _panelId: string;
168
+ private _triggerEl: HTMLElement | null = null;
148
169
 
149
170
  constructor() {
150
171
  super();
151
172
  this.placement = 'bottom';
152
173
  this.open = false;
174
+ this.ariaLabel = null;
153
175
  const uid = Math.random().toString(36).slice(2, 8);
154
- this._anchorName = `--bds-popover-${uid}`;
176
+ // Non-`--bds-` prefixed — these are internal coordination idents, not
177
+ // foundation-owned tokens, so they must not squat the `--bds-` namespace.
178
+ this._anchorName = `--popover-${uid}`;
179
+ this._panelId = `bds-popover-panel-${uid}`;
155
180
  }
156
181
 
157
182
  connectedCallback() {
@@ -159,7 +184,7 @@ export class BdsPopover extends LitElement {
159
184
  // Inject the unique anchor-name as a CSS custom property so both
160
185
  // `.wrapper` (anchor-name) and `.panel` (position-anchor) reference
161
186
  // the same value without hard-coding it in the shared static styles.
162
- this.style.setProperty('--bds-popover-anchor', this._anchorName);
187
+ this.style.setProperty('--popover-anchor', this._anchorName);
163
188
  document.addEventListener('keydown', this._onKeydown);
164
189
  document.addEventListener('mousedown', this._onOutsideClick);
165
190
  // Invoker Commands API — host is in light DOM so commandfor can reference its id.
@@ -199,25 +224,51 @@ export class BdsPopover extends LitElement {
199
224
  private _open() {
200
225
  if (this.open) return;
201
226
  this.open = true;
227
+ this._triggerEl?.setAttribute('aria-expanded', 'true');
202
228
  this.dispatchEvent(new CustomEvent('bds-open', { bubbles: true, composed: true }));
203
229
  }
204
230
 
205
231
  private _close() {
206
232
  if (!this.open) return;
207
233
  this.open = false;
234
+ this._triggerEl?.setAttribute('aria-expanded', 'false');
208
235
  this.dispatchEvent(new CustomEvent('bds-close', { bubbles: true, composed: true }));
236
+ // Restore focus to the trigger only if it was inside this popover (panel
237
+ // content or the trigger itself) — an outside click that closes the
238
+ // panel has already moved focus elsewhere and should not be overridden.
239
+ if (this.contains(document.activeElement)) {
240
+ this._triggerEl?.focus();
241
+ }
242
+ }
243
+
244
+ // Track the slotted trigger so aria-expanded/haspopup/controls can be kept
245
+ // in sync and focus can be restored on close (mirrors the React
246
+ // cloneElement wiring and bds-dropdown-menu's trigger-tracking pattern).
247
+ private _onTriggerSlotChange(e: Event) {
248
+ const slot = e.target as HTMLSlotElement;
249
+ this._triggerEl = (slot.assignedElements()[0] as HTMLElement) ?? null;
250
+ if (this._triggerEl) {
251
+ this._triggerEl.setAttribute('aria-haspopup', 'true');
252
+ this._triggerEl.setAttribute('aria-controls', this._panelId);
253
+ this._triggerEl.setAttribute('aria-expanded', String(this.open));
254
+ }
209
255
  }
210
256
 
211
257
  render() {
212
258
  return html`
213
259
  <span class="wrapper">
214
- <slot name="trigger" @click=${this._toggle}></slot>
260
+ <slot
261
+ name="trigger"
262
+ @click=${this._toggle}
263
+ @slotchange=${this._onTriggerSlotChange}
264
+ ></slot>
215
265
  </span>
216
266
  <div
217
267
  class="panel ${this.open ? 'open' : ''}"
218
268
  data-placement=${this.placement}
219
- role="region"
220
- aria-label="Popover"
269
+ id=${this._panelId}
270
+ role=${this.ariaLabel ? 'region' : nothing}
271
+ aria-label=${this.ariaLabel ?? nothing}
221
272
  >
222
273
  <slot></slot>
223
274
  </div>
@@ -39,4 +39,22 @@ describe('bds-skip-link', () => {
39
39
  expect(el.textContent?.trim()).toBe('Go to main');
40
40
  cleanup(el);
41
41
  });
42
+
43
+ it('uses a themeable z-index token with a fallback instead of a hardcoded value', () => {
44
+ const cssText = BdsSkipLink.styles.cssText;
45
+ expect(cssText).toContain('z-index: var(--skip-link_z-index, var(--bds-z-index_xl))');
46
+ expect(cssText).not.toContain('z-index: 9999');
47
+ });
48
+
49
+ it('exposes the --skip-link_border hook and the bdc inset focus-border chain', () => {
50
+ const cssText = BdsSkipLink.styles.cssText;
51
+ expect(cssText).toContain('border: var(--skip-link_border, none)');
52
+ expect(cssText).toMatch(/box-shadow:\s*inset 0 0 0 var\(--bdc-outline_width\) var\(--bdc_color\)/);
53
+ expect(cssText).toContain('var(--skip-link_shadow, var(--bdc-outline_shadow))');
54
+ });
55
+
56
+ it('guards the slide-in transition with prefers-reduced-motion', () => {
57
+ const cssText = BdsSkipLink.styles.cssText;
58
+ expect(cssText).toMatch(/@media \(prefers-reduced-motion: reduce\)\s*{\s*\.skip-link\s*{\s*transition:\s*none;/);
59
+ });
42
60
  });
@@ -17,24 +17,39 @@ import { LitElement, css, html } from 'lit';
17
17
  */
18
18
  export class BdsSkipLink 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: block;
22
32
  }
23
33
 
24
34
  .skip-link {
25
35
  position: absolute;
26
- inset-block-start: var(--bds-space_s);
27
- inset-inline-start: var(--bds-space_s);
28
- z-index: 9999;
29
- padding: var(--bds-space_xs) var(--bds-space_m);
36
+ inset-block-start: var(--skip-link_inset, var(--bds-space_s));
37
+ inset-inline-start: var(--skip-link_inset, var(--bds-space_s));
38
+ z-index: var(--skip-link_z-index, var(--bds-z-index_xl));
39
+ padding: var(--skip-link_padding, var(--bds-space_xs) var(--bds-space_m));
30
40
  background-color: var(--bds-color_bg);
31
41
  color: var(--bds-color_on-bg);
32
- border-radius: var(--bds-border_radius--s);
42
+ border-radius: var(--skip-link_radius, var(--bds-border_radius--s));
33
43
  font-weight: var(--bds-font_weight--semibold);
34
- font-size: var(--bds-font_size--body);
44
+ font-size: var(--skip-link_font-size, var(--bds-font_size--body));
35
45
  text-decoration: none;
36
- box-shadow: var(--bds-shadow_m);
37
- transform: translateY(calc(-100% - var(--bds-space_s)));
46
+
47
+ --bdc_color: var(--color_focus, var(--bds-color_interactive));
48
+
49
+ border: var(--skip-link_border, none);
50
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color),
51
+ var(--skip-link_shadow, var(--bdc-outline_shadow));
52
+ transform: translateY(calc(-100% - var(--skip-link_inset, var(--bds-space_s))));
38
53
  transition: transform var(--bds-animation_transition-duration) var(--bds-animation_easing);
39
54
  }
40
55
 
@@ -43,6 +58,12 @@ export class BdsSkipLink extends LitElement {
43
58
  outline: var(--bds-outline_default);
44
59
  outline-offset: var(--bds-outline_offset);
45
60
  }
61
+
62
+ @media (prefers-reduced-motion: reduce) {
63
+ .skip-link {
64
+ transition: none;
65
+ }
66
+ }
46
67
  `;
47
68
 
48
69
  static properties = {
@@ -14,6 +14,16 @@ import { LitElement, css, html } from 'lit';
14
14
  */
15
15
  export class BdsTabPanel extends LitElement {
16
16
  static styles = css`
17
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
18
+ here; otherwise elements with an explicit size + padding size as
19
+ content-box and layouts drift from the React (light-DOM) versions. */
20
+ :host,
21
+ *,
22
+ *::before,
23
+ *::after {
24
+ box-sizing: border-box;
25
+ }
26
+
17
27
  :host {
18
28
  display: block;
19
29
  padding-block-start: var(--bds-space_m);
@@ -68,4 +68,29 @@ describe('bds-tab', () => {
68
68
  expect(btn.hasAttribute('disabled')).toBe(true);
69
69
  cleanup(el);
70
70
  });
71
+
72
+ it('lets the public --tab_color/--tab_border-color tokens win over selected/hover-state defaults', () => {
73
+ const cssText = BdsTab.styles.cssText;
74
+ expect(cssText).toMatch(
75
+ /\.tab\s*{[^}]*color:\s*var\(--tab_color,\s*var\(--tab_color--state,\s*var\(--bds-color_on-bg\)\)\)/,
76
+ );
77
+ expect(cssText).toMatch(
78
+ /\.tab\s*{[^}]*border-block-end:\s*2px solid var\(--tab_border-color,\s*var\(--tab_border-color--state,\s*transparent\)\)/,
79
+ );
80
+ });
81
+
82
+ it('has the selected/hover rules write only to the internal --tab_*--state slots, not the public tokens', () => {
83
+ const cssText = BdsTab.styles.cssText;
84
+ expect(cssText).not.toMatch(/:host\(\[selected\]\)\s*\.tab\s*{[^}]*--tab_color:/);
85
+ expect(cssText).not.toMatch(/:host\(\[selected\]\)\s*\.tab\s*{[^}]*--tab_border-color:/);
86
+ expect(cssText).toMatch(
87
+ /:host\(\[selected\]\)\s*\.tab\s*{[^}]*--tab_color--state:\s*var\(--tab_color--active,/,
88
+ );
89
+ expect(cssText).toMatch(
90
+ /:host\(\[selected\]\)\s*\.tab\s*{[^}]*--tab_border-color--state:\s*var\(--tab_border-color--active,/,
91
+ );
92
+ expect(cssText).toMatch(
93
+ /--tab_color--state:\s*var\(--tab_color--hover,/,
94
+ );
95
+ });
71
96
  });