@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
@@ -13,17 +13,35 @@ import { LitElement, css, html } from 'lit';
13
13
  * checked — boolean; whether the switch is on
14
14
  * required — boolean
15
15
  * disabled — boolean
16
+ * error — error message; when set the track shows the error colour and
17
+ * the input reports `aria-invalid="true"`
18
+ * size — "small" | "medium" (default) | "large"
16
19
  *
17
20
  * Slots:
18
21
  * (default) — label text
19
22
  *
23
+ * Events:
24
+ * bds-change — composed CustomEvent fired when the switch is toggled
25
+ * (crosses the shadow boundary); `detail: { checked: boolean }`
26
+ *
20
27
  * @example
21
28
  * <bds-switch name="notifications" value="enabled">Enable notifications</bds-switch>
29
+ * <bds-switch name="darkMode" size="small" error="Required">Dark mode</bds-switch>
22
30
  */
23
31
  export class BdsSwitch extends LitElement {
24
32
  static formAssociated = true;
25
33
 
26
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
+
27
45
  :host {
28
46
  display: inline-flex;
29
47
  align-items: center;
@@ -32,6 +50,19 @@ export class BdsSwitch extends LitElement {
32
50
  font-size: var(--bds-font_size--body);
33
51
  font-family: var(--bds-font_family--body);
34
52
  color: var(--switch_color, var(--bds-color_on-bg));
53
+
54
+ --switch_thumb-size: 1.25em;
55
+ --switch_track-pad: var(--bds-space_xxxs);
56
+ --switch_track-height: calc(var(--switch_thumb-size) + var(--switch_track-pad) * 2);
57
+ --switch_track-width: calc(var(--switch_thumb-size) * 2 + var(--switch_track-pad) * 2);
58
+ }
59
+
60
+ :host([size='small']) {
61
+ --switch_thumb-size: 1em;
62
+ }
63
+
64
+ :host([size='large']) {
65
+ --switch_thumb-size: 1.5em;
35
66
  }
36
67
 
37
68
  :host([disabled]) {
@@ -47,6 +78,19 @@ export class BdsSwitch extends LitElement {
47
78
  cursor: pointer;
48
79
  }
49
80
 
81
+ /* Positioned ancestor for the invisible overlay input — without this the
82
+ input's position:absolute + inset:0 climbs to the nearest positioned
83
+ ancestor outside the component (often the whole page), so clicks
84
+ anywhere would toggle the switch. Matches React's .trackWrapper. */
85
+ .track-wrapper {
86
+ position: relative;
87
+ display: inline-flex;
88
+ align-items: center;
89
+ flex-shrink: 0;
90
+ inline-size: var(--switch_track-width);
91
+ block-size: var(--switch_track-height);
92
+ }
93
+
50
94
  .input {
51
95
  position: absolute;
52
96
  inset: 0;
@@ -64,12 +108,10 @@ export class BdsSwitch extends LitElement {
64
108
  }
65
109
 
66
110
  .track {
67
- --switch_thumb-size: 1.25em;
68
- --switch_track-pad: var(--bds-space_xxxs);
69
- --switch_track-height: calc(var(--switch_thumb-size) + var(--switch_track-pad) * 2);
70
- --switch_track-width: calc(var(--switch_thumb-size) * 2 + var(--switch_track-pad) * 2);
71
-
72
- position: relative;
111
+ /* Shadow DOM does not inherit the global box-sizing reset — without this
112
+ the padding-inline is added on top of inline-size (content-box), so the
113
+ thumb's checked translate falls short of the track end. */
114
+ box-sizing: border-box;
73
115
  display: inline-flex;
74
116
  align-items: center;
75
117
  inline-size: var(--switch_track-width);
@@ -96,6 +138,12 @@ export class BdsSwitch extends LitElement {
96
138
  transform: translateX(0);
97
139
  }
98
140
 
141
+ /* Error state — lower specificity than :checked below, so a checked
142
+ switch still shows the active colour even when it also has an error. */
143
+ :host([error]) .track {
144
+ --bdc_color: var(--switch_border-color--error, var(--bds-color_error));
145
+ }
146
+
99
147
  .input:checked + .track {
100
148
  background-color: var(--switch_track-bg--active, var(--bdc-color_active--subtle));
101
149
  --bdc_color: var(--switch_thumb-bg--active, var(--bds-color_interactive));
@@ -130,6 +178,8 @@ export class BdsSwitch extends LitElement {
130
178
  checked: { type: Boolean, reflect: true },
131
179
  required: { type: Boolean, reflect: true },
132
180
  disabled: { type: Boolean, reflect: true },
181
+ error: { type: String, reflect: true },
182
+ size: { type: String, reflect: true },
133
183
  };
134
184
 
135
185
  declare name: string;
@@ -137,9 +187,12 @@ export class BdsSwitch extends LitElement {
137
187
  declare checked: boolean;
138
188
  declare required: boolean;
139
189
  declare disabled: boolean;
190
+ declare error: string | undefined;
191
+ declare size: 'small' | 'medium' | 'large';
140
192
 
141
193
  private _internals!: ElementInternals;
142
194
  private readonly _uid = Math.random().toString(36).slice(2, 8);
195
+ private _initialChecked = false;
143
196
 
144
197
  constructor() {
145
198
  super();
@@ -149,6 +202,12 @@ export class BdsSwitch extends LitElement {
149
202
  this.checked = false;
150
203
  this.required = false;
151
204
  this.disabled = false;
205
+ this.size = 'medium';
206
+ }
207
+
208
+ connectedCallback() {
209
+ super.connectedCallback();
210
+ this._initialChecked = this.checked;
152
211
  }
153
212
 
154
213
  updated(changed: Map<string, unknown>) {
@@ -159,30 +218,38 @@ export class BdsSwitch extends LitElement {
159
218
  }
160
219
 
161
220
  formResetCallback() {
162
- this.checked = false;
163
- this._internals.setFormValue?.(null);
221
+ this.checked = this._initialChecked;
222
+ this._internals.setFormValue?.(this.checked ? this.value : null);
164
223
  }
165
224
 
166
225
  private _handleChange(e: Event) {
167
226
  this.checked = (e.target as HTMLInputElement).checked;
227
+ this.dispatchEvent(new CustomEvent('bds-change', {
228
+ detail: { checked: this.checked },
229
+ bubbles: true,
230
+ composed: true,
231
+ }));
168
232
  }
169
233
 
170
234
  render() {
171
235
  const id = `bds-switch-${this._uid}`;
172
236
  return html`
173
237
  <label for=${id}>
174
- <input
175
- id=${id}
176
- type="checkbox"
177
- role="switch"
178
- class="input"
179
- .checked=${this.checked}
180
- ?required=${this.required}
181
- ?disabled=${this.disabled}
182
- @change=${this._handleChange}
183
- />
184
- <span class="track">
185
- <span class="thumb"></span>
238
+ <span class="track-wrapper">
239
+ <input
240
+ id=${id}
241
+ type="checkbox"
242
+ role="switch"
243
+ class="input"
244
+ .checked=${this.checked}
245
+ ?required=${this.required}
246
+ ?disabled=${this.disabled}
247
+ aria-invalid=${this.error ? 'true' : 'false'}
248
+ @change=${this._handleChange}
249
+ />
250
+ <span class="track">
251
+ <span class="thumb"></span>
252
+ </span>
186
253
  </span>
187
254
  <slot></slot>
188
255
  </label>
@@ -52,4 +52,75 @@ describe('bds-textarea', () => {
52
52
  it('is form-associated', () => {
53
53
  expect(BdsTextarea.formAssociated).toBe(true);
54
54
  });
55
+
56
+ it('renders a label associated with the textarea via for/id', async () => {
57
+ const el = await fixture('<bds-textarea name="message" label="Message"></bds-textarea>');
58
+ const textarea = el.shadowRoot!.querySelector<HTMLTextAreaElement>('textarea')!;
59
+ const label = el.shadowRoot!.querySelector('label')!;
60
+ expect(label.textContent?.trim()).toContain('Message');
61
+ expect(textarea.id).toBeTruthy();
62
+ expect(label.getAttribute('for')).toBe(textarea.id);
63
+ cleanup(el);
64
+ });
65
+
66
+ it('renders no label when label attribute is unset', async () => {
67
+ const el = await fixture('<bds-textarea name="message"></bds-textarea>');
68
+ expect(el.shadowRoot!.querySelector('label')).toBeNull();
69
+ cleanup(el);
70
+ });
71
+
72
+ it('sets aria-invalid and renders an error message when error is set', async () => {
73
+ const el = await fixture('<bds-textarea name="message" error="Message is required"></bds-textarea>');
74
+ const textarea = el.shadowRoot!.querySelector<HTMLTextAreaElement>('textarea')!;
75
+ const errorMsg = el.shadowRoot!.querySelector('.message--error');
76
+ expect(textarea.getAttribute('aria-invalid')).toBe('true');
77
+ expect(errorMsg?.textContent?.trim()).toBe('Message is required');
78
+ expect(textarea.getAttribute('aria-describedby')).toBe(errorMsg!.id);
79
+ cleanup(el);
80
+ });
81
+
82
+ it('renders a hint message when hint is set (no error)', async () => {
83
+ const el = await fixture('<bds-textarea name="message" hint="Max 200 characters"></bds-textarea>');
84
+ const textarea = el.shadowRoot!.querySelector<HTMLTextAreaElement>('textarea')!;
85
+ const hint = el.shadowRoot!.querySelector('.message--hint');
86
+ expect(hint?.textContent?.trim()).toBe('Max 200 characters');
87
+ expect(textarea.getAttribute('aria-describedby')).toBe(hint!.id);
88
+ cleanup(el);
89
+ });
90
+
91
+ it('fires bds-input on keystroke with the current value in detail', async () => {
92
+ const el = await fixture('<bds-textarea name="message"></bds-textarea>');
93
+ const values: string[] = [];
94
+ el.addEventListener('bds-input', (e: Event) => {
95
+ values.push((e as CustomEvent).detail.value);
96
+ });
97
+ const textarea = el.shadowRoot!.querySelector<HTMLTextAreaElement>('textarea')!;
98
+ textarea.value = 'Hi';
99
+ textarea.dispatchEvent(new Event('input', { bubbles: true }));
100
+ expect(values).toEqual(['Hi']);
101
+ cleanup(el);
102
+ });
103
+
104
+ it('fires a composed bds-change event that crosses the shadow boundary', async () => {
105
+ const el = await fixture('<bds-textarea name="message"></bds-textarea>');
106
+ const values: string[] = [];
107
+ // A native `change` on the inner textarea is not composed and never
108
+ // reaches a listener on the host; `bds-change` is re-dispatched so it does.
109
+ el.addEventListener('bds-change', (e: Event) => {
110
+ values.push((e as CustomEvent).detail.value);
111
+ });
112
+ const textarea = el.shadowRoot!.querySelector<HTMLTextAreaElement>('textarea')!;
113
+ textarea.value = 'Hello world';
114
+ textarea.dispatchEvent(new Event('change', { bubbles: true }));
115
+ expect(values).toEqual(['Hello world']);
116
+ cleanup(el);
117
+ });
118
+
119
+ it('restores the initial value on form reset', async () => {
120
+ const el = await fixture('<bds-textarea name="message" value="Initial text"></bds-textarea>') as BdsTextarea;
121
+ el.value = 'Something else entirely';
122
+ el.formResetCallback();
123
+ expect(el.value).toBe('Initial text');
124
+ cleanup(el);
125
+ });
55
126
  });
@@ -11,20 +11,33 @@ import { LitElement, css, html } from 'lit';
11
11
  * value — text value
12
12
  * placeholder — placeholder text
13
13
  * rows — number of visible rows (default: 4)
14
+ * label — visible label text
15
+ * hint — helper text shown below the textarea
16
+ * error — error message; when set the textarea enters error state
14
17
  * required — boolean
15
18
  * disabled — boolean
16
19
  *
17
20
  * Events:
18
- * input — native input event
19
- * change — native change event
21
+ * bds-input — fired on every keystroke; `detail: { value: string }`
22
+ * bds-change — fired when the value is committed; `detail: { value: string }`
20
23
  *
21
24
  * @example
22
- * <bds-textarea name="message" placeholder="Enter message…" rows="6"></bds-textarea>
25
+ * <bds-textarea name="message" label="Message" placeholder="Enter message…" rows="6"></bds-textarea>
23
26
  */
24
27
  export class BdsTextarea extends LitElement {
25
28
  static formAssociated = true;
26
29
 
27
30
  static styles = css`
31
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
32
+ here; otherwise elements with an explicit size + padding size as
33
+ content-box and layouts drift from the React (light-DOM) versions. */
34
+ :host,
35
+ *,
36
+ *::before,
37
+ *::after {
38
+ box-sizing: border-box;
39
+ }
40
+
28
41
  :host {
29
42
  display: block;
30
43
  }
@@ -34,14 +47,28 @@ export class BdsTextarea extends LitElement {
34
47
  pointer-events: none;
35
48
  }
36
49
 
50
+ .label {
51
+ display: block;
52
+ font-size: var(--bds-font_size--body--s);
53
+ font-family: var(--bds-font_family--body);
54
+ font-weight: var(--bds-font_weight--semibold);
55
+ color: var(--bds-color_on-bg);
56
+ margin-block-end: var(--bds-space_xxs);
57
+ }
58
+
59
+ .required {
60
+ color: var(--bds-color_error);
61
+ margin-inline-start: var(--bds-space_xxxs);
62
+ }
63
+
37
64
  .textarea {
38
65
  inline-size: 100%;
39
66
  font-family: var(--bds-font_family--body);
40
67
  font-size: var(--bds-font_size--body);
41
68
  line-height: var(--bds-font_line-height--body);
42
69
  padding: var(--bds-space_s);
43
- border: none;
44
- --bdc_color: var(--textarea_border-color, currentcolor);
70
+ border: var(--textarea_border, none);
71
+ --bdc_color: currentcolor;
45
72
  --bdc-outline_radius: var(--bdc-outline_radius--l);
46
73
 
47
74
  border-radius: var(--textarea_radius, var(--bdc-outline_radius));
@@ -69,6 +96,29 @@ export class BdsTextarea extends LitElement {
69
96
  cursor: not-allowed;
70
97
  resize: none;
71
98
  }
99
+
100
+ :host([error]) .textarea {
101
+ --bdc_color: var(--bdc_color--error);
102
+ }
103
+
104
+ :host([error]) .textarea:focus {
105
+ --bdc_color: var(--bdc_color--error);
106
+ }
107
+
108
+ .message {
109
+ margin-block-start: var(--bds-space_xxs);
110
+ font-size: var(--bds-font_size--body--s);
111
+ font-family: var(--bds-font_family--body);
112
+ }
113
+
114
+ .message--error {
115
+ color: var(--bds-color_error);
116
+ }
117
+
118
+ .message--hint {
119
+ color: var(--bds-color_on-bg--subtle);
120
+ font-style: italic;
121
+ }
72
122
  `;
73
123
 
74
124
  static properties = {
@@ -76,6 +126,9 @@ export class BdsTextarea extends LitElement {
76
126
  value: { type: String },
77
127
  placeholder: { type: String },
78
128
  rows: { type: Number },
129
+ label: { type: String },
130
+ hint: { type: String },
131
+ error: { type: String, reflect: true },
79
132
  required: { type: Boolean, reflect: true },
80
133
  disabled: { type: Boolean, reflect: true },
81
134
  };
@@ -84,10 +137,15 @@ export class BdsTextarea extends LitElement {
84
137
  declare value: string;
85
138
  declare placeholder: string;
86
139
  declare rows: number;
140
+ declare label: string;
141
+ declare hint: string | undefined;
142
+ declare error: string | undefined;
87
143
  declare required: boolean;
88
144
  declare disabled: boolean;
89
145
 
90
146
  private _internals!: ElementInternals;
147
+ private readonly _uid = Math.random().toString(36).slice(2, 8);
148
+ private _initialValue = '';
91
149
 
92
150
  constructor() {
93
151
  super();
@@ -96,10 +154,16 @@ export class BdsTextarea extends LitElement {
96
154
  this.value = '';
97
155
  this.placeholder = '';
98
156
  this.rows = 4;
157
+ this.label = '';
99
158
  this.required = false;
100
159
  this.disabled = false;
101
160
  }
102
161
 
162
+ connectedCallback() {
163
+ super.connectedCallback();
164
+ this._initialValue = this.value;
165
+ }
166
+
103
167
  updated(changed: Map<string, unknown>) {
104
168
  super.updated(changed);
105
169
  if (changed.has('value')) {
@@ -108,17 +172,45 @@ export class BdsTextarea extends LitElement {
108
172
  }
109
173
 
110
174
  formResetCallback() {
111
- this.value = '';
112
- this._internals.setFormValue?.(null);
175
+ this.value = this._initialValue;
176
+ this._internals.setFormValue?.(this.value || null);
113
177
  }
114
178
 
115
179
  private _handleInput(e: Event) {
116
180
  this.value = (e.target as HTMLTextAreaElement).value;
181
+ this.dispatchEvent(new CustomEvent('bds-input', {
182
+ detail: { value: this.value },
183
+ bubbles: true,
184
+ composed: true,
185
+ }));
186
+ }
187
+
188
+ private _handleChange(e: Event) {
189
+ this.value = (e.target as HTMLTextAreaElement).value;
190
+ this.dispatchEvent(new CustomEvent('bds-change', {
191
+ detail: { value: this.value },
192
+ bubbles: true,
193
+ composed: true,
194
+ }));
117
195
  }
118
196
 
119
197
  render() {
198
+ const id = `bds-textarea-${this._uid}`;
199
+ const hintId = `${id}-hint`;
200
+ const errorId = `${id}-error`;
201
+ const describedBy = [this.error && errorId, this.hint && hintId].filter(Boolean).join(' ') || undefined;
202
+
120
203
  return html`
204
+ ${this.label
205
+ ? html`
206
+ <label class="label" for=${id}>
207
+ ${this.label}
208
+ ${this.required ? html`<span class="required" aria-hidden="true">*</span>` : ''}
209
+ </label>
210
+ `
211
+ : ''}
121
212
  <textarea
213
+ id=${id}
122
214
  class="textarea"
123
215
  name=${this.name}
124
216
  .value=${this.value}
@@ -126,8 +218,17 @@ export class BdsTextarea extends LitElement {
126
218
  rows=${this.rows}
127
219
  ?required=${this.required}
128
220
  ?disabled=${this.disabled}
221
+ aria-invalid=${this.error ? 'true' : 'false'}
222
+ aria-describedby=${describedBy ?? ''}
223
+ aria-required=${this.required ? 'true' : 'false'}
129
224
  @input=${this._handleInput}
225
+ @change=${this._handleChange}
130
226
  ></textarea>
227
+ ${this.error
228
+ ? html`<p id=${errorId} class="message message--error" role="alert">${this.error}</p>`
229
+ : this.hint
230
+ ? html`<p id=${hintId} class="message message--hint">${this.hint}</p>`
231
+ : ''}
131
232
  `;
132
233
  }
133
234
  }
@@ -50,6 +50,7 @@ Numeric `column-span="n"` is literal — it does not adapt. Use named values for
50
50
  | `is-masonry` | boolean | `false` | Enables masonry layout (CSS Grid Level 3 "grid-lanes"). Uses native CSS when supported, otherwise runs a JS polyfill. |
51
51
  | `auto-span-media` | boolean | `false` | Child `<bds-grid-item>`s without an explicit `column-span` resolve their span from the intrinsic aspect ratio of their first `<img>`/`<video>` descendant. See [Aspect-ratio auto-span](#aspect-ratio-auto-span). |
52
52
  | `masonry-preferred-column-span` | named span \| number | — | In masonry mode, items without an explicit `column-span` try to span this value, demoting to fit at row ends. Accepts the same named values as `column-span` (`"half"`, `"one-third"`, etc.). See [Masonry preferred column span](#masonry-preferred-column-span). |
53
+ | `masonry-row-unit-px` | number | — | Masonry only. Manual override for the row-track unit (px) the polyfill uses for `grid-auto-rows` and as the ceil() granularity of each item's row span. Omit to let the polyfill auto-pick the smallest unit that keeps the implicit row count under any browser cap. |
53
54
 
54
55
  ## Slots
55
56
 
@@ -75,7 +76,7 @@ Numeric `column-span="n"` is literal — it does not adapt. Use named values for
75
76
 
76
77
  ## Nesting
77
78
 
78
- Grids can nest. A `variant="page"` inside another grid uses `grid-column: 1 / -1` so nested page grids always occupy the full width of their container.
79
+ Grids can nest. A `variant="page"` inside another grid uses `grid-column: 1 / -1` so nested page grids always occupy the full width of their container. Because `<bds-grid>` renders its track layout inside a shadow root, the spanning rule is applied to the **host element** itself (`:host([variant='page'])`) rather than the internal `.grid` div — the host is the element that actually participates in the parent grid's tracks.
79
80
 
80
81
  ```html
81
82
  <bds-grid variant="main">
@@ -118,7 +119,9 @@ Targets [CSS Grid Layout Module Level 3](https://www.w3.org/TR/css-grid-3/) ("gr
118
119
  - **Native path** — when the browser supports `display: grid-lanes` or `grid-template-rows: masonry`, CSS handles everything and the polyfill short-circuits.
119
120
  - **Polyfill path** — row-span packing: the container collapses to a 1 px row track with `grid-auto-flow: dense`, and each item is given `grid-row-end: span ceil(height + gap)`. Items stay in **normal flow** (no absolute positioning), so surrounding content lays out correctly around the grid.
120
121
 
121
- Re-runs on child size changes (`ResizeObserver` on each child) and child addition/removal (`MutationObserver` on the host). The container itself is not observed to avoid a feedback loop — setting row-spans changes the container's block-size.
122
+ Re-runs on child size changes (`ResizeObserver` on each child, and on any `<img>`/`<video>` descendant so late-decoding media is caught the same frame) and child addition/removal (`MutationObserver` on the host). The container itself is not observed to avoid a feedback loop — setting row-spans changes the container's block-size.
123
+
124
+ A `ResizeObserver`-triggered re-run applies immediately, except while the page is actively scrolling — those are deferred until scroll idles (150ms) to avoid a compositor race where the re-pack visibly overlaps already-painted content. `MutationObserver`-triggered runs (new children) and the initial layout are always deferred one frame via `requestAnimationFrame` so the browser lays out new content before it's measured. Masonry re-engages automatically if the element is disconnected and reconnected (e.g. moved in the DOM).
122
125
 
123
126
  ### Span behavior in masonry
124
127
 
@@ -131,6 +134,7 @@ Re-runs on child size changes (`ResizeObserver` on each child) and child additio
131
134
  - Initial layout is one measure pass + one write pass (heights are batched to avoid per-item reflows).
132
135
  - Re-layout is scheduled via `requestAnimationFrame` so bursts of observer callbacks coalesce into a single run per frame.
133
136
  - Row-gap is baked into each item's span, so ceil-rounding only introduces sub-pixel overshoot per item — not visible in practice.
137
+ - Set `masonry-row-unit-px` to override the auto-picked row-track unit if you've confirmed the auto-tuner is wrong for your gallery (see [Attributes](#attributes)).
134
138
 
135
139
  ### Accessibility
136
140
 
@@ -10,6 +10,7 @@ function BdsGrid({
10
10
  isCentered,
11
11
  isMasonry,
12
12
  masonryPreferredColumnSpan,
13
+ masonryRowUnitPx,
13
14
  children,
14
15
  }: {
15
16
  variant?: GridVariant;
@@ -17,6 +18,7 @@ function BdsGrid({
17
18
  isCentered?: boolean;
18
19
  isMasonry?: boolean;
19
20
  masonryPreferredColumnSpan?: string;
21
+ masonryRowUnitPx?: number;
20
22
  children?: React.ReactNode;
21
23
  }) {
22
24
  return React.createElement(
@@ -27,6 +29,7 @@ function BdsGrid({
27
29
  'is-centered': isCentered || undefined,
28
30
  'is-masonry': isMasonry || undefined,
29
31
  'masonry-preferred-column-span': masonryPreferredColumnSpan,
32
+ 'masonry-row-unit-px': masonryRowUnitPx,
30
33
  },
31
34
  children,
32
35
  );
@@ -78,6 +81,7 @@ const meta = {
78
81
  control: 'select',
79
82
  options: ['full', 'three-quarters', 'two-thirds', 'half', 'one-third', 'one-quarter'],
80
83
  },
84
+ masonryRowUnitPx: { control: 'number' },
81
85
  },
82
86
  } satisfies Meta<typeof BdsGrid>;
83
87
 
@@ -48,6 +48,27 @@ describe('bds-grid-item', () => {
48
48
  cleanup(el);
49
49
  });
50
50
 
51
+ it('restores default span after row-span attribute is removed', async () => {
52
+ const el = (await fixture('<bds-grid-item row-span="2"></bds-grid-item>')) as BdsGridItem;
53
+ expect(el.style.gridRow).toBe('span 2');
54
+ el.removeAttribute('row-span');
55
+ await el.updateComplete;
56
+ expect(el.style.gridRow).toBe('');
57
+ cleanup(el);
58
+ });
59
+
60
+ it('restores default full-span column after start-column/end-column are removed', async () => {
61
+ const el = (await fixture(
62
+ '<bds-grid-item start-column="2" end-column="5"></bds-grid-item>',
63
+ )) as BdsGridItem;
64
+ expect(el.style.gridColumn).toBe('2 / 5');
65
+ el.removeAttribute('start-column');
66
+ el.removeAttribute('end-column');
67
+ await el.updateComplete;
68
+ expect(el.style.gridColumn).toBe('var(--bds-grid_span-100)');
69
+ cleanup(el);
70
+ });
71
+
51
72
  it('renders slotted children', async () => {
52
73
  const el = await fixture('<bds-grid-item>cell</bds-grid-item>');
53
74
  expect(el.textContent?.trim()).toBe('cell');
@@ -55,6 +55,16 @@ function resolveSpan(value: Exclude<GridItemSpanValue, 'auto'> | undefined): str
55
55
  */
56
56
  export class BdsGridItem extends LitElement {
57
57
  static styles = css`
58
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
59
+ here; otherwise elements with an explicit size + padding size as
60
+ content-box and layouts drift from the React (light-DOM) versions. */
61
+ :host,
62
+ *,
63
+ *::before,
64
+ *::after {
65
+ box-sizing: border-box;
66
+ }
67
+
58
68
  :host {
59
69
  display: block;
60
70
  min-inline-size: 0;
@@ -151,8 +161,12 @@ export class BdsGridItem extends LitElement {
151
161
  const effective = this._effectiveColumnSpan();
152
162
  if (effective === 'auto') this._resolveAutoSpan();
153
163
 
154
- const hasExplicitRange =
155
- this.startColumn !== undefined || this.endColumn !== undefined;
164
+ // Lit's default `Number` attribute converter resolves a removed attribute
165
+ // to `null` (not `undefined`), so these checks must use `!= null` — an
166
+ // `!== undefined` check would keep treating a removed attribute as "set"
167
+ // and produce `span null` / a stuck explicit range instead of restoring
168
+ // the default span behaviour.
169
+ const hasExplicitRange = this.startColumn != null || this.endColumn != null;
156
170
 
157
171
  const resolved = effective === 'auto' ? this._autoSpan : effective;
158
172
  const gridColumn = hasExplicitRange
@@ -160,7 +174,7 @@ export class BdsGridItem extends LitElement {
160
174
  : resolveSpan(resolved);
161
175
 
162
176
  this.style.gridColumn = gridColumn;
163
- this.style.gridRow = this.rowSpan !== undefined ? `span ${this.rowSpan}` : '';
177
+ this.style.gridRow = this.rowSpan != null ? `span ${this.rowSpan}` : '';
164
178
 
165
179
  // Marker the masonry polyfill reads to know this item's span wasn't set by
166
180
  // the consumer — it may override with the parent `masonry-preferred-column-span`.