@boostdev/design-system-components 2.7.1 → 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 (535) 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-3AY2YKCM.js +223 -0
  15. package/dist/web-components/chunk-3WAV6MDN.js +212 -0
  16. package/dist/web-components/{chunk-CZBIVR4V.js → chunk-47AT5KZD.js} +88 -16
  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-5R6IFELV.js → chunk-AIBOHNDU.js} +11 -1
  23. package/dist/web-components/{chunk-ITHLKR7Q.js → chunk-AP4RDUL2.js} +16 -5
  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-CM3KYZ6Z.js → chunk-JH6LQKLK.js} +78 -6
  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-57TRNCWQ.js → chunk-WFVKU4GX.js} +22 -11
  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-F5E3IT7G.js → chunk-XIZWAWMJ.js} +50 -11
  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 +60 -13
  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 +102 -17
  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 +136 -23
  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 +96 -8
  441. package/src/web-components/interaction/form/bds-slider.spec.ts +83 -0
  442. package/src/web-components/interaction/form/bds-slider.ts +181 -33
  443. package/src/web-components/interaction/form/bds-switch.spec.ts +68 -0
  444. package/src/web-components/interaction/form/bds-switch.ts +142 -41
  445. package/src/web-components/interaction/form/bds-textarea.spec.ts +71 -0
  446. package/src/web-components/interaction/form/bds-textarea.ts +109 -8
  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 +30 -12
  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 +113 -36
  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 +16 -15
  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 +11 -1
  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 +19 -6
  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-5TFWOPLH.js +0 -49
  526. package/dist/web-components/chunk-7FMR53BW.js +0 -127
  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-IYGF6CD5.js +0 -118
  530. package/dist/web-components/chunk-QBB6ZX4R.js +0 -107
  531. package/dist/web-components/chunk-Y3POAKGS.js +0 -133
  532. package/dist/web-components/chunk-Y4MAXTMA.js +0 -80
  533. package/dist/web-components/chunk-Y7V7LM6R.js +0 -98
  534. package/src/web-components/ui/BdsButtonGroup.mdx +0 -69
  535. package/src/web-components/ui/BdsButtonGroup.stories.tsx +0 -66
@@ -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,15 +47,29 @@ 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);
45
- --bdc-outline_radius: var(--bds-border_radius--xs);
70
+ border: var(--textarea_border, none);
71
+ --bdc_color: currentcolor;
72
+ --bdc-outline_radius: var(--bdc-outline_radius--l);
46
73
 
47
74
  border-radius: var(--textarea_radius, var(--bdc-outline_radius));
48
75
  box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--textarea_shadow, var(--bdc-outline_shadow));
