@boostdev/design-system-components 2.15.0 → 2.17.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 (299) hide show
  1. package/AGENTS.md +22 -15
  2. package/dist/client.cjs +271 -205
  3. package/dist/client.css +813 -650
  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 +813 -650
  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-3WAV6MDN.js → chunk-3FSGP35V.js} +1 -0
  14. package/dist/web-components/{chunk-SYNLFKD3.js → chunk-3LQKFSMR.js} +6 -0
  15. package/dist/web-components/{chunk-KA3VPKMY.js → chunk-4MB2ICSK.js} +1 -0
  16. package/dist/web-components/{chunk-6G22I2CK.js → chunk-5ATTKUCP.js} +33 -3
  17. package/dist/web-components/{chunk-IDSM74LM.js → chunk-6XL5P76D.js} +3 -3
  18. package/dist/web-components/{chunk-P24JYC5V.js → chunk-7VXRMCAL.js} +10 -4
  19. package/dist/web-components/{chunk-CUKQ2L5B.js → chunk-AGI6YQRC.js} +9 -16
  20. package/dist/web-components/chunk-BDRV452N.js +51 -0
  21. package/dist/web-components/{chunk-3AY2YKCM.js → chunk-CKCPV347.js} +1 -0
  22. package/dist/web-components/{chunk-HBLJ4SLY.js → chunk-DD3OUXNG.js} +5 -5
  23. package/dist/web-components/{chunk-RVF7W653.js → chunk-DGOCPKHI.js} +30 -10
  24. package/dist/web-components/{chunk-2OLVQUBU.js → chunk-EYCW7TKB.js} +8 -15
  25. package/dist/web-components/{chunk-VANAKB6D.js → chunk-GPJWLAKU.js} +33 -16
  26. package/dist/web-components/{chunk-VED2DFJ6.js → chunk-GQNQBEYR.js} +8 -15
  27. package/dist/web-components/{chunk-WFVKU4GX.js → chunk-IKAJLROF.js} +49 -4
  28. package/dist/web-components/{chunk-RSZ3BWBK.js → chunk-KSK4RZZQ.js} +30 -7
  29. package/dist/web-components/{chunk-FEDABOR5.js → chunk-LHFUXZ52.js} +7 -15
  30. package/dist/web-components/{chunk-M3ECULRH.js → chunk-RBF7WPLI.js} +10 -8
  31. package/dist/web-components/{chunk-BPYKZVDK.js → chunk-SJLMW2VR.js} +2 -2
  32. package/dist/web-components/chunk-SRKQ6GST.js +40 -0
  33. package/dist/web-components/{chunk-5HUAFCU6.js → chunk-TJV7UIXC.js} +7 -0
  34. package/dist/web-components/{chunk-LDIPCTRN.js → chunk-WWWJCSXN.js} +50 -4
  35. package/dist/web-components/{chunk-J3HVUIUH.js → chunk-XI2U6U26.js} +8 -15
  36. package/dist/web-components/{chunk-JH6LQKLK.js → chunk-XJU7KITO.js} +8 -15
  37. package/dist/web-components/{chunk-JV5QT2OU.js → chunk-YLDQOESI.js} +27 -13
  38. package/dist/web-components/globals.js +1 -1
  39. package/dist/web-components/index.d.ts +1 -0
  40. package/dist/web-components/index.js +35 -30
  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-badge.js +1 -1
  68. package/dist/web-components/ui/bds-notification-banner.d.ts +14 -1
  69. package/dist/web-components/ui/bds-notification-banner.js +1 -1
  70. package/dist/web-components/ui/bds-rating.js +1 -1
  71. package/dist/web-components/ui/bds-section-header.d.ts +6 -0
  72. package/dist/web-components/ui/bds-section-header.js +1 -1
  73. package/dist/web-components/ui/bds-separator.js +1 -1
  74. package/dist/web-components/ui/bds-table.js +1 -1
  75. package/package.json +6 -2
  76. package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +35 -4
  77. package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +58 -10
  78. package/src/components/interaction/DropdownMenu/DropdownMenu.spec.tsx +114 -1
  79. package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +25 -0
  80. package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +69 -17
  81. package/src/components/interaction/Rating/Rating.mdx +4 -1
  82. package/src/components/interaction/Rating/Rating.module.css +9 -0
  83. package/src/components/interaction/Rating/Rating.spec.tsx +9 -0
  84. package/src/components/interaction/form/Checkbox/Checkbox.module.css +1 -0
  85. package/src/components/interaction/form/Checkbox/Checkbox.spec.tsx +7 -0
  86. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +2 -1
  87. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css +8 -1
  88. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.spec.tsx +27 -0
  89. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +1 -1
  90. package/src/components/interaction/form/Combobox/Combobox.module.css +1 -0
  91. package/src/components/interaction/form/Combobox/Combobox.spec.tsx +7 -0
  92. package/src/components/interaction/form/FormInput/FormInput.mdx +9 -0
  93. package/src/components/interaction/form/FormInput/FormInput.module.css +1 -0
  94. package/src/components/interaction/form/FormInput/FormInput.spec.tsx +7 -0
  95. package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -0
  96. package/src/components/interaction/form/NumberInput/NumberInput.spec.tsx +7 -0
  97. package/src/components/interaction/form/Radio/Radio.module.css +1 -0
  98. package/src/components/interaction/form/Radio/Radio.spec.tsx +7 -0
  99. package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +2 -1
  100. package/src/components/interaction/form/RadioGroup/RadioGroup.module.css +8 -1
  101. package/src/components/interaction/form/RadioGroup/RadioGroup.spec.tsx +27 -0
  102. package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +1 -1
  103. package/src/components/interaction/form/Select/Select.module.css +1 -0
  104. package/src/components/interaction/form/Select/Select.spec.tsx +7 -0
  105. package/src/components/interaction/form/Switch/Switch.module.css +1 -0
  106. package/src/components/interaction/form/Switch/Switch.spec.tsx +7 -0
  107. package/src/components/interaction/form/Textarea/Textarea.module.css +1 -0
  108. package/src/components/interaction/form/Textarea/Textarea.spec.tsx +7 -0
  109. package/src/components/interaction/form/atoms/Message.module.css +14 -3
  110. package/src/components/interaction/form/atoms/Message.spec.tsx +22 -0
  111. package/src/components/interaction/form/atoms/Message.tsx +5 -0
  112. package/src/components/layout/SectionHeader/SectionHeader.mdx +18 -5
  113. package/src/components/layout/SectionHeader/SectionHeader.module.css +35 -14
  114. package/src/components/layout/SectionHeader/SectionHeader.spec.tsx +44 -0
  115. package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +5 -0
  116. package/src/components/layout/SectionHeader/SectionHeader.tsx +3 -1
  117. package/src/components/ui/Alert/Alert.mdx +12 -1
  118. package/src/components/ui/Alert/Alert.module.css +32 -3
  119. package/src/components/ui/Alert/Alert.spec.tsx +27 -0
  120. package/src/components/ui/Alert/Alert.stories.tsx +20 -0
  121. package/src/components/ui/Alert/Alert.tsx +8 -1
  122. package/src/components/ui/Avatar/Avatar.mdx +22 -2
  123. package/src/components/ui/Avatar/Avatar.module.css +29 -2
  124. package/src/components/ui/Avatar/Avatar.spec.tsx +26 -0
  125. package/src/components/ui/Avatar/Avatar.stories.tsx +23 -0
  126. package/src/components/ui/Avatar/Avatar.tsx +4 -2
  127. package/src/components/ui/Avatar/avatarTone.spec.ts +41 -0
  128. package/src/components/ui/Avatar/avatarTone.ts +22 -0
  129. package/src/components/ui/Badge/Badge.mdx +1 -1
  130. package/src/components/ui/Badge/Badge.module.css +2 -2
  131. package/src/components/ui/Badge/Badge.spec.tsx +8 -0
  132. package/src/components/ui/Collapsible/Collapsible.module.css +1 -2
  133. package/src/components/ui/NotificationBanner/NotificationBanner.mdx +27 -1
  134. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +33 -3
  135. package/src/components/ui/NotificationBanner/NotificationBanner.spec.tsx +35 -0
  136. package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +25 -0
  137. package/src/components/ui/NotificationBanner/NotificationBanner.tsx +15 -1
  138. package/src/components/ui/Separator/Separator.mdx +1 -0
  139. package/src/components/ui/Separator/Separator.module.css +6 -0
  140. package/src/components/ui/Separator/Separator.spec.tsx +19 -0
  141. package/src/components/ui/Separator/Separator.stories.tsx +15 -0
  142. package/src/components/ui/Table/Table.mdx +4 -1
  143. package/src/components/ui/Table/Table.module.css +7 -7
  144. package/src/components/ui/Table/Table.spec.tsx +11 -0
  145. package/src/components/ui/Tabs/Tabs.mdx +11 -5
  146. package/src/components/ui/Tabs/Tabs.module.css +23 -5
  147. package/src/components/ui/Tabs/Tabs.spec.tsx +39 -0
  148. package/src/components/ui/Tabs/Tabs.tsx +2 -1
  149. package/src/components/ui/Tabs/getNodeText.spec.tsx +23 -0
  150. package/src/components/ui/Tabs/getNodeText.ts +13 -0
  151. package/src/components/ui/Tooltip/Tooltip.mdx +6 -3
  152. package/src/components/ui/Tooltip/Tooltip.module.css +6 -2
  153. package/src/components/ui/Tooltip/Tooltip.spec.tsx +10 -0
  154. package/src/components/ui/Tooltip/Tooltip.stories.tsx +13 -0
  155. package/src/css/bdc.css +5 -2
  156. package/src/css/bdc.spec.ts +65 -0
  157. package/src/stories/DesignSystem/Borders.mdx +17 -9
  158. package/src/test/token-contract.spec.ts +1 -0
  159. package/src/web-components/globals.ts +10 -4
  160. package/src/web-components/index.ts +1 -0
  161. package/src/web-components/interaction/BdsAccordion.mdx +1 -1
  162. package/src/web-components/interaction/BdsAccordion.stories.tsx +1 -1
  163. package/src/web-components/interaction/BdsButton.mdx +1 -1
  164. package/src/web-components/interaction/BdsButton.stories.tsx +1 -1
  165. package/src/web-components/interaction/BdsCollapsible.mdx +1 -1
  166. package/src/web-components/interaction/BdsCollapsible.stories.tsx +1 -1
  167. package/src/web-components/interaction/BdsDialog.mdx +1 -1
  168. package/src/web-components/interaction/BdsDialog.stories.tsx +1 -1
  169. package/src/web-components/interaction/BdsDrawer.mdx +1 -1
  170. package/src/web-components/interaction/BdsDrawer.stories.tsx +1 -1
  171. package/src/web-components/interaction/BdsDropdownMenu.mdx +62 -6
  172. package/src/web-components/interaction/BdsDropdownMenu.stories.tsx +32 -7
  173. package/src/web-components/interaction/BdsPopover.mdx +1 -1
  174. package/src/web-components/interaction/BdsPopover.stories.tsx +1 -1
  175. package/src/web-components/interaction/BdsSkipLink.mdx +1 -1
  176. package/src/web-components/interaction/BdsSkipLink.stories.tsx +1 -1
  177. package/src/web-components/interaction/BdsTabs.mdx +10 -1
  178. package/src/web-components/interaction/BdsTabs.stories.tsx +1 -1
  179. package/src/web-components/interaction/BdsToastProvider.mdx +1 -1
  180. package/src/web-components/interaction/BdsToastProvider.stories.tsx +1 -1
  181. package/src/web-components/interaction/BdsTooltip.mdx +13 -5
  182. package/src/web-components/interaction/BdsTooltip.stories.tsx +12 -12
  183. package/src/web-components/interaction/bds-dropdown-menu-group.ts +72 -0
  184. package/src/web-components/interaction/bds-dropdown-menu-item.ts +65 -17
  185. package/src/web-components/interaction/bds-dropdown-menu.spec.ts +135 -0
  186. package/src/web-components/interaction/bds-dropdown-menu.ts +5 -4
  187. package/src/web-components/interaction/bds-tab.spec.ts +34 -2
  188. package/src/web-components/interaction/bds-tab.ts +31 -6
  189. package/src/web-components/interaction/bds-tooltip.spec.ts +16 -0
  190. package/src/web-components/interaction/bds-tooltip.ts +10 -8
  191. package/src/web-components/interaction/form/BdsCheckbox.mdx +5 -1
  192. package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +1 -1
  193. package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +5 -1
  194. package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +1 -1
  195. package/src/web-components/interaction/form/BdsCombobox.mdx +5 -1
  196. package/src/web-components/interaction/form/BdsCombobox.stories.tsx +1 -1
  197. package/src/web-components/interaction/form/BdsFieldGroup.stories.tsx +1 -1
  198. package/src/web-components/interaction/form/BdsFileInput.mdx +1 -1
  199. package/src/web-components/interaction/form/BdsFileInput.stories.tsx +1 -1
  200. package/src/web-components/interaction/form/BdsFormInput.mdx +8 -1
  201. package/src/web-components/interaction/form/BdsFormInput.stories.tsx +1 -1
  202. package/src/web-components/interaction/form/BdsNumberInput.mdx +5 -1
  203. package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +1 -1
  204. package/src/web-components/interaction/form/BdsRadio.mdx +5 -1
  205. package/src/web-components/interaction/form/BdsRadio.stories.tsx +1 -1
  206. package/src/web-components/interaction/form/BdsRadioGroup.mdx +5 -1
  207. package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +1 -1
  208. package/src/web-components/interaction/form/BdsSegmentedControl.mdx +1 -1
  209. package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +1 -1
  210. package/src/web-components/interaction/form/BdsSelect.mdx +5 -1
  211. package/src/web-components/interaction/form/BdsSelect.stories.tsx +1 -1
  212. package/src/web-components/interaction/form/BdsSlider.mdx +5 -1
  213. package/src/web-components/interaction/form/BdsSlider.stories.tsx +1 -1
  214. package/src/web-components/interaction/form/BdsSwitch.mdx +5 -1
  215. package/src/web-components/interaction/form/BdsSwitch.stories.tsx +1 -1
  216. package/src/web-components/interaction/form/BdsTextarea.mdx +5 -1
  217. package/src/web-components/interaction/form/BdsTextarea.stories.tsx +1 -1
  218. package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +32 -3
  219. package/src/web-components/interaction/form/bds-checkbox-group.ts +33 -17
  220. package/src/web-components/interaction/form/bds-checkbox.ts +1 -0
  221. package/src/web-components/interaction/form/bds-combobox.ts +6 -16
  222. package/src/web-components/interaction/form/bds-form-input.ts +6 -16
  223. package/src/web-components/interaction/form/bds-number-input.ts +6 -16
  224. package/src/web-components/interaction/form/bds-radio-group.spec.ts +32 -3
  225. package/src/web-components/interaction/form/bds-radio-group.ts +26 -14
  226. package/src/web-components/interaction/form/bds-radio.ts +1 -0
  227. package/src/web-components/interaction/form/bds-select.ts +6 -16
  228. package/src/web-components/interaction/form/bds-slider.ts +5 -16
  229. package/src/web-components/interaction/form/bds-switch.ts +1 -0
  230. package/src/web-components/interaction/form/bds-textarea.ts +6 -16
  231. package/src/web-components/interaction/form/field-message.spec.ts +62 -0
  232. package/src/web-components/interaction/form/field-message.ts +55 -0
  233. package/src/web-components/layout/BdsGrid.mdx +1 -1
  234. package/src/web-components/layout/BdsGrid.stories.tsx +1 -1
  235. package/src/web-components/layout/BdsGridItem.mdx +1 -1
  236. package/src/web-components/layout/BdsGridItem.stories.tsx +1 -1
  237. package/src/web-components/ui/BdsAlert.mdx +20 -1
  238. package/src/web-components/ui/BdsAlert.stories.tsx +29 -3
  239. package/src/web-components/ui/BdsAvatar.mdx +20 -3
  240. package/src/web-components/ui/BdsAvatar.stories.tsx +25 -2
  241. package/src/web-components/ui/BdsBadge.mdx +2 -1
  242. package/src/web-components/ui/BdsBadge.stories.tsx +1 -1
  243. package/src/web-components/ui/BdsBreadcrumb.mdx +1 -1
  244. package/src/web-components/ui/BdsBreadcrumb.stories.tsx +1 -1
  245. package/src/web-components/ui/BdsButtonContainer.mdx +1 -1
  246. package/src/web-components/ui/BdsButtonContainer.stories.tsx +1 -1
  247. package/src/web-components/ui/BdsCalendar.mdx +1 -1
  248. package/src/web-components/ui/BdsCalendar.stories.tsx +1 -1
  249. package/src/web-components/ui/BdsCalendarRange.mdx +1 -1
  250. package/src/web-components/ui/BdsCalendarRange.stories.tsx +1 -1
  251. package/src/web-components/ui/BdsCard.mdx +1 -1
  252. package/src/web-components/ui/BdsCard.stories.tsx +1 -1
  253. package/src/web-components/ui/BdsCarousel.mdx +1 -1
  254. package/src/web-components/ui/BdsCarousel.stories.tsx +1 -1
  255. package/src/web-components/ui/BdsDescriptionList.mdx +1 -1
  256. package/src/web-components/ui/BdsDescriptionList.stories.tsx +1 -1
  257. package/src/web-components/ui/BdsIconWrapper.mdx +1 -1
  258. package/src/web-components/ui/BdsIconWrapper.stories.tsx +1 -1
  259. package/src/web-components/ui/BdsLink.mdx +1 -1
  260. package/src/web-components/ui/BdsLink.stories.tsx +1 -1
  261. package/src/web-components/ui/BdsLoading.mdx +1 -1
  262. package/src/web-components/ui/BdsLoading.stories.tsx +1 -1
  263. package/src/web-components/ui/BdsNotificationBanner.mdx +11 -2
  264. package/src/web-components/ui/BdsNotificationBanner.stories.tsx +29 -2
  265. package/src/web-components/ui/BdsPagination.mdx +1 -1
  266. package/src/web-components/ui/BdsPagination.stories.tsx +1 -1
  267. package/src/web-components/ui/BdsProgress.mdx +1 -1
  268. package/src/web-components/ui/BdsProgress.stories.tsx +1 -1
  269. package/src/web-components/ui/BdsProgressCircle.mdx +1 -1
  270. package/src/web-components/ui/BdsProgressCircle.stories.tsx +1 -1
  271. package/src/web-components/ui/BdsRating.mdx +5 -2
  272. package/src/web-components/ui/BdsRating.stories.tsx +1 -1
  273. package/src/web-components/ui/BdsSectionHeader.mdx +18 -6
  274. package/src/web-components/ui/BdsSectionHeader.stories.tsx +10 -2
  275. package/src/web-components/ui/BdsSeparator.mdx +2 -1
  276. package/src/web-components/ui/BdsSeparator.stories.tsx +13 -1
  277. package/src/web-components/ui/BdsSkeleton.mdx +1 -1
  278. package/src/web-components/ui/BdsSkeleton.stories.tsx +1 -1
  279. package/src/web-components/ui/BdsTable.mdx +5 -1
  280. package/src/web-components/ui/BdsTable.stories.tsx +1 -1
  281. package/src/web-components/ui/BdsTypography.mdx +1 -1
  282. package/src/web-components/ui/BdsTypography.stories.tsx +1 -1
  283. package/src/web-components/ui/bds-alert.spec.ts +34 -0
  284. package/src/web-components/ui/bds-alert.ts +36 -3
  285. package/src/web-components/ui/bds-avatar.spec.ts +47 -0
  286. package/src/web-components/ui/bds-avatar.ts +36 -3
  287. package/src/web-components/ui/bds-badge.spec.ts +6 -0
  288. package/src/web-components/ui/bds-badge.ts +2 -2
  289. package/src/web-components/ui/bds-notification-banner.spec.ts +52 -0
  290. package/src/web-components/ui/bds-notification-banner.ts +55 -3
  291. package/src/web-components/ui/bds-rating.spec.ts +7 -0
  292. package/src/web-components/ui/bds-rating.ts +6 -0
  293. package/src/web-components/ui/bds-section-header.spec.ts +36 -3
  294. package/src/web-components/ui/bds-section-header.ts +32 -10
  295. package/src/web-components/ui/bds-separator.spec.ts +8 -0
  296. package/src/web-components/ui/bds-separator.ts +7 -0
  297. package/src/web-components/ui/bds-table.spec.ts +11 -0
  298. package/src/web-components/ui/bds-table.ts +5 -5
  299. package/dist/web-components/chunk-QZAIRJ75.js +0 -111
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import { Rating } from './Rating';
3
5
 
