@boostdev/design-system-components 2.14.1 → 2.16.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 (290) hide show
  1. package/AGENTS.md +20 -15
  2. package/dist/client.cjs +271 -205
  3. package/dist/client.css +808 -648
  4. package/dist/client.d.cts +19 -4
  5. package/dist/client.d.ts +19 -4
  6. package/dist/client.js +206 -139
  7. package/dist/index.cjs +271 -205
  8. package/dist/index.css +808 -648
  9. package/dist/index.d.cts +19 -4
  10. package/dist/index.d.ts +19 -4
  11. package/dist/index.js +206 -139
  12. package/dist/web-components/chunk-2DHW2WJZ.js +154 -0
  13. package/dist/web-components/{chunk-LDIPCTRN.js → chunk-2MJTO2IW.js} +48 -2
  14. package/dist/web-components/{chunk-3WAV6MDN.js → chunk-3FSGP35V.js} +1 -0
  15. package/dist/web-components/{chunk-KA3VPKMY.js → chunk-4MB2ICSK.js} +1 -0
  16. package/dist/web-components/{chunk-IDSM74LM.js → chunk-6XL5P76D.js} +3 -3
  17. package/dist/web-components/{chunk-P24JYC5V.js → chunk-7VXRMCAL.js} +10 -4
  18. package/dist/web-components/{chunk-CUKQ2L5B.js → chunk-AGI6YQRC.js} +9 -16
  19. package/dist/web-components/chunk-BDRV452N.js +51 -0
  20. package/dist/web-components/{chunk-3AY2YKCM.js → chunk-CKCPV347.js} +1 -0
  21. package/dist/web-components/{chunk-HBLJ4SLY.js → chunk-DD3OUXNG.js} +5 -5
  22. package/dist/web-components/{chunk-RVF7W653.js → chunk-DGOCPKHI.js} +30 -10
  23. package/dist/web-components/{chunk-2OLVQUBU.js → chunk-EYCW7TKB.js} +8 -15
  24. package/dist/web-components/{chunk-VANAKB6D.js → chunk-GPJWLAKU.js} +33 -16
  25. package/dist/web-components/{chunk-VED2DFJ6.js → chunk-GQNQBEYR.js} +8 -15
  26. package/dist/web-components/{chunk-WFVKU4GX.js → chunk-IKAJLROF.js} +49 -4
  27. package/dist/web-components/{chunk-RSZ3BWBK.js → chunk-KSK4RZZQ.js} +30 -7
  28. package/dist/web-components/{chunk-FEDABOR5.js → chunk-LHFUXZ52.js} +7 -15
  29. package/dist/web-components/{chunk-M3ECULRH.js → chunk-RBF7WPLI.js} +10 -8
  30. package/dist/web-components/{chunk-6G22I2CK.js → chunk-RZH5IVB5.js} +31 -1
  31. package/dist/web-components/chunk-SRKQ6GST.js +40 -0
  32. package/dist/web-components/{chunk-5HUAFCU6.js → chunk-TJV7UIXC.js} +7 -0
  33. package/dist/web-components/{chunk-J3HVUIUH.js → chunk-XI2U6U26.js} +8 -15
  34. package/dist/web-components/{chunk-JH6LQKLK.js → chunk-XJU7KITO.js} +8 -15
  35. package/dist/web-components/{chunk-XIZWAWMJ.js → chunk-XTPUKUN5.js} +16 -6
  36. package/dist/web-components/{chunk-JV5QT2OU.js → chunk-YLDQOESI.js} +27 -13
  37. package/dist/web-components/globals.js +1 -1
  38. package/dist/web-components/index.d.ts +1 -0
  39. package/dist/web-components/index.js +34 -29
  40. package/dist/web-components/interaction/bds-button.js +1 -1
  41. package/dist/web-components/interaction/bds-dropdown-menu-group.d.ts +41 -0
  42. package/dist/web-components/interaction/bds-dropdown-menu-group.js +7 -0
  43. package/dist/web-components/interaction/bds-dropdown-menu-item.d.ts +14 -1
  44. package/dist/web-components/interaction/bds-dropdown-menu-item.js +1 -1
  45. package/dist/web-components/interaction/bds-dropdown-menu.d.ts +2 -1
  46. package/dist/web-components/interaction/bds-dropdown-menu.js +1 -1
  47. package/dist/web-components/interaction/bds-tab.d.ts +5 -0
  48. package/dist/web-components/interaction/bds-tab.js +1 -1
  49. package/dist/web-components/interaction/bds-tooltip.js +1 -1
  50. package/dist/web-components/interaction/form/bds-checkbox-group.d.ts +6 -0
  51. package/dist/web-components/interaction/form/bds-checkbox-group.js +2 -1
  52. package/dist/web-components/interaction/form/bds-checkbox.js +1 -1
  53. package/dist/web-components/interaction/form/bds-combobox.js +2 -1
  54. package/dist/web-components/interaction/form/bds-form-input.js +2 -1
  55. package/dist/web-components/interaction/form/bds-number-input.js +2 -1
  56. package/dist/web-components/interaction/form/bds-radio-group.d.ts +5 -0
  57. package/dist/web-components/interaction/form/bds-radio-group.js +2 -1
  58. package/dist/web-components/interaction/form/bds-radio.js +1 -1
  59. package/dist/web-components/interaction/form/bds-select.js +2 -1
  60. package/dist/web-components/interaction/form/bds-slider.js +2 -1
  61. package/dist/web-components/interaction/form/bds-switch.js +1 -1
  62. package/dist/web-components/interaction/form/bds-textarea.js +2 -1
  63. package/dist/web-components/ui/bds-alert.d.ts +8 -1
  64. package/dist/web-components/ui/bds-alert.js +1 -1
  65. package/dist/web-components/ui/bds-avatar.d.ts +11 -0
  66. package/dist/web-components/ui/bds-avatar.js +1 -1
  67. package/dist/web-components/ui/bds-notification-banner.d.ts +14 -1
  68. package/dist/web-components/ui/bds-notification-banner.js +1 -1
  69. package/dist/web-components/ui/bds-section-header.d.ts +6 -0
  70. package/dist/web-components/ui/bds-section-header.js +1 -1
  71. package/dist/web-components/ui/bds-separator.js +1 -1
  72. package/dist/web-components/ui/bds-table.js +1 -1
  73. package/package.json +6 -2
  74. package/src/components/interaction/Button/Button.mdx +7 -0
  75. package/src/components/interaction/Button/Button.module.css +17 -9
  76. package/src/components/interaction/Button/Button.stories.tsx +28 -0
  77. package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +35 -4
  78. package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +58 -10
  79. package/src/components/interaction/DropdownMenu/DropdownMenu.spec.tsx +114 -1
  80. package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +25 -0
  81. package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +69 -17
  82. package/src/components/interaction/form/Checkbox/Checkbox.module.css +1 -0
  83. package/src/components/interaction/form/Checkbox/Checkbox.spec.tsx +7 -0
  84. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +1 -0
  85. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css +7 -0
  86. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.spec.tsx +22 -0
  87. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +1 -1
  88. package/src/components/interaction/form/Combobox/Combobox.module.css +1 -0
  89. package/src/components/interaction/form/Combobox/Combobox.spec.tsx +7 -0
  90. package/src/components/interaction/form/FormInput/FormInput.mdx +9 -0
  91. package/src/components/interaction/form/FormInput/FormInput.module.css +1 -0
  92. package/src/components/interaction/form/FormInput/FormInput.spec.tsx +7 -0
  93. package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -0
  94. package/src/components/interaction/form/NumberInput/NumberInput.spec.tsx +7 -0
  95. package/src/components/interaction/form/Radio/Radio.module.css +1 -0
  96. package/src/components/interaction/form/Radio/Radio.spec.tsx +7 -0
  97. package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +1 -0
  98. package/src/components/interaction/form/RadioGroup/RadioGroup.module.css +7 -0
  99. package/src/components/interaction/form/RadioGroup/RadioGroup.spec.tsx +22 -0
  100. package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +1 -1
  101. package/src/components/interaction/form/Select/Select.module.css +1 -0
  102. package/src/components/interaction/form/Select/Select.spec.tsx +7 -0
  103. package/src/components/interaction/form/Switch/Switch.module.css +1 -0
  104. package/src/components/interaction/form/Switch/Switch.spec.tsx +7 -0
  105. package/src/components/interaction/form/Textarea/Textarea.module.css +1 -0
  106. package/src/components/interaction/form/Textarea/Textarea.spec.tsx +7 -0
  107. package/src/components/interaction/form/atoms/Message.module.css +14 -3
  108. package/src/components/interaction/form/atoms/Message.spec.tsx +22 -0
  109. package/src/components/interaction/form/atoms/Message.tsx +5 -0
  110. package/src/components/layout/SectionHeader/SectionHeader.mdx +18 -5
  111. package/src/components/layout/SectionHeader/SectionHeader.module.css +35 -14
  112. package/src/components/layout/SectionHeader/SectionHeader.spec.tsx +44 -0
  113. package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +5 -0
  114. package/src/components/layout/SectionHeader/SectionHeader.tsx +3 -1
  115. package/src/components/ui/Alert/Alert.mdx +11 -0
  116. package/src/components/ui/Alert/Alert.module.css +30 -1
  117. package/src/components/ui/Alert/Alert.spec.tsx +21 -0
  118. package/src/components/ui/Alert/Alert.stories.tsx +20 -0
  119. package/src/components/ui/Alert/Alert.tsx +8 -1
  120. package/src/components/ui/Avatar/Avatar.mdx +22 -2
  121. package/src/components/ui/Avatar/Avatar.module.css +29 -2
  122. package/src/components/ui/Avatar/Avatar.spec.tsx +26 -0
  123. package/src/components/ui/Avatar/Avatar.stories.tsx +23 -0
  124. package/src/components/ui/Avatar/Avatar.tsx +4 -2
  125. package/src/components/ui/Avatar/avatarTone.spec.ts +41 -0
  126. package/src/components/ui/Avatar/avatarTone.ts +22 -0
  127. package/src/components/ui/Collapsible/Collapsible.module.css +1 -2
  128. package/src/components/ui/NotificationBanner/NotificationBanner.mdx +14 -0
  129. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +31 -1
  130. package/src/components/ui/NotificationBanner/NotificationBanner.spec.tsx +29 -0
  131. package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +25 -0
  132. package/src/components/ui/NotificationBanner/NotificationBanner.tsx +15 -1
  133. package/src/components/ui/Separator/Separator.mdx +1 -0
  134. package/src/components/ui/Separator/Separator.module.css +6 -0
  135. package/src/components/ui/Separator/Separator.spec.tsx +19 -0
  136. package/src/components/ui/Separator/Separator.stories.tsx +15 -0
  137. package/src/components/ui/Table/Table.mdx +4 -1
  138. package/src/components/ui/Table/Table.module.css +7 -7
  139. package/src/components/ui/Table/Table.spec.tsx +11 -0
  140. package/src/components/ui/Tabs/Tabs.mdx +11 -5
  141. package/src/components/ui/Tabs/Tabs.module.css +23 -5
  142. package/src/components/ui/Tabs/Tabs.spec.tsx +39 -0
  143. package/src/components/ui/Tabs/Tabs.tsx +2 -1
  144. package/src/components/ui/Tabs/getNodeText.spec.tsx +23 -0
  145. package/src/components/ui/Tabs/getNodeText.ts +13 -0
  146. package/src/components/ui/Tooltip/Tooltip.mdx +6 -3
  147. package/src/components/ui/Tooltip/Tooltip.module.css +6 -2
  148. package/src/components/ui/Tooltip/Tooltip.spec.tsx +10 -0
  149. package/src/components/ui/Tooltip/Tooltip.stories.tsx +13 -0
  150. package/src/css/bdc.css +5 -2
  151. package/src/css/bdc.spec.ts +65 -0
  152. package/src/stories/DesignSystem/Borders.mdx +17 -9
  153. package/src/web-components/globals.ts +10 -4
  154. package/src/web-components/index.ts +1 -0
  155. package/src/web-components/interaction/BdsAccordion.mdx +1 -1
  156. package/src/web-components/interaction/BdsAccordion.stories.tsx +1 -1
  157. package/src/web-components/interaction/BdsButton.mdx +9 -1
  158. package/src/web-components/interaction/BdsButton.stories.tsx +42 -1
  159. package/src/web-components/interaction/BdsCollapsible.mdx +1 -1
  160. package/src/web-components/interaction/BdsCollapsible.stories.tsx +1 -1
  161. package/src/web-components/interaction/BdsDialog.mdx +1 -1
  162. package/src/web-components/interaction/BdsDialog.stories.tsx +1 -1
  163. package/src/web-components/interaction/BdsDrawer.mdx +1 -1
  164. package/src/web-components/interaction/BdsDrawer.stories.tsx +1 -1
  165. package/src/web-components/interaction/BdsDropdownMenu.mdx +62 -6
  166. package/src/web-components/interaction/BdsDropdownMenu.stories.tsx +32 -7
  167. package/src/web-components/interaction/BdsPopover.mdx +1 -1
  168. package/src/web-components/interaction/BdsPopover.stories.tsx +1 -1
  169. package/src/web-components/interaction/BdsSkipLink.mdx +1 -1
  170. package/src/web-components/interaction/BdsSkipLink.stories.tsx +1 -1
  171. package/src/web-components/interaction/BdsTabs.mdx +10 -1
  172. package/src/web-components/interaction/BdsTabs.stories.tsx +1 -1
  173. package/src/web-components/interaction/BdsToastProvider.mdx +1 -1
  174. package/src/web-components/interaction/BdsToastProvider.stories.tsx +1 -1
  175. package/src/web-components/interaction/BdsTooltip.mdx +13 -5
  176. package/src/web-components/interaction/BdsTooltip.stories.tsx +12 -12
  177. package/src/web-components/interaction/bds-button.ts +16 -6
  178. package/src/web-components/interaction/bds-dropdown-menu-group.ts +72 -0
  179. package/src/web-components/interaction/bds-dropdown-menu-item.ts +65 -17
  180. package/src/web-components/interaction/bds-dropdown-menu.spec.ts +135 -0
  181. package/src/web-components/interaction/bds-dropdown-menu.ts +5 -4
  182. package/src/web-components/interaction/bds-tab.spec.ts +34 -2
  183. package/src/web-components/interaction/bds-tab.ts +31 -6
  184. package/src/web-components/interaction/bds-tooltip.spec.ts +16 -0
  185. package/src/web-components/interaction/bds-tooltip.ts +10 -8
  186. package/src/web-components/interaction/form/BdsCheckbox.mdx +5 -1
  187. package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +1 -1
  188. package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +5 -1
  189. package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +1 -1
  190. package/src/web-components/interaction/form/BdsCombobox.mdx +5 -1
  191. package/src/web-components/interaction/form/BdsCombobox.stories.tsx +1 -1
  192. package/src/web-components/interaction/form/BdsFieldGroup.stories.tsx +1 -1
  193. package/src/web-components/interaction/form/BdsFileInput.mdx +1 -1
  194. package/src/web-components/interaction/form/BdsFileInput.stories.tsx +1 -1
  195. package/src/web-components/interaction/form/BdsFormInput.mdx +8 -1
  196. package/src/web-components/interaction/form/BdsFormInput.stories.tsx +1 -1
  197. package/src/web-components/interaction/form/BdsNumberInput.mdx +5 -1
  198. package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +1 -1
  199. package/src/web-components/interaction/form/BdsRadio.mdx +5 -1
  200. package/src/web-components/interaction/form/BdsRadio.stories.tsx +1 -1
  201. package/src/web-components/interaction/form/BdsRadioGroup.mdx +5 -1
  202. package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +1 -1
  203. package/src/web-components/interaction/form/BdsSegmentedControl.mdx +1 -1
  204. package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +1 -1
  205. package/src/web-components/interaction/form/BdsSelect.mdx +5 -1
  206. package/src/web-components/interaction/form/BdsSelect.stories.tsx +1 -1
  207. package/src/web-components/interaction/form/BdsSlider.mdx +5 -1
  208. package/src/web-components/interaction/form/BdsSlider.stories.tsx +1 -1
  209. package/src/web-components/interaction/form/BdsSwitch.mdx +5 -1
  210. package/src/web-components/interaction/form/BdsSwitch.stories.tsx +1 -1
  211. package/src/web-components/interaction/form/BdsTextarea.mdx +5 -1
  212. package/src/web-components/interaction/form/BdsTextarea.stories.tsx +1 -1
  213. package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +32 -3
  214. package/src/web-components/interaction/form/bds-checkbox-group.ts +33 -17
  215. package/src/web-components/interaction/form/bds-checkbox.ts +1 -0
  216. package/src/web-components/interaction/form/bds-combobox.ts +6 -16
  217. package/src/web-components/interaction/form/bds-form-input.ts +6 -16
  218. package/src/web-components/interaction/form/bds-number-input.ts +6 -16
  219. package/src/web-components/interaction/form/bds-radio-group.spec.ts +32 -3
  220. package/src/web-components/interaction/form/bds-radio-group.ts +26 -14
  221. package/src/web-components/interaction/form/bds-radio.ts +1 -0
  222. package/src/web-components/interaction/form/bds-select.ts +6 -16
  223. package/src/web-components/interaction/form/bds-slider.ts +5 -16
  224. package/src/web-components/interaction/form/bds-switch.ts +1 -0
  225. package/src/web-components/interaction/form/bds-textarea.ts +6 -16
  226. package/src/web-components/interaction/form/field-message.spec.ts +62 -0
  227. package/src/web-components/interaction/form/field-message.ts +55 -0
  228. package/src/web-components/layout/BdsGrid.mdx +1 -1
  229. package/src/web-components/layout/BdsGrid.stories.tsx +1 -1
  230. package/src/web-components/layout/BdsGridItem.mdx +1 -1
  231. package/src/web-components/layout/BdsGridItem.stories.tsx +1 -1
  232. package/src/web-components/ui/BdsAlert.mdx +19 -1
  233. package/src/web-components/ui/BdsAlert.stories.tsx +29 -3
  234. package/src/web-components/ui/BdsAvatar.mdx +20 -3
  235. package/src/web-components/ui/BdsAvatar.stories.tsx +25 -2
  236. package/src/web-components/ui/BdsBadge.mdx +1 -1
  237. package/src/web-components/ui/BdsBadge.stories.tsx +1 -1
  238. package/src/web-components/ui/BdsBreadcrumb.mdx +1 -1
  239. package/src/web-components/ui/BdsBreadcrumb.stories.tsx +1 -1
  240. package/src/web-components/ui/BdsButtonContainer.mdx +1 -1
  241. package/src/web-components/ui/BdsButtonContainer.stories.tsx +1 -1
  242. package/src/web-components/ui/BdsCalendar.mdx +1 -1
  243. package/src/web-components/ui/BdsCalendar.stories.tsx +1 -1
  244. package/src/web-components/ui/BdsCalendarRange.mdx +1 -1
  245. package/src/web-components/ui/BdsCalendarRange.stories.tsx +1 -1
  246. package/src/web-components/ui/BdsCard.mdx +1 -1
  247. package/src/web-components/ui/BdsCard.stories.tsx +1 -1
  248. package/src/web-components/ui/BdsCarousel.mdx +1 -1
  249. package/src/web-components/ui/BdsCarousel.stories.tsx +1 -1
  250. package/src/web-components/ui/BdsDescriptionList.mdx +1 -1
  251. package/src/web-components/ui/BdsDescriptionList.stories.tsx +1 -1
  252. package/src/web-components/ui/BdsIconWrapper.mdx +1 -1
  253. package/src/web-components/ui/BdsIconWrapper.stories.tsx +1 -1
  254. package/src/web-components/ui/BdsLink.mdx +1 -1
  255. package/src/web-components/ui/BdsLink.stories.tsx +1 -1
  256. package/src/web-components/ui/BdsLoading.mdx +1 -1
  257. package/src/web-components/ui/BdsLoading.stories.tsx +1 -1
  258. package/src/web-components/ui/BdsNotificationBanner.mdx +10 -1
  259. package/src/web-components/ui/BdsNotificationBanner.stories.tsx +29 -2
  260. package/src/web-components/ui/BdsPagination.mdx +1 -1
  261. package/src/web-components/ui/BdsPagination.stories.tsx +1 -1
  262. package/src/web-components/ui/BdsProgress.mdx +1 -1
  263. package/src/web-components/ui/BdsProgress.stories.tsx +1 -1
  264. package/src/web-components/ui/BdsProgressCircle.mdx +1 -1
  265. package/src/web-components/ui/BdsProgressCircle.stories.tsx +1 -1
  266. package/src/web-components/ui/BdsRating.mdx +1 -1
  267. package/src/web-components/ui/BdsRating.stories.tsx +1 -1
  268. package/src/web-components/ui/BdsSectionHeader.mdx +18 -6
  269. package/src/web-components/ui/BdsSectionHeader.stories.tsx +10 -2
  270. package/src/web-components/ui/BdsSeparator.mdx +2 -1
  271. package/src/web-components/ui/BdsSeparator.stories.tsx +13 -1
  272. package/src/web-components/ui/BdsSkeleton.mdx +1 -1
  273. package/src/web-components/ui/BdsSkeleton.stories.tsx +1 -1
  274. package/src/web-components/ui/BdsTable.mdx +5 -1
  275. package/src/web-components/ui/BdsTable.stories.tsx +1 -1
  276. package/src/web-components/ui/BdsTypography.mdx +1 -1
  277. package/src/web-components/ui/BdsTypography.stories.tsx +1 -1
  278. package/src/web-components/ui/bds-alert.spec.ts +27 -0
  279. package/src/web-components/ui/bds-alert.ts +34 -1
  280. package/src/web-components/ui/bds-avatar.spec.ts +47 -0
  281. package/src/web-components/ui/bds-avatar.ts +36 -3
  282. package/src/web-components/ui/bds-notification-banner.spec.ts +45 -0
  283. package/src/web-components/ui/bds-notification-banner.ts +53 -1
  284. package/src/web-components/ui/bds-section-header.spec.ts +36 -3
  285. package/src/web-components/ui/bds-section-header.ts +32 -10
  286. package/src/web-components/ui/bds-separator.spec.ts +8 -0
  287. package/src/web-components/ui/bds-separator.ts +7 -0
  288. package/src/web-components/ui/bds-table.spec.ts +11 -0
  289. package/src/web-components/ui/bds-table.ts +5 -5
  290. package/dist/web-components/chunk-QZAIRJ75.js +0 -111