@@ -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`.
@@ -1,7 +1,17 @@
1
1
  import { fixture, cleanup } from '../test/helpers';
2
2
  import { BdsGrid } from './bds-grid';
3
+ import type { BdsGridItem } from './bds-grid-item';
4
+ // Side-effect import: registers the `bds-grid-item` custom element. BdsGridItem
5
+ // above is used only as a type, which esbuild's isolatedModules-style transform
6
+ // would otherwise elide — silently leaving <bds-grid-item> un-upgraded (a plain
7
+ // HTMLElement) in every fixture in this file that nests one inside <bds-grid>.
3
8
  import './bds-grid-item';
4
9
 
10
+ /** Flush one requestAnimationFrame tick — the masonry polyfill's schedule/apply cycle. */
11
+ function nextFrame(): Promise<void> {
12
+ return new Promise((resolve) => requestAnimationFrame(() => resolve()));
13
+ }
14
+
5
15
  describe('bds-grid', () => {
6
16
  it('is registered as a custom element', () => {
7
17
  expect(customElements.get('bds-grid')).toBe(BdsGrid);
@@ -67,4 +77,92 @@ describe('bds-grid', () => {
67
77
  expect(el.masonryPreferredColumnSpan).toBe('half');
68
78
  cleanup(el);
69
79
  });
80
+
81
+ it('reflects masonry-row-unit-px as a number property', async () => {
82
+ const el = await fixture(
83
+ '<bds-grid is-masonry masonry-row-unit-px="8"></bds-grid>',
84
+ ) as BdsGrid;
85
+ expect(el.masonryRowUnitPx).toBe(8);
86
+ cleanup(el);
87
+ });
88
+
89
+ it('applies grid-column: 1 / -1 to the host (not the inner .grid) for variant="page", so nested page grids span the parent', () => {
90
+ const cssText = BdsGrid.styles.cssText;
91
+ expect(cssText).toMatch(/:host\(\[variant='page'\]\)\s*{[^}]*grid-column:\s*1\s*\/\s*-1/);
92
+ // The inner .grid.--page rule must not also carry grid-column — the host
93
+ // (which participates in a parent grid's tracks) owns spanning; the inner
94
+ // div only owns its own track sizing.
95
+ expect(cssText).not.toMatch(/\.grid\.--page\s*{[^}]*grid-column:/);
96
+ });
97
+
98
+ it('falls back to the --grid_columns default when the columns attribute is removed', async () => {
99
+ const el = await fixture(
100
+ '<bds-grid variant="custom" columns="6"></bds-grid>',
101
+ ) as BdsGrid;
102
+ const grid = el.shadowRoot!.querySelector('.grid') as HTMLElement;
103
+ expect(grid.getAttribute('style')).toContain('--grid_columns: 6');
104
+
105
+ el.removeAttribute('columns');
106
+ await el.updateComplete;
107
+
108
+ // Lit's Number converter yields `null` (not `undefined`) when a reflected
109
+ // attribute is removed. A strict `!== undefined` check would still match
110
+ // and emit the literal string "null", making `var(--grid_columns, 4)`
111
+ // resolve to an invalid custom property and collapse the grid to one
112
+ // column instead of falling back to the default.
113
+ expect(grid.getAttribute('style') ?? '').not.toContain('null');
114
+ cleanup(el);
115
+ });
116
+
117
+ it('does not clear a slotted grid-item row-span style when re-rendering a non-masonry grid', async () => {
118
+ const el = await fixture(
119
+ '<bds-grid><bds-grid-item row-span="3">A</bds-grid-item></bds-grid>',
120
+ ) as BdsGrid;
121
+ const item = el.querySelector('bds-grid-item') as BdsGridItem;
122
+ await item.updateComplete;
123
+ expect(item.style.gridRow).toBe('span 3');
124
+
125
+ // An unrelated property change re-renders bds-grid. Before the fix,
126
+ // updated() called clearMasonryLayout() unconditionally on every
127
+ // non-masonry update, wiping the row-span this GridItem's own render set.
128
+ el.isCentered = true;
129
+ await el.updateComplete;
130
+
131
+ expect(item.style.gridRow).toBe('span 3');
132
+ cleanup(el);
133
+ });
134
+
135
+ it('applies a masonry row-span to slotted items after mount', async () => {
136
+ const el = await fixture(
137
+ '<bds-grid is-masonry><bds-grid-item>A</bds-grid-item></bds-grid>',
138
+ ) as BdsGrid;
139
+ await nextFrame();
140
+ const item = el.querySelector('bds-grid-item') as HTMLElement;
141
+ // jsdom reports offsetHeight 0 for unlaid-out elements, so the polyfill
142
+ // assigns the 1-row floor.
143
+ expect(item.style.gridRowEnd).toBe('span 1');
144
+ cleanup(el);
145
+ });
146
+
147
+ it('re-engages masonry after being disconnected and reconnected', async () => {
148
+ const el = await fixture(
149
+ '<bds-grid is-masonry><bds-grid-item>A</bds-grid-item></bds-grid>',
150
+ ) as BdsGrid;
151
+ await nextFrame();
152
+ const item = el.querySelector('bds-grid-item') as HTMLElement;
153
+ expect(item.style.gridRowEnd).toBe('span 1');
154
+
155
+ const parent = el.parentElement!;
156
+ el.remove();
157
+ // disconnectedCallback tears down masonry synchronously.
158
+ expect(item.style.gridRowEnd).toBe('');
159
+
160
+ parent.appendChild(el);
161
+ // No property changed, so updated() won't fire — connectedCallback must
162
+ // re-arm masonry on its own for this to re-apply.
163
+ await nextFrame();
164
+ expect(item.style.gridRowEnd).toBe('span 1');
165
+
166
+ cleanup(el);
167
+ });
70
168
  });