@@ -27,4 +29,11 @@ describe('Rating', () => {
27
29
  render(<Rating value={3} className="custom" />);
28
30
  expect(screen.getByRole('img')).toHaveClass('custom');
29
31
  });
32
+
33
+ it('outlines empty stars with the as-surface mark colour and strokes filled stars in their own colour', () => {
34
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/Rating/Rating.module.css'), 'utf8');
35
+ expect(css).toMatch(/\.star \{[^}]*stroke:\s*var\(--rating_stroke-color,\s*var\(--bds-color_on-bg--as-surface,\s*var\(--bds-color_on-bg--border,\s*var\(--bds-color_on-bg\)\)\)\)/);
36
+ expect(css).toMatch(/\.star \{[^}]*stroke-width:\s*var\(--rating_stroke-width,\s*var\(--bdc-outline_width\)\)/);
37
+ expect(css).toMatch(/\.--filled \{[^}]*stroke:\s*var\(--rating_stroke-color--filled,\s*currentcolor\)/);
38
+ });
30
39
  });
@@ -65,5 +65,6 @@
65
65
 
66
66
  .checkboxError {
67
67
  --bdc_color: var(--bdc_color--error);
68
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
68
69
  }
69
70
  }
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import userEvent from '@testing-library/user-event';
3
5
  import { Checkbox } from './Checkbox';