@@ -1,4 +1,5 @@
1
1
  import { LitElement, css, html, nothing } from 'lit';
2
+ import { fieldMessageStyles, renderFieldMessage } from './field-message';
2
3
 
3
4
  /**
4
5
  * `<bds-checkbox-group>` — groups related checkboxes with a shared label.
@@ -96,19 +97,10 @@ export class BdsCheckboxGroup extends LitElement {
96
97
  gap: var(--bds-space_xs);
97
98
  }
98
99
 
100
+ ${fieldMessageStyles}
101
+
99
102
  .message {
100
103
  margin-block-start: var(--bds-space_xs);
101
- font-size: var(--bds-font_size--body--s);
102
- font-family: var(--bds-font_family--body);
103
- }
104
-
105
- .message--error {
106
- color: var(--bds-color_error_on-bg);
107
- }
108
-
109
- .message--hint {
110
- color: var(--bds-color_on-bg--subtle);
111
- font-style: italic;
112
104
  }
113
105
  `;
114
106
 
@@ -156,16 +148,40 @@ export class BdsCheckboxGroup extends LitElement {
156
148
  });
157
149
  };
158
150
 
151
+ /**
152
+ * Propagates the group's `error` to slotted `<bds-checkbox>` elements so they
153
+ * show the error ring. A checkbox individually marked `error` in markup keeps it.
154
+ */
155
+ private _propagateError = () => {
156
+ this.querySelectorAll('bds-checkbox').forEach(checkbox => {
157
+ const el = checkbox as HTMLElement;
158
+ if (this.error) {
159
+ if (!el.hasAttribute('error')) {
160
+ el.setAttribute('data-bds-group-error', '');
161
+ }
162
+ el.setAttribute('error', '');
163
+ } else if (el.hasAttribute('data-bds-group-error')) {
164
+ el.removeAttribute('error');
165
+ el.removeAttribute('data-bds-group-error');
166
+ }
167
+ });
168
+ };
169
+
170
+ private _propagateToCheckboxes = () => {
171
+ this._propagateDisabled();
172
+ this._propagateError();
173
+ };
174
+
159
175
  connectedCallback() {
160
176
  super.connectedCallback();
161
177
  // Propagate after slot content has been parsed
162
- Promise.resolve().then(this._propagateDisabled);
178
+ Promise.resolve().then(this._propagateToCheckboxes);
163
179
  }