@@ -42,4 +44,9 @@ describe('Checkbox', () => {
42
44
  const checkbox = screen.getByLabelText('Accept terms');
43
45
  expect(checkbox.id).not.toBe('consumer-supplied-id');
44
46
  });
47
+
48
+ it('widens the outline in the error state', () => {
49
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Checkbox/Checkbox.module.css'), 'utf8');
50
+ expect(css).toMatch(/\.checkboxError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
51
+ });
45
52
  });
@@ -46,7 +46,7 @@ All public override tokens. Set on the CheckboxGroup or any ancestor; they casca
46
46
  | `--checkboxGroup_items-gap` | `var(--bds-space_xs)` | Gap between the child checkboxes. |
47
47
  | `--checkboxGroup_legend-color` | `var(--bds-color_on-bg)` | Legend text colour. |
48
48
  | `--checkboxGroup_legend-font-family` | `var(--bds-font_family--body)` | Legend font family. |
49
- | `--checkboxGroup_legend-font-size` | `var(--bds-font_size--body--s)` | Legend font size. |
49
+ | `--checkboxGroup_legend-font-size` | `var(--bds-font_size--body)` | Legend font size, the same as the labels of the other fields. |
50
50
  | `--checkboxGroup_legend-font-weight` | `var(--bds-font_weight--medium)` | Legend font weight. |
51
51
  | `--checkboxGroup_legend-gap` | `var(--bds-space_xs)` | Space below the legend before the items. |
52
52
  | `--checkboxGroup_required-color` | `var(--bds-color_error)` | Colour of the visible `*` required marker. |
@@ -57,3 +57,4 @@ All public override tokens. Set on the CheckboxGroup or any ancestor; they casca
57
57
  - `disabled` on the group sets the HTML `disabled` attribute on the `<fieldset>`, which disables all child inputs
58
58
  - When `required` is true, the legend shows a visible `*` (hidden from assistive tech via `aria-hidden`) plus a visually-hidden "(required)" span, so the fieldset's accessible name resolves to `"<legend> (required)"` (e.g. `"Contact preferences (required)"`). `aria-required` is intentionally **not** set on the `<fieldset>` — it is not supported ARIA on a `role=group` element and would be ignored by assistive tech
59
59
  - Error and hint messages are linked via `aria-describedby`
60
+ - With `error`, the child controls show the error ring (`--bdc-outline_width--error`, `--bdc_color--error`) and the message gets a warning icon. Message hooks (`--formMessage_*`) are listed on FormInput
@@ -10,7 +10,7 @@
10
10
 
11
11
  .legend {
12
12
  font-family: var(--checkboxGroup_legend-font-family, var(--bds-font_family--body));
13
- font-size: var(--checkboxGroup_legend-font-size, var(--bds-font_size--body--s));
13
+ font-size: var(--checkboxGroup_legend-font-size, var(--bds-font_size--body));
14
14
  font-weight: var(--checkboxGroup_legend-font-weight, var(--bds-font_weight--medium));
15
15
  color: var(--checkboxGroup_legend-color, var(--bds-color_on-bg));
16
16
  padding: 0;
@@ -42,4 +42,11 @@
42
42
  flex-direction: column;
43
43
  gap: var(--checkboxGroup_items-gap, var(--bds-space_xs));
44
44
  }
45
+
46
+ /* Error ring on the controls inside; state rules on the control itself
47
+ (checked, focus) still win because they have higher specificity. */
48
+ .groupError input {
49
+ --bdc_color: var(--bdc_color--error);
50
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
51
+ }
45
52
  }
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import { Checkbox } from '../Checkbox';
3
5
  import { CheckboxGroup } from './CheckboxGroup';
@@ -114,4 +116,29 @@ describe('CheckboxGroup', () => {
114
116
  );
115
117
  expect(screen.getByRole('group')).toBeDisabled();
116
118
  });