164
180
 
165
181
  updated(changed: Map<string, unknown>) {
166
182
  super.updated(changed);
167
- if (changed.has('disabled')) {
168
- this._propagateDisabled();
183
+ if (changed.has('disabled') || changed.has('error')) {
184
+ this._propagateToCheckboxes();
169
185
  }
170
186
  }
171
187
 
@@ -183,12 +199,12 @@ export class BdsCheckboxGroup extends LitElement {
183
199
  : ''}
184
200
  </legend>
185
201
  <div class="items">
186
- <slot @slotchange=${this._propagateDisabled}></slot>
202
+ <slot @slotchange=${this._propagateToCheckboxes}></slot>
187
203
  </div>
188
204
  ${this.error
189
- ? html`<p id=${errorId} class="message message--error" role="alert">${this.error}</p>`
205
+ ? renderFieldMessage('error', errorId, this.error)
190
206
  : this.hint
191
- ? html`<p id=${hintId} class="message message--hint">${this.hint}</p>`
207
+ ? renderFieldMessage('hint', hintId, this.hint)
192
208
  : ''}
193
209
  </fieldset>
194
210
  `;
@@ -103,6 +103,7 @@ export class BdsCheckbox extends LitElement {
103
103
 
104
104
  :host([error]) .input {
105
105
  --bdc_color: var(--bdc_color--error);
106
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
106
107
  }
107
108
 
108
109
  label {
@@ -1,4 +1,5 @@
1
1
  import { LitElement, css, html, nothing } from 'lit';
2
+ import { fieldMessageStyles, renderFieldMessage } from './field-message';
2
3
 
3
4
  export interface ComboboxOption {
4
5
  value: string;
@@ -99,10 +100,12 @@ export class BdsCombobox extends LitElement {
99
100
 
100
101
  :host([error]) .input {
101
102
  --bdc_color: var(--bdc_color--error);
103
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
102
104
  }
103
105
 
104
106
  :host([error]) .input:focus {
105
107
  --bdc_color: var(--bdc_color--error);
108
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
106
109
  }
107
110
 
108
111
  .chevron {
@@ -176,20 +179,7 @@ export class BdsCombobox extends LitElement {
176
179
  color: var(--bds-color_on-bg--subtle);
177
180
  }
178
181
 
179
- .message {
180
- margin-block-start: var(--bds-space_xxs);
181
- font-size: var(--bds-font_size--body--s);
182
- font-family: var(--bds-font_family--body);
183
- }
184
-
185
- .message--error {
186
- color: var(--bds-color_error);
187
- }
188
-
189
- .message--hint {
190
- color: var(--bds-color_on-bg--subtle);
191
- font-style: italic;
192
- }
182
+ ${fieldMessageStyles}
193
183
  `;
194
184
 
195
185
  static properties = {
@@ -402,9 +392,9 @@ export class BdsCombobox extends LitElement {
402
392
  ` : ''}
403
393
  </div>
404
394
  ${this.error
405
- ? html`<p id=${errorId} class="message message--error" role="alert">${this.error}</p>`
395
+ ? renderFieldMessage('error', errorId, this.error)
406
396
  : this.hint
407
- ? html`<p id=${hintId} class="message message--hint">${this.hint}</p>`
397
+ ? renderFieldMessage('hint', hintId, this.hint)
408
398
  : ''}
409
399
  `;
410
400
  }
@@ -1,5 +1,6 @@
1
1
  import { LitElement, css, html } from 'lit';
2
2
  import { ifDefined } from 'lit/directives/if-defined.js';
3
+ import { fieldMessageStyles, renderFieldMessage } from './field-message';
3
4
 
4
5
  /**
5
6
  * `<bds-form-input>` — form-associated text input with label, hint, and error support.
@@ -83,6 +84,7 @@ export class BdsFormInput extends LitElement {
83
84
 
84
85
  :host([error]) .wrapper {
85
86
  --bdc_color: var(--bdc_color--error);
87
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
86
88
  }
87
89
 
88
90
  .wrapper:focus-within {
@@ -91,6 +93,7 @@ export class BdsFormInput extends LitElement {
91
93
 
92
94
  :host([error]) .wrapper:focus-within {
93
95
  --bdc_color: var(--bdc_color--error);
96
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
94
97
  }
95
98
 
96
99
  .input {
@@ -140,20 +143,7 @@ export class BdsFormInput extends LitElement {
140
143
  display: none;
141
144
  }
142
145
 
143
- .message {
144
- margin-block-start: var(--bds-space_xxs);
145
- font-size: var(--bds-font_size--body--s);
146
- font-family: var(--bds-font_family--body);
147
- }
148
-
149
- .message--error {
150
- color: var(--bds-color_error);
151
- }
152
-
153
- .message--hint {
154
- color: var(--bds-color_on-bg--subtle);
155
- font-style: italic;
156
- }
146
+ ${fieldMessageStyles}
157
147
  `;
158
148
 
159
149
  static properties = {
@@ -265,8 +255,8 @@ export class BdsFormInput extends LitElement {
265
255
  />
266
256
  <span class="suffix"><slot name="suffix"></slot></span>
267
257
  </div>
268
- ${this.error ? html`<p id=${errorId} class="message message--error" role="alert">${this.error}</p>` : ''}
269
- ${this.hint ? html`<p id=${hintId} class="message message--hint">${this.hint}</p>` : ''}
258
+ ${this.error ? renderFieldMessage('error', errorId, this.error) : ''}
259
+ ${this.hint ? renderFieldMessage('hint', hintId, this.hint) : ''}
270
260
  `;
271
261
  }
272
262
  }
@@ -1,4 +1,5 @@
1
1
  import { LitElement, css, html } from 'lit';
2
+ import { fieldMessageStyles, renderFieldMessage } from './field-message';
2
3
 
3
4
  /**
4
5
  * `<bds-number-input>` — form-associated Number Input with stepper buttons.
@@ -72,6 +73,7 @@ export class BdsNumberInput extends LitElement {
72
73
 
73
74
  :host([error]) .row {
74
75
  --bdc_color: var(--bdc_color--error);
76
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
75
77
  }
76
78
 
77
79
  .row:focus-within {
@@ -80,6 +82,7 @@ export class BdsNumberInput extends LitElement {
80
82
 
81
83
  :host([error]) .row:focus-within {
82
84
  --bdc_color: var(--bdc_color--error);
85
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
83
86
  }
84
87
 
85
88
  .row:focus-within:has(:focus-visible) {
@@ -154,20 +157,7 @@ export class BdsNumberInput extends LitElement {
154
157
  color: var(--numberInput_stepper-color, var(--bds-color_interactive));
155
158
  }
156
159
 
157
- .message {
158
- margin-block-start: var(--bds-space_xxs);
159
- font-size: var(--bds-font_size--body--s);
160
- font-family: var(--bds-font_family--body);
161
- }
162
-
163
- .message--error {
164
- color: var(--bds-color_error);
165
- }
166
-
167
- .message--hint {
168
- color: var(--bds-color_on-bg--subtle);
169
- font-style: italic;
170
- }
160
+ ${fieldMessageStyles}
171
161
  `;
172
162
 
173
163
  static properties = {
@@ -292,9 +282,9 @@ export class BdsNumberInput extends LitElement {
292
282
  </button>
293
283
  </div>
294
284
  ${this.error
295
- ? html`<p id=${errorId} class="message message--error" role="alert">${this.error}</p>`
285
+ ? renderFieldMessage('error', errorId, this.error)
296
286
  : this.hint
297
- ? html`<p id=${hintId} class="message message--hint">${this.hint}</p>`
287
+ ? renderFieldMessage('hint', hintId, this.hint)
298
288
  : ''}
299
289
  `;
300
290
  }
@@ -112,8 +112,37 @@ describe('bds-radio-group', () => {
112
112
  });
113
113
 
114
114
  it('uses medium font-weight token for the legend (not semibold)', () => {
115
- const styles = BdsRadioGroup.styles.toString();
116
- expect(styles).toContain('--bds-font_weight--medium');
117
- expect(styles).not.toContain('--bds-font_weight--semibold');
115
+ const legendRule = BdsRadioGroup.styles.toString().match(/\.legend \{[^}]*\}/)?.[0] ?? '';
116
+ expect(legendRule).toContain('--bds-font_weight--medium');
117
+ expect(legendRule).not.toContain('--bds-font_weight--semibold');
118
+ });
119
+
120
+ it('shows the error icon in the message', async () => {
121
+ const el = await fixture('<bds-radio-group label="Test" error="Required field"></bds-radio-group>');
122
+ const message = el.shadowRoot!.querySelector('.message--error')!;
123
+ expect(message.querySelector('svg')?.getAttribute('aria-hidden')).toBe('true');
124
+ expect(message.textContent).toContain('Required field');
125
+ cleanup(el);
126
+ });
127
+
128
+ it('propagates error to slotted bds-radio elements', async () => {
129
+ const el = await fixture(
130
+ '<bds-radio-group label="Test" error="Required field"><bds-radio value="a">A</bds-radio><bds-radio value="b">B</bds-radio></bds-radio-group>',
131
+ );
132
+ const children = Array.from(el.querySelectorAll('bds-radio'));
133
+ expect(children.every(child => child.hasAttribute('error'))).toBe(true);
134
+ cleanup(el);
135
+ });
136
+
137
+ it('removes propagated error when the group error clears, keeping an individual error', async () => {
138
+ const el = await fixture(
139
+ '<bds-radio-group label="Test" error="Required field"><bds-radio value="a">A</bds-radio><bds-radio value="b" error>B</bds-radio></bds-radio-group>',
140
+ ) as BdsRadioGroup;
141
+ el.error = undefined;
142
+ await el.updateComplete;
143
+ const [first, second] = Array.from(el.querySelectorAll('bds-radio'));
144
+ expect(first.hasAttribute('error')).toBe(false);
145
+ expect(second.hasAttribute('error')).toBe(true);
146
+ cleanup(el);
118
147
  });
119
148
  });
@@ -1,4 +1,5 @@
1
1
  import { LitElement, css, html, nothing } from 'lit';
2
+ import { fieldMessageStyles, renderFieldMessage } from './field-message';
2
3
 
3
4
  /**
4
5
  * `<bds-radio-group>` — groups related radio buttons with a shared label and name.
@@ -67,19 +68,10 @@ export class BdsRadioGroup extends LitElement {
67
68
  gap: var(--bds-space_xs);
68
69
  }
69
70
 
71
+ ${fieldMessageStyles}
72
+
70
73
  .message {
71
74
  margin-block-start: var(--bds-space_xs);
72
- font-size: var(--bds-font_size--body--s);
73
- font-family: var(--bds-font_family--body);
74
- }
75
-
76
- .message--error {
77
- color: var(--bds-color_error);
78
- }
79
-
80
- .message--hint {
81
- color: var(--bds-color_on-bg--subtle);
82
- font-style: italic;
83
75
  }
84
76
  `;
85
77
 
@@ -136,9 +128,29 @@ export class BdsRadioGroup extends LitElement {
136
128
  });
137
129
  };
138
130
 
131
+ /**
132
+ * Propagates the group's `error` to slotted `<bds-radio>` elements so they show
133
+ * the error ring. A radio individually marked `error` in markup keeps it.
134
+ */
135
+ private _propagateError = () => {
136
+ this.querySelectorAll('bds-radio').forEach(radio => {
137
+ const el = radio as HTMLElement;
138
+ if (this.error) {
139
+ if (!el.hasAttribute('error')) {
140
+ el.setAttribute('data-bds-group-error', '');
141
+ }
142
+ el.setAttribute('error', '');
143
+ } else if (el.hasAttribute('data-bds-group-error')) {
144
+ el.removeAttribute('error');
145
+ el.removeAttribute('data-bds-group-error');
146
+ }
147
+ });
148
+ };
149
+
139
150
  private _propagateToRadios = () => {
140
151
  this._propagateName();
141
152
  this._propagateDisabled();
153
+ this._propagateError();
142
154
  };
143
155
 
144
156
  connectedCallback() {
@@ -149,7 +161,7 @@ export class BdsRadioGroup extends LitElement {
149
161
 
150
162
  updated(changed: Map<string, unknown>) {
151
163
  super.updated(changed);
152
- if (changed.has('name') || changed.has('disabled')) {
164
+ if (changed.has('name') || changed.has('disabled') || changed.has('error')) {
153
165
  this._propagateToRadios();
154
166
  }
155
167
  }
@@ -173,9 +185,9 @@ export class BdsRadioGroup extends LitElement {
173
185
  <slot @slotchange=${this._propagateToRadios}></slot>
174
186
  </div>
175
187
  ${this.error
176
- ? html`<p id=${errorId} class="message message--error" role="alert">${this.error}</p>`
188
+ ? renderFieldMessage('error', errorId, this.error)
177
189
  : this.hint
178
- ? html`<p id=${hintId} class="message message--hint">${this.hint}</p>`
190
+ ? renderFieldMessage('hint', hintId, this.hint)
179
191
  : ''}
180
192
  </fieldset>
181
193
  `;
@@ -103,6 +103,7 @@ export class BdsRadio extends LitElement {
103
103
 
104
104
  :host([error]) .input {
105
105
  --bdc_color: var(--bdc_color--error);
106
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
106
107
  }
107
108
 
108
109
  .textWrapper {
@@ -1,4 +1,5 @@
1
1
  import { LitElement, css, html, nothing } from 'lit';
2
+ import { fieldMessageStyles, renderFieldMessage } from './field-message';
2
3
 
3
4
  export interface SelectOption {
4
5
  value: string;
@@ -110,22 +111,9 @@ export class BdsSelect extends LitElement {
110
111
 
111
112
  :host([error]) .select {
112
113
  --bdc_color: var(--bdc_color--error);
114
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
113
115
  }
114
116
 
115
- .message {
116
- margin-block-start: var(--bds-space_xxs);
117
- font-size: var(--bds-font_size--body--s);
118
- font-family: var(--bds-font_family--body);
119
- }
120
-
121
- .message--error {
122
- color: var(--bds-color_error);
123
- }
124
-
125
- .message--hint {
126
- color: var(--bds-color_on-bg--subtle);
127
- font-style: italic;
128
- }
129
117
 
130
118
  .chevron {
131
119
  position: absolute;
@@ -142,6 +130,8 @@ export class BdsSelect extends LitElement {
142
130
  inline-size: 100%;
143
131
  block-size: 100%;
144
132
  }
133
+
134
+ ${fieldMessageStyles}
145
135
  `;
146
136
 
147
137
  static properties = {
@@ -259,9 +249,9 @@ export class BdsSelect extends LitElement {
259
249
  </span>
260
250
  </div>
261
251
  ${this.error
262
- ? html`<p id=${errorId} class="message message--error" role="alert">${this.error}</p>`
252
+ ? renderFieldMessage('error', errorId, this.error)
263
253
  : this.hint
264
- ? html`<p id=${hintId} class="message message--hint">${this.hint}</p>`
254
+ ? renderFieldMessage('hint', hintId, this.hint)
265
255
  : ''}
266
256
  `;
267
257
  }
@@ -1,4 +1,5 @@
1
1
  import { LitElement, css, html } from 'lit';
2
+ import { fieldMessageStyles, renderFieldMessage } from './field-message';
2
3
 
3
4
  /**
4
5
  * `<bds-slider>` — form-associated Slider / Range Input custom element.
@@ -147,20 +148,6 @@ export class BdsSlider extends LitElement {
147
148
  border-radius: var(--slider_track-radius, var(--bds-border_radius--full));
148
149
  }
149
150
 
150
- .message {
151
- margin-block-start: var(--bds-space_xxs);
152
- font-size: var(--bds-font_size--body--s);
153
- font-family: var(--bds-font_family--body);
154
- }
155
-
156
- .message--error {
157
- color: var(--bds-color_error);
158
- }
159
-
160
- .message--hint {
161
- color: var(--bds-color_on-bg--subtle);
162
- font-style: italic;
163
- }
164
151
 
165
152
  @media (prefers-reduced-motion: reduce) {
166
153
  .input,
@@ -169,6 +156,8 @@ export class BdsSlider extends LitElement {
169
156
  transition: none;
170
157
  }
171
158
  }
159
+
160
+ ${fieldMessageStyles}
172
161
  `;
173
162
 
174
163
  static properties = {
@@ -293,9 +282,9 @@ export class BdsSlider extends LitElement {
293
282
  />
294
283
  </div>
295
284
  ${this.error
296
- ? html`<p id=${errorId} class="message message--error" role="alert">${this.error}</p>`
285
+ ? renderFieldMessage('error', errorId, this.error)
297
286
  : this.hint
298
- ? html`<p id=${hintId} class="message message--hint">${this.hint}</p>`
287
+ ? renderFieldMessage('hint', hintId, this.hint)
299
288
  : ''}
300
289
  `;
301
290
  }
@@ -142,6 +142,7 @@ export class BdsSwitch extends LitElement {
142
142
  switch still shows the active colour even when it also has an error. */
143
143
  :host([error]) .track {
144
144
  --bdc_color: var(--switch_border-color--error, var(--bds-color_error));
145
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
145
146
  }
146
147
 
147
148
  .input:checked + .track {
@@ -1,4 +1,5 @@
1
1
  import { LitElement, css, html } from 'lit';
2
+ import { fieldMessageStyles, renderFieldMessage } from './field-message';
2
3
 
3
4
  /**
4
5
  * `<bds-textarea>` — form-associated Textarea custom element.
@@ -99,26 +100,15 @@ export class BdsTextarea extends LitElement {
99
100
 
100
101
  :host([error]) .textarea {
101
102
  --bdc_color: var(--bdc_color--error);
103
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
102
104
  }
103
105
 
104
106
  :host([error]) .textarea:focus {
105
107
  --bdc_color: var(--bdc_color--error);
108
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
106
109
  }
107
110
 
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
- }
111
+ ${fieldMessageStyles}
122
112
  `;
123
113
 
124
114
  static properties = {
@@ -225,9 +215,9 @@ export class BdsTextarea extends LitElement {
225
215
  @change=${this._handleChange}
226
216
  ></textarea>
227
217
  ${this.error
228
- ? html`<p id=${errorId} class="message message--error" role="alert">${this.error}</p>`
218
+ ? renderFieldMessage('error', errorId, this.error)
229
219
  : this.hint
230
- ? html`<p id=${hintId} class="message message--hint">${this.hint}</p>`
220
+ ? renderFieldMessage('hint', hintId, this.hint)
231
221
  : ''}
232
222
  `;
233
223
  }
@@ -0,0 +1,62 @@
1
+ import { render } from 'lit';
2
+ import { fieldMessageStyles, renderFieldMessage } from './field-message';
3
+ import { BdsFormInput } from './bds-form-input';
4
+ import { BdsTextarea } from './bds-textarea';
5
+ import { BdsSelect } from './bds-select';
6
+ import { BdsCombobox } from './bds-combobox';
7
+ import { BdsNumberInput } from './bds-number-input';
8
+ import { BdsCheckbox } from './bds-checkbox';
9
+ import { BdsRadio } from './bds-radio';
10
+ import { BdsSwitch } from './bds-switch';
11
+
12
+ const renderInto = (template: ReturnType<typeof renderFieldMessage>): HTMLElement => {
13
+ const host = document.createElement('div');
14
+ render(template, host);
15
+ return host;
16
+ };
17
+
18
+ describe('field-message', () => {
19
+ it('renders the error with role alert, the id and an aria-hidden icon', () => {
20
+ const host = renderInto(renderFieldMessage('error', 'err-1', 'Invalid email'));
21
+ const message = host.querySelector('p')!;
22
+ expect(message.id).toBe('err-1');
23
+ expect(message.getAttribute('role')).toBe('alert');
24
+ expect(message.classList.contains('message--error')).toBe(true);
25
+ expect(message.querySelector('svg')?.getAttribute('aria-hidden')).toBe('true');
26
+ expect(message.textContent).toContain('Invalid email');
27
+ });
28
+
29
+ it('renders the hint without role and without icon', () => {
30
+ const host = renderInto(renderFieldMessage('hint', 'hint-1', 'We never share it'));
31
+ const message = host.querySelector('p')!;
32
+ expect(message.id).toBe('hint-1');
33
+ expect(message.hasAttribute('role')).toBe(false);
34
+ expect(message.querySelector('svg')).toBeNull();
35
+ });
36
+
37
+ it('reads every message look from a --formMessage_* hook', () => {
38
+ const css = fieldMessageStyles.toString();
39
+ [
40
+ '--formMessage_gap',
41
+ '--formMessage_color--error',
42
+ '--formMessage_font-weight--error',
43
+ '--formMessage_icon-size',
44
+ '--formMessage_color--hint',
45
+ '--formMessage_font-style--hint',
46
+ '--formMessage_font-size',
47
+ ].forEach(hook => expect(css).toContain(hook));
48
+ });
49
+
50
+ it.each([
51
+ ['bds-form-input', BdsFormInput],
52
+ ['bds-textarea', BdsTextarea],
53
+ ['bds-select', BdsSelect],
54
+ ['bds-combobox', BdsCombobox],
55
+ ['bds-number-input', BdsNumberInput],
56
+ ['bds-checkbox', BdsCheckbox],
57
+ ['bds-radio', BdsRadio],
58
+ ['bds-switch', BdsSwitch],
59
+ ])('%s widens the outline in the error state', (_tag, ctor) => {
60
+ expect(ctor.styles.toString()).toContain('--bdc-outline_width: var(--bdc-outline_width--error, 2px)');
61
+ });
62
+ });
@@ -0,0 +1,55 @@
1
+ import { css, html, svg } from 'lit';
2
+ import type { TemplateResult } from 'lit';
3
+
4
+ /**
5
+ * Shared look and markup for the error and hint message under a form control.
6
+ * Every `bds-*` form control spreads `fieldMessageStyles` into its own styles and
7
+ * renders its message with `renderFieldMessage()`, so the controls cannot drift.
8
+ *
9
+ * Hooks: --formMessage_gap, --formMessage_color--error, --formMessage_font-weight--error,
10
+ * --formMessage_icon-size, --formMessage_color--hint, --formMessage_font-style--hint,
11
+ * --formMessage_font-size.
12
+ */
13
+ export const fieldMessageStyles = css`
14
+ .message {
15
+ margin-block-start: var(--bds-space_xxs);
16
+ font-size: var(--formMessage_font-size, var(--bds-font_size--body--s));
17
+ font-family: var(--bds-font_family--body);
18
+ }
19
+
20
+ .message--error {
21
+ display: flex;
22
+ align-items: flex-start;
23
+ gap: var(--formMessage_gap, var(--bds-space_xxs));
24
+ color: var(--formMessage_color--error, var(--bds-color_error_on-bg));
25
+ font-weight: var(--formMessage_font-weight--error, var(--bds-font_weight--semibold));
26
+ }
27
+
28
+ .message__icon {
29
+ flex-shrink: 0;
30
+ inline-size: var(--formMessage_icon-size, 1.15em);
31
+ block-size: var(--formMessage_icon-size, 1.15em);
32
+ }
33
+
34
+ .message--hint {
35
+ color: var(--formMessage_color--hint, inherit);
36
+ font-style: var(--formMessage_font-style--hint, italic);
37
+ }
38
+ `;
39
+
40
+ // Phosphor "warning-circle" (fill)
41
+ const warningIcon = svg`<svg class="message__icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-8,56a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm8,104a12,12,0,1,1,12-12A12,12,0,0,1,128,184Z"></path></svg>`;
42
+
43
+ /**
44
+ * Renders the message paragraph. The error variant gets `role="alert"` and a
45
+ * leading icon; the hint variant is plain text. `id` is the one the control
46
+ * references from `aria-describedby`.
47
+ */
48
+ export const renderFieldMessage = (
49
+ kind: 'error' | 'hint',
50
+ id: string,
51
+ text: string,
52
+ ): TemplateResult =>
53
+ kind === 'error'
54
+ ? html`<p id=${id} class="message message--error" role="alert">${warningIcon}${text}</p>`
55
+ : html`<p id=${id} class="message message--hint">${text}</p>`;
@@ -7,7 +7,7 @@ import * as Stories from './BdsGrid.stories';
7
7
 
8
8
  Framework-agnostic Grid layout custom element. Built on the `@boostdev/design-system-foundation` grid tokens.
9
9
 
10
- > **Status: experimental** — API may change before stable release.
10
+ > **Status: alpha** — API may change before stable release.
11
11
 
12
12
  ## Installation
13
13