119
+
120
+ it('marks the group with the error class so the controls inside show the error ring', () => {
121
+ render(
122
+ <CheckboxGroup legend="Pick" error="Required">
123
+ <Checkbox label="One" name="one" value="1" />
124
+ </CheckboxGroup>
125
+ );
126
+ expect(screen.getByRole('group', { name: 'Pick' }).className).toContain('groupError');
127
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css'), 'utf8');
128
+ expect(css).toMatch(/\.groupError input \{[^}]*--bdc_color: var\(--bdc_color--error\)[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
129
+ });
130
+
131
+ it('does not mark the group without an error', () => {
132
+ render(
133
+ <CheckboxGroup legend="Pick">
134
+ <Checkbox label="One" name="one" value="1" />
135
+ </CheckboxGroup>
136
+ );
137
+ expect(screen.getByRole('group', { name: 'Pick' }).className).not.toContain('groupError');
138
+ });
139
+
140
+ it('sizes the legend like the labels of the other fields', () => {
141
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css'), 'utf8');
142
+ expect(css).toMatch(/\.legend \{[^}]*font-size:\s*var\(--checkboxGroup_legend-font-size,\s*var\(--bds-font_size--body\)\)/);
143
+ });
117
144
  });
@@ -28,7 +28,7 @@ export function CheckboxGroup({
28
28
  return (
29
29
  <fieldset
30
30
  {...rest}
31
- className={cn(css.group, className)}
31
+ className={cn(css.group, error && css.groupError, className)}
32
32
  aria-describedby={describedBy}
33
33
  >
34
34
  <legend className={css.legend}>
@@ -45,6 +45,7 @@
45
45
 
46
46
  .inputError {
47
47
  --bdc_color: var(--bdc_color--error);
48
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
48
49
  }
49
50
 
50
51
  .chevron {
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import userEvent from '@testing-library/user-event';
3
5
  import { Combobox } from './Combobox';
@@ -125,4 +127,9 @@ describe('Combobox', () => {
125
127
  expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
126
128
  expect(combobox).not.toHaveAttribute('aria-activedescendant');
127
129
  });
130
+
131
+ it('widens the outline in the error state', () => {
132
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Combobox/Combobox.module.css'), 'utf8');
133
+ expect(css).toMatch(/\.inputError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
134
+ });
128
135
  });
@@ -43,9 +43,18 @@ Text input field with label, optional hint, and error state. The label and input
43
43
  <tr><td>`--formInput_border`</td><td>`none`</td><td>Border</td></tr>
44
44
  <tr><td>`--formInput_radius`</td><td>`var(--bds-border_radius--full)`</td><td>Corner radius</td></tr>
45
45
  <tr><td>`--formInput_shadow`</td><td>`var(--bdc-outline_shadow)`</td><td>Box shadow</td></tr>
46
+ <tr><td>`--formMessage_font-size`</td><td>`var(--bds-font_size--body--s)`</td><td>Font size of the error and hint message (shared by all form controls)</td></tr>
47
+ <tr><td>`--formMessage_gap`</td><td>`var(--bds-space_xxs)`</td><td>Gap between the error icon and the error text</td></tr>
48
+ <tr><td>`--formMessage_icon-size`</td><td>`1.15em`</td><td>Size of the error icon</td></tr>
49
+ <tr><td>`--formMessage_color--error`</td><td>`var(--bds-color_error_on-bg)`</td><td>Error message colour</td></tr>
50
+ <tr><td>`--formMessage_font-weight--error`</td><td>`var(--bds-font_weight--semibold)`</td><td>Error message weight</td></tr>
51
+ <tr><td>`--formMessage_color--hint`</td><td>`inherit`</td><td>Hint colour</td></tr>
52
+ <tr><td>`--formMessage_font-style--hint`</td><td>`italic`</td><td>Hint font style</td></tr>
46
53
  </tbody>
47
54
  </table>
48
55
 
56
+ In the error state the control draws a `--bdc-outline_width--error` (2px) ring in `--bdc_color--error`, and the message gets a warning icon.
57
+
49
58
  `--input_*` (e.g. `--input_border`, `--input_radius`, `--input_shadow`, `--input_color`, `--input_color_bg`) are deprecated aliases for the `--formInput_*` tokens above — they still work but `--formInput_*` is the documented, forward-compatible name.
50
59
 
51
60
 
@@ -42,5 +42,6 @@
42
42
 
43
43
  .inputError {
44
44
  --bdc_color: var(--bdc_color--error);
45
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
45
46
  }
46
47
  }
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import userEvent from '@testing-library/user-event';
3
5
  import { FormInput } from './FormInput';
@@ -60,4 +62,9 @@ describe('FormInput', () => {
60
62
  render(<FormInput label="Email" name="email" ariaLabel="Deprecated label" aria-label="Native label" />);
61
63
  expect(screen.getByRole('textbox')).toHaveAccessibleName('Deprecated label');
62
64
  });
65
+
66
+ it('widens the outline in the error state', () => {
67
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/FormInput/FormInput.module.css'), 'utf8');
68
+ expect(css).toMatch(/\.inputError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
69
+ });
63
70
  });
@@ -49,6 +49,7 @@
49
49
 
50
50
  .inputError {
51
51
  --bdc_color: var(--bdc_color--error);
52
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
52
53
  }
53
54
 
54
55
  .stepper {
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { useState } from 'react';
2
4
  import { render, screen } from '@testing-library/react';
3
5
  import userEvent from '@testing-library/user-event';
@@ -97,4 +99,9 @@ describe('NumberInput', () => {
97
99
  expect(input).toHaveValue(null);
98
100
  expect(onChange).not.toHaveBeenCalledWith(0);
99
101
  });
102
+
103
+ it('widens the outline in the error state', () => {
104
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/NumberInput/NumberInput.module.css'), 'utf8');
105
+ expect(css).toMatch(/\.inputError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
106
+ });
100
107
  });
@@ -78,5 +78,6 @@
78
78
 
79
79
  .radioError {
80
80
  --bdc_color: var(--bdc_color--error);
81
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
81
82
  }
82
83
  }
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import userEvent from '@testing-library/user-event';
3
5
  import { Radio } from './Radio';
@@ -50,4 +52,9 @@ describe('Radio', () => {
50
52
  const radio = screen.getByLabelText('Option');
51
53
  expect(radio).toHaveAttribute('id', 'custom-radio-id');
52
54
  });
55
+
56
+ it('widens the outline in the error state', () => {
57
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Radio/Radio.module.css'), 'utf8');
58
+ expect(css).toMatch(/\.radioError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
59
+ });
53
60
  });
@@ -46,7 +46,7 @@ All public override tokens. Set on the RadioGroup or any ancestor; they cascade.
46
46
  | `--radioGroup_items-gap` | `var(--bds-space_xs)` | Gap between the child radios. |
47
47
  | `--radioGroup_legend-color` | `var(--bds-color_on-bg)` | Legend text colour. |
48
48
  | `--radioGroup_legend-font-family` | `var(--bds-font_family--body)` | Legend font family. |
49
- | `--radioGroup_legend-font-size` | `var(--bds-font_size--body--s)` | Legend font size. |
49
+ | `--radioGroup_legend-font-size` | `var(--bds-font_size--body)` | Legend font size, the same as the labels of the other fields. |
50
50
  | `--radioGroup_legend-font-weight` | `var(--bds-font_weight--medium)` | Legend font weight. |
51
51
  | `--radioGroup_legend-gap` | `var(--bds-space_xs)` | Space below the legend before the items. |
52
52
  | `--radioGroup_required-color` | `var(--bds-color_error)` | Colour of the visible `*` required marker. |
@@ -57,4 +57,5 @@ All public override tokens. Set on the RadioGroup or any ancestor; they cascade.
57
57
  - `disabled` on the group sets the HTML `disabled` attribute on the `<fieldset>`, which disables all child inputs
58
58
  - When `required` is true, the legend shows a visible `*` (hidden from assistive tech via `aria-hidden`) plus a visually-hidden "(required)" span, so the fieldset's accessible name resolves to `"<legend> (required)"` (e.g. `"Preferred contact (required)"`). `aria-required` is intentionally **not** set on the `<fieldset>` — it is not supported ARIA on a `role=group` element and would be ignored by assistive tech
59
59
  - Error and hint messages are linked via `aria-describedby`
60
+ - With `error`, the child controls show the error ring (`--bdc-outline_width--error`, `--bdc_color--error`) and the message gets a warning icon. Message hooks (`--formMessage_*`) are listed on FormInput
60
61
  - Arrow keys move between options within the group; `Tab` moves focus out of the group
@@ -10,7 +10,7 @@
10
10
 
11
11
  .legend {
12
12
  font-family: var(--radioGroup_legend-font-family, var(--bds-font_family--body));
13
- font-size: var(--radioGroup_legend-font-size, var(--bds-font_size--body--s));
13
+ font-size: var(--radioGroup_legend-font-size, var(--bds-font_size--body));
14
14
  font-weight: var(--radioGroup_legend-font-weight, var(--bds-font_weight--medium));
15
15
  color: var(--radioGroup_legend-color, var(--bds-color_on-bg));
16
16
  padding: 0;
@@ -42,4 +42,11 @@
42
42
  flex-direction: column;
43
43
  gap: var(--radioGroup_items-gap, var(--bds-space_xs));
44
44
  }
45
+
46
+ /* Error ring on the controls inside; state rules on the control itself
47
+ (checked, focus) still win because they have higher specificity. */
48
+ .groupError input {
49
+ --bdc_color: var(--bdc_color--error);
50
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
51
+ }
45
52
  }
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import { Radio } from '../Radio';
3
5
  import { RadioGroup } from './RadioGroup';
@@ -102,4 +104,29 @@ describe('RadioGroup', () => {
102
104
  );
103
105
  expect(screen.getByRole('group')).toBeDisabled();
104
106
  });
107
+
108
+ it('marks the group with the error class so the controls inside show the error ring', () => {
109
+ render(
110
+ <RadioGroup legend="Pick" error="Required">
111
+ <Radio label="One" name="one" value="1" />
112
+ </RadioGroup>
113
+ );
114
+ expect(screen.getByRole('group', { name: 'Pick' }).className).toContain('groupError');
115
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/RadioGroup/RadioGroup.module.css'), 'utf8');
116
+ expect(css).toMatch(/\.groupError input \{[^}]*--bdc_color: var\(--bdc_color--error\)[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
117
+ });
118
+
119
+ it('does not mark the group without an error', () => {
120
+ render(
121
+ <RadioGroup legend="Pick">
122
+ <Radio label="One" name="one" value="1" />
123
+ </RadioGroup>
124
+ );
125
+ expect(screen.getByRole('group', { name: 'Pick' }).className).not.toContain('groupError');
126
+ });
127
+
128
+ it('sizes the legend like the labels of the other fields', () => {
129
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/RadioGroup/RadioGroup.module.css'), 'utf8');
130
+ expect(css).toMatch(/\.legend \{[^}]*font-size:\s*var\(--radioGroup_legend-font-size,\s*var\(--bds-font_size--body\)\)/);
131
+ });
105
132
  });
@@ -28,7 +28,7 @@ export function RadioGroup({
28
28
  return (
29
29
  <fieldset
30
30
  {...rest}
31
- className={cn(css.group, className)}
31
+ className={cn(css.group, error && css.groupError, className)}
32
32
  aria-describedby={describedBy}
33
33
  >
34
34
  <legend className={css.legend}>
@@ -46,6 +46,7 @@
46
46
 
47
47
  .selectError {
48
48
  --bdc_color: var(--bdc_color--error);
49
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
49
50
  }
50
51
 
51
52
  .chevron {
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import userEvent from '@testing-library/user-event';
3
5
  import { Select } from './Select';
@@ -93,4 +95,9 @@ describe('Select', () => {
93
95
  expect(select).toHaveAttribute('aria-describedby', 'custom-iderror');
94
96
  expect(screen.getByText('Required')).toHaveAttribute('id', 'custom-iderror');
95
97
  });
98
+
99
+ it('widens the outline in the error state', () => {
100
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Select/Select.module.css'), 'utf8');
101
+ expect(css).toMatch(/\.selectError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
102
+ });
96
103
  });
@@ -74,6 +74,7 @@
74
74
  /* Error state — declared before checked/focus so higher-specificity states can override */
75
75
  .switch.switchError + .track {
76
76
  --bdc_color: var(--switch_border-color--error, var(--bds-color_error));
77
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
77
78
  }
78
79
 
79
80
  /* Checked state via CSS sibling combinator */
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import userEvent from '@testing-library/user-event';
3
5
  import { Switch } from './Switch';
@@ -88,4 +90,9 @@ describe('Switch', () => {
88
90
  render(<Switch label="Toggle" name="toggle" role="checkbox" />);
89
91
  expect(screen.getByRole('switch')).toBeInTheDocument();
90
92
  });
93
+
94
+ it('widens the outline in the error state', () => {
95
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Switch/Switch.module.css'), 'utf8');
96
+ expect(css).toMatch(/\.switchError \+ \.track \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
97
+ });
91
98
  });
@@ -40,5 +40,6 @@
40
40
 
41
41
  .textareaError {
42
42
  --bdc_color: var(--bdc_color--error);
43
+ --bdc-outline_width: var(--bdc-outline_width--error, 2px);
43
44
  }
44
45
  }
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import userEvent from '@testing-library/user-event';
3
5
  import { Textarea } from './Textarea';
@@ -63,4 +65,9 @@ describe('Textarea', () => {
63
65
  ?.split(' ')
64
66
  .forEach((describedById) => expect(describedById.startsWith('custom-id')).toBe(true));
65
67
  });
68
+
69
+ it('widens the outline in the error state', () => {
70
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Textarea/Textarea.module.css'), 'utf8');
71
+ expect(css).toMatch(/\.textareaError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
72
+ });
66
73
  });
@@ -1,15 +1,26 @@
1
1
  @layer boostdev.component {
2
2
  .error,
3
3
  .hint {
4
- font-size: var(--bds-font_size--body--s);
4
+ font-size: var(--formMessage_font-size, var(--bds-font_size--body--s));
5
5
  margin-inline-start: var(--labelPosition, 0);
6
6
  }
7
7
 
8
8
  .error {
9
- color: var(--bds-color_error_on-bg);
9
+ display: flex;
10
+ align-items: flex-start;
11
+ gap: var(--formMessage_gap, var(--bds-space_xxs));
12
+ color: var(--formMessage_color--error, var(--bds-color_error_on-bg));
13
+ font-weight: var(--formMessage_font-weight--error, var(--bds-font_weight--semibold));
10
14
  }
11
15
 
12
16
  .hint {
13
- font-style: italic;
17
+ color: var(--formMessage_color--hint, inherit);
18
+ font-style: var(--formMessage_font-style--hint, italic);
19
+ }
20
+
21
+ .icon {
22
+ inline-size: var(--formMessage_icon-size, 1.15em);
23
+ block-size: var(--formMessage_icon-size, 1.15em);
24
+ flex-shrink: 0;
14
25
  }
15
26
  }
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import { render, screen } from '@testing-library/react';
2
4
  import { Message } from './Message';
3
5
 
@@ -26,4 +28,24 @@ describe('Message', () => {
26
28
  render(<Message inputId="field-1" type="error" message="Required" />);
27
29
  expect(screen.getByRole('alert')).toHaveAttribute('id', 'field-1error');
28
30
  });
31
+
32
+ it('renders an aria-hidden icon before the error text', () => {
33
+ render(<Message inputId="field-1" type="error" message="Required" />);
34
+ const icon = screen.getByRole('alert').querySelector('svg');
35
+ expect(icon).toHaveAttribute('aria-hidden', 'true');
36
+ expect(screen.getByRole('alert')).toHaveTextContent('Required');
37
+ });
38
+
39
+ it('renders no icon for a hint', () => {
40
+ render(<Message inputId="field-1" type="hint" message="Hint" />);
41
+ expect(screen.getByText('Hint').querySelector('svg')).toBeNull();
42
+ });
43
+
44
+ it('exposes the message hooks in the stylesheet', () => {
45
+ const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/atoms/Message.module.css'), 'utf8');
46
+ ['--formMessage_gap', '--formMessage_icon-size', '--formMessage_color--error', '--formMessage_font-weight--error',
47
+ '--formMessage_color--hint', '--formMessage_font-style--hint', '--formMessage_font-size'].forEach((hook) => {
48
+ expect(css).toContain(hook);
49
+ });
50
+ });
29
51
  });
@@ -13,6 +13,11 @@ export const Message = ({ message, type, inputId, className }: MessageProps) =>
13
13
 
14
14
  return (
15
15
  <p id={inputId + type} role={type === 'error' ? 'alert' : undefined} className={cn(css[type], className)}>
16
+ {type === 'error' && (
17
+ <svg className={css.icon} aria-hidden="true" viewBox="0 0 256 256" fill="currentColor">
18
+ <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" />
19
+ </svg>
20
+ )}
16
21
  {message}
17
22
  </p>
18
23
  );
@@ -31,6 +31,17 @@ Heading block with optional subtitle for introducing a content section.
31
31
  ### Centered
32
32
  <Canvas of={Stories.Centered} />
33
33
 
34
+ ### Gradient
35
+ Set `appearance="gradient"` for a gradient-filled title. The gradient spans the text, not the row. Where `background-clip: text` is unsupported the title keeps its normal colour; in forced-colors mode it uses `CanvasText`.
36
+
37
+ <Canvas of={Stories.Gradient} />
38
+
39
+ <Canvas of={Stories.GradientExtraSmall} />
40
+
41
+ <Canvas of={Stories.GradientSmall} />
42
+
43
+ <Canvas of={Stories.GradientLarge} />
44
+
34
45
  ## Props
35
46
 
36
47
  <ArgTypes of={Stories} />
@@ -43,12 +54,14 @@ Heading block with optional subtitle for introducing a content section.
43
54
  <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
44
55
  </thead>
45
56
  <tbody>
46
- <tr><td>`--sectionHeader_title-color`</td><td>`var(--bds-color_on-bg)`</td><td>Title text colour</td></tr>
47
- <tr><td>`--sectionHeader_title-size`</td><td>varies by `size` prop</td><td>Title font-size (overrides the variant default)</td></tr>
57
+ <tr><td>`--sectionHeader_title-color`</td><td>`var(--bds-color_on-bg)`</td><td>Title text colour; no effect with `appearance="gradient"`, where the colour comes from `--sectionHeader_title-gradient`</td></tr>
58
+ <tr><td>`--sectionHeader_title-font-family`</td><td>`var(--bds-font_family--heading)`</td><td>Title font family</td></tr>
59
+ <tr><td>`--sectionHeader_title-gradient`</td><td>`linear-gradient(90deg, var(--bds-color_primary--strong), var(--bds-color_accent-3--strong))`</td><td>Title gradient, only with `appearance="gradient"`</td></tr>
60
+ <tr><td>`--sectionHeader_title-size`</td><td>`xs` body, `small` heading-3, `medium` heading-2, `large` heading-1</td><td>Title font-size (overrides the variant default)</td></tr>
48
61
  <tr><td>`--sectionHeader_title-line-height`</td><td>varies by `size` prop</td><td>Title line-height</td></tr>
49
- <tr><td>`--sectionHeader_title-weight`</td><td>`var(--bds-font_weight--bold)`</td><td>Title font-weight</td></tr>
50
- <tr><td>`--sectionHeader_subtitle-color`</td><td>`var(--bds-color_grey--strong)`</td><td>Subtitle text colour</td></tr>
51
- <tr><td>`--sectionHeader_subtitle-size`</td><td>varies by `size` prop</td><td>Subtitle font-size</td></tr>
62
+ <tr><td>`--sectionHeader_title-weight`</td><td>`var(--bds-font_weight--heading)`</td><td>Title font-weight</td></tr>
63
+ <tr><td>`--sectionHeader_subtitle-color`</td><td>`var(--bds-color_on-bg--muted)`</td><td>Subtitle text colour</td></tr>
64
+ <tr><td>`--sectionHeader_subtitle-size`</td><td>`xs` body--s, `small`/`medium` body, `large` heading-3</td><td>Subtitle font-size</td></tr>
52
65
  <tr><td>`--sectionHeader_subtitle-line-height`</td><td>`1.4` (`xs`/`small`), `1.3` (`medium`/`large`)</td><td>Subtitle line-height</td></tr>
53
66
  <tr><td>`--sectionHeader_gap`</td><td>`var(--bds-space_m)`</td><td>Gap between title and subtitle</td></tr>
54
67
  </tbody>
@@ -10,11 +10,11 @@
10
10
  /* --sectionHeader-title-color (kebab delimiter) is deprecated — use --sectionHeader_title-color */
11
11
  color: var(--sectionHeader_title-color, var(--sectionHeader-title-color, var(--bds-color_on-bg)));
12
12
  margin: 0;
13
- font-family: var(--bds-font_family--body);
13
+ font-family: var(--sectionHeader_title-font-family, var(--bds-font_family--heading));
14
14
  }
15
15
 
16
16
  .subtitle {
17
- color: var(--sectionHeader_subtitle-color, var(--bds-color_grey--strong));
17
+ color: var(--sectionHeader_subtitle-color, var(--bds-color_on-bg--muted, var(--bds-color_grey--strong)));
18
18
  margin: 0;
19
19
  font-family: var(--bds-font_family--body);
20
20
  font-weight: var(--bds-font_weight--medium);
@@ -37,31 +37,31 @@
37
37
 
38
38
  /* medium first — default size, so MCP extraction picks up its fallbacks */
39
39
  .sectionHeader.--medium .title {
40
- font-size: var(--sectionHeader_title-size, var(--bds-font_size--heading-1));
40
+ font-size: var(--sectionHeader_title-size, var(--bds-font_size--heading-2));
41
41
  line-height: var(--sectionHeader_title-line-height, var(--bds-font_line-height--heading));
42
- font-weight: var(--sectionHeader_title-weight, var(--bds-font_weight--bold));
42
+ font-weight: var(--sectionHeader_title-weight, var(--bds-font_weight--heading, var(--bds-font_weight--bold)));
43
43
  }
44
44
 
45
45
  .sectionHeader.--medium .subtitle {
46
- font-size: var(--sectionHeader_subtitle-size, var(--bds-font_size--heading-3));
46
+ font-size: var(--sectionHeader_subtitle-size, var(--bds-font_size--body));
47
47
  line-height: var(--sectionHeader_subtitle-line-height, 1.3);
48
48
  }
49
49
 
50
50
  .sectionHeader.--xs .title {
51
- font-size: var(--sectionHeader_title-size, var(--bds-font_size--heading-2));
51
+ font-size: var(--sectionHeader_title-size, var(--bds-font_size--body));
52
52
  line-height: var(--sectionHeader_title-line-height, var(--bds-font_line-height--heading));
53
- font-weight: var(--sectionHeader_title-weight, var(--bds-font_weight--bold));
53
+ font-weight: var(--sectionHeader_title-weight, var(--bds-font_weight--heading, var(--bds-font_weight--bold)));
54
54
  }
55
55
 
56
56
  .sectionHeader.--xs .subtitle {
57
- font-size: var(--sectionHeader_subtitle-size, var(--bds-font_size--body));
57
+ font-size: var(--sectionHeader_subtitle-size, var(--bds-font_size--body--s));
58
58
  line-height: var(--sectionHeader_subtitle-line-height, 1.4);
59
59
  }
60
60
 
61
61
  .sectionHeader.--small .title {
62
- font-size: var(--sectionHeader_title-size, var(--bds-font_size--heading-1));
62
+ font-size: var(--sectionHeader_title-size, var(--bds-font_size--heading-3));
63
63
  line-height: var(--sectionHeader_title-line-height, var(--bds-font_line-height--heading));
64
- font-weight: var(--sectionHeader_title-weight, var(--bds-font_weight--bold));
64
+ font-weight: var(--sectionHeader_title-weight, var(--bds-font_weight--heading, var(--bds-font_weight--bold)));
65
65
  }
66
66
 
67
67
  .sectionHeader.--small .subtitle {
@@ -70,15 +70,36 @@
70
70
  }
71
71
 
72
72
  .sectionHeader.--large .title {
73
- font-size: var(--sectionHeader_title-size, var(--bds-font_size--display));
74
- line-height: var(--sectionHeader_title-line-height, var(--bds-font_line-height--display));
75
- font-weight: var(--sectionHeader_title-weight, var(--bds-font_weight--bold));
73
+ font-size: var(--sectionHeader_title-size, var(--bds-font_size--heading-1));
74
+ line-height: var(--sectionHeader_title-line-height, var(--bds-font_line-height--heading));
75
+ font-weight: var(--sectionHeader_title-weight, var(--bds-font_weight--heading, var(--bds-font_weight--bold)));
76
76
  letter-spacing: var(--bds-font_tracking--tight);
77
77
  }
78
78
 
79
79
  .sectionHeader.--large .subtitle {
80
- font-size: var(--sectionHeader_subtitle-size, var(--bds-font_size--heading-2));
80
+ font-size: var(--sectionHeader_subtitle-size, var(--bds-font_size--heading-3));
81
81
  line-height: var(--sectionHeader_subtitle-line-height, 1.3);
82
82
  font-weight: var(--bds-font_weight--medium);
83
83
  }
84
+
85
+ /* Gradient only where background-clip: text is supported; elsewhere the title keeps its normal colour */
86
+ @supports (background-clip: text) {
87
+ .sectionHeader.--gradient .title {
88
+ inline-size: fit-content;
89
+ max-inline-size: 100%;
90
+ background-image: var(--sectionHeader_title-gradient, linear-gradient(90deg, var(--bds-color_primary--strong, var(--bds-color_blue--strong)), var(--bds-color_accent-3--strong, var(--bds-color_blue--strong))));
91
+ background-clip: text;
92
+ color: transparent;
93
+ }
94
+ }
95
+
96
+ @media (forced-colors: active) {
97
+ .sectionHeader.--gradient .title {
98
+ background-image: none;
99
+
100
+ /* system colour keyword: forced-colors mode ignores tokens */
101
+ /* stylelint-disable-next-line scale-unlimited/declaration-strict-value */
102
+ color: CanvasText;
103
+ }
104
+ }
84
105
  }