@boostdev/design-system-components 1.0.3 → 1.1.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 (225) hide show
  1. package/AGENTS.md +5 -5
  2. package/dist/client.cjs +236 -153
  3. package/dist/client.css +547 -483
  4. package/dist/client.d.cts +53 -94
  5. package/dist/client.d.ts +53 -94
  6. package/dist/client.js +237 -163
  7. package/dist/index.cjs +236 -153
  8. package/dist/index.css +547 -483
  9. package/dist/index.d.cts +53 -94
  10. package/dist/index.d.ts +53 -94
  11. package/dist/index.js +237 -163
  12. package/dist/native/index.cjs +991 -0
  13. package/dist/native/index.d.cts +208 -0
  14. package/dist/native/index.d.ts +208 -0
  15. package/dist/native/index.js +968 -0
  16. package/dist/web-components.d.ts +303 -0
  17. package/dist/web-components.js +1968 -0
  18. package/package.json +27 -5
  19. package/src/components/interaction/Button/Button.module.css +10 -7
  20. package/src/components/interaction/Button/Button.native.spec.tsx +35 -0
  21. package/src/components/interaction/Button/Button.native.stories.tsx +42 -0
  22. package/src/components/interaction/Button/Button.native.tsx +95 -0
  23. package/src/components/interaction/Button/Button.stories.tsx +1 -1
  24. package/src/components/interaction/Button/Button.tsx +2 -2
  25. package/src/components/interaction/Command/Command.mdx +16 -0
  26. package/src/components/interaction/Command/Command.stories.tsx +1 -1
  27. package/src/components/interaction/Command/Command.tsx +2 -2
  28. package/src/components/interaction/Dialog/Dialog.stories.tsx +1 -1
  29. package/src/components/interaction/Dialog/Dialog.tsx +2 -2
  30. package/src/components/interaction/Drawer/Drawer.mdx +14 -0
  31. package/src/components/interaction/Drawer/Drawer.stories.tsx +1 -1
  32. package/src/components/interaction/Drawer/Drawer.tsx +2 -2
  33. package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +1 -1
  34. package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +2 -2
  35. package/src/components/interaction/Popover/Popover.mdx +17 -0
  36. package/src/components/interaction/Popover/Popover.module.css +70 -13
  37. package/src/components/interaction/Popover/Popover.spec.tsx +68 -4
  38. package/src/components/interaction/Popover/Popover.stories.tsx +1 -1
  39. package/src/components/interaction/Popover/Popover.tsx +76 -20
  40. package/src/components/interaction/Rating/Rating.stories.tsx +1 -1
  41. package/src/components/interaction/Rating/Rating.tsx +2 -2
  42. package/src/components/interaction/Toast/Toast.module.css +6 -1
  43. package/src/components/interaction/Toast/Toast.spec.tsx +21 -0
  44. package/src/components/interaction/Toast/Toast.stories.tsx +1 -1
  45. package/src/components/interaction/Toast/Toast.tsx +22 -2
  46. package/src/components/interaction/form/Checkbox/Checkbox.mdx +2 -2
  47. package/src/components/interaction/form/Checkbox/Checkbox.module.css +3 -3
  48. package/src/components/interaction/form/Checkbox/Checkbox.native.spec.tsx +42 -0
  49. package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +36 -0
  50. package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +80 -0
  51. package/src/components/interaction/form/Checkbox/Checkbox.stories.tsx +1 -1
  52. package/src/components/interaction/form/Checkbox/Checkbox.tsx +2 -2
  53. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +44 -0
  54. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.stories.tsx +1 -1
  55. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +2 -2
  56. package/src/components/interaction/form/Combobox/Combobox.mdx +21 -0
  57. package/src/components/interaction/form/Combobox/Combobox.module.css +1 -1
  58. package/src/components/interaction/form/Combobox/Combobox.stories.tsx +1 -1
  59. package/src/components/interaction/form/Combobox/Combobox.tsx +2 -2
  60. package/src/components/interaction/form/FileInput/FileInput.mdx +14 -0
  61. package/src/components/interaction/form/FileInput/FileInput.module.css +1 -1
  62. package/src/components/interaction/form/FileInput/FileInput.stories.tsx +1 -1
  63. package/src/components/interaction/form/FileInput/FileInput.tsx +2 -2
  64. package/src/components/interaction/form/FormInput/FormInput.stories.tsx +1 -1
  65. package/src/components/interaction/form/FormInput/FormInput.tsx +2 -2
  66. package/src/components/interaction/form/NumberInput/NumberInput.mdx +16 -0
  67. package/src/components/interaction/form/NumberInput/NumberInput.stories.tsx +1 -1
  68. package/src/components/interaction/form/NumberInput/NumberInput.tsx +2 -2
  69. package/src/components/interaction/form/Radio/Radio.mdx +3 -3
  70. package/src/components/interaction/form/Radio/Radio.module.css +3 -3
  71. package/src/components/interaction/form/Radio/Radio.native.stories.tsx +46 -0
  72. package/src/components/interaction/form/Radio/Radio.native.tsx +79 -0
  73. package/src/components/interaction/form/Radio/Radio.stories.tsx +1 -1
  74. package/src/components/interaction/form/Radio/Radio.tsx +2 -2
  75. package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +45 -0
  76. package/src/components/interaction/form/RadioGroup/RadioGroup.stories.tsx +1 -1
  77. package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +2 -2
  78. package/src/components/interaction/form/SegmentedControl/SegmentedControl.stories.tsx +1 -1
  79. package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +2 -2
  80. package/src/components/interaction/form/Select/Select.stories.tsx +1 -1
  81. package/src/components/interaction/form/Select/Select.tsx +2 -2
  82. package/src/components/interaction/form/Slider/Slider.mdx +1 -1
  83. package/src/components/interaction/form/Slider/Slider.module.css +4 -4
  84. package/src/components/interaction/form/Slider/Slider.stories.tsx +1 -1
  85. package/src/components/interaction/form/Slider/Slider.tsx +2 -2
  86. package/src/components/interaction/form/Switch/Switch.mdx +4 -4
  87. package/src/components/interaction/form/Switch/Switch.module.css +3 -3
  88. package/src/components/interaction/form/Switch/Switch.native.spec.tsx +60 -0
  89. package/src/components/interaction/form/Switch/Switch.native.stories.tsx +35 -0
  90. package/src/components/interaction/form/Switch/Switch.native.tsx +59 -0
  91. package/src/components/interaction/form/Switch/Switch.stories.tsx +1 -1
  92. package/src/components/interaction/form/Switch/Switch.tsx +2 -2
  93. package/src/components/interaction/form/Textarea/Textarea.mdx +17 -0
  94. package/src/components/interaction/form/Textarea/Textarea.stories.tsx +1 -1
  95. package/src/components/interaction/form/Textarea/Textarea.tsx +2 -2
  96. package/src/components/interaction/form/atoms/InputContainer.tsx +2 -1
  97. package/src/components/interaction/form/atoms/Label.native.stories.tsx +18 -0
  98. package/src/components/interaction/form/atoms/Label.native.tsx +29 -0
  99. package/src/components/interaction/form/atoms/Label.tsx +5 -3
  100. package/src/components/interaction/form/atoms/Message.native.spec.tsx +30 -0
  101. package/src/components/interaction/form/atoms/Message.native.stories.tsx +22 -0
  102. package/src/components/interaction/form/atoms/Message.native.tsx +34 -0
  103. package/src/components/interaction/form/atoms/Message.tsx +5 -3
  104. package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +50 -0
  105. package/src/components/layout/ButtonGroup/ButtonGroup.native.tsx +34 -0
  106. package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +1 -1
  107. package/src/components/layout/ButtonGroup/ButtonGroup.tsx +2 -2
  108. package/src/components/layout/Card/Card.native.stories.tsx +53 -0
  109. package/src/components/layout/Card/Card.native.tsx +89 -0
  110. package/src/components/layout/Card/Card.stories.tsx +1 -1
  111. package/src/components/layout/Card/Card.tsx +2 -2
  112. package/src/components/layout/IconWrapper/IconWrapper.mdx +2 -2
  113. package/src/components/layout/IconWrapper/IconWrapper.native.spec.tsx +39 -0
  114. package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +41 -0
  115. package/src/components/layout/IconWrapper/IconWrapper.native.tsx +20 -0
  116. package/src/components/layout/IconWrapper/IconWrapper.stories.tsx +1 -1
  117. package/src/components/layout/IconWrapper/IconWrapper.tsx +2 -2
  118. package/src/components/layout/SectionHeader/SectionHeader.mdx +13 -0
  119. package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +38 -0
  120. package/src/components/layout/SectionHeader/SectionHeader.native.tsx +79 -0
  121. package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +1 -1
  122. package/src/components/layout/SectionHeader/SectionHeader.tsx +2 -2
  123. package/src/components/ui/Accordion/Accordion.mdx +14 -0
  124. package/src/components/ui/Accordion/Accordion.stories.tsx +1 -1
  125. package/src/components/ui/Accordion/Accordion.tsx +2 -2
  126. package/src/components/ui/Alert/Alert.native.stories.tsx +43 -0
  127. package/src/components/ui/Alert/Alert.native.tsx +94 -0
  128. package/src/components/ui/Alert/Alert.stories.tsx +1 -1
  129. package/src/components/ui/Alert/Alert.tsx +2 -2
  130. package/src/components/ui/Avatar/Avatar.native.spec.tsx +47 -0
  131. package/src/components/ui/Avatar/Avatar.native.stories.tsx +34 -0
  132. package/src/components/ui/Avatar/Avatar.native.tsx +85 -0
  133. package/src/components/ui/Avatar/Avatar.stories.tsx +1 -1
  134. package/src/components/ui/Avatar/Avatar.tsx +2 -2
  135. package/src/components/ui/Badge/Badge.mdx +2 -2
  136. package/src/components/ui/Badge/Badge.native.stories.tsx +36 -0
  137. package/src/components/ui/Badge/Badge.native.tsx +50 -0
  138. package/src/components/ui/Badge/Badge.stories.tsx +1 -1
  139. package/src/components/ui/Badge/Badge.tsx +2 -2
  140. package/src/components/ui/Breadcrumb/Breadcrumb.stories.tsx +1 -1
  141. package/src/components/ui/Breadcrumb/Breadcrumb.tsx +2 -2
  142. package/src/components/ui/Calendar/Calendar.mdx +16 -0
  143. package/src/components/ui/Calendar/Calendar.stories.tsx +1 -1
  144. package/src/components/ui/Calendar/Calendar.tsx +2 -2
  145. package/src/components/ui/Carousel/Carousel.stories.tsx +1 -1
  146. package/src/components/ui/Carousel/Carousel.tsx +2 -2
  147. package/src/components/ui/Collapsible/Collapsible.stories.tsx +1 -1
  148. package/src/components/ui/Collapsible/Collapsible.tsx +2 -2
  149. package/src/components/ui/DescriptionList/DescriptionList.stories.tsx +1 -1
  150. package/src/components/ui/DescriptionList/DescriptionList.tsx +2 -2
  151. package/src/components/ui/Link/Link.mdx +14 -0
  152. package/src/components/ui/Link/Link.stories.tsx +1 -1
  153. package/src/components/ui/Link/Link.tsx +2 -2
  154. package/src/components/ui/Loading/Loading.native.spec.tsx +24 -0
  155. package/src/components/ui/Loading/Loading.native.stories.tsx +33 -0
  156. package/src/components/ui/Loading/Loading.native.tsx +29 -0
  157. package/src/components/ui/Loading/Loading.stories.tsx +1 -1
  158. package/src/components/ui/Loading/Loading.tsx +2 -2
  159. package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +39 -0
  160. package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +76 -0
  161. package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +1 -1
  162. package/src/components/ui/NotificationBanner/NotificationBanner.tsx +2 -2
  163. package/src/components/ui/Pagination/Pagination.module.css +1 -1
  164. package/src/components/ui/Pagination/Pagination.stories.tsx +1 -1
  165. package/src/components/ui/Pagination/Pagination.tsx +2 -2
  166. package/src/components/ui/Progress/Progress.mdx +1 -1
  167. package/src/components/ui/Progress/Progress.module.css +1 -1
  168. package/src/components/ui/Progress/Progress.native.stories.tsx +34 -0
  169. package/src/components/ui/Progress/Progress.native.tsx +84 -0
  170. package/src/components/ui/Progress/Progress.stories.tsx +1 -1
  171. package/src/components/ui/Progress/Progress.tsx +2 -2
  172. package/src/components/ui/ProgressCircle/ProgressCircle.module.css +1 -1
  173. package/src/components/ui/ProgressCircle/ProgressCircle.stories.tsx +1 -1
  174. package/src/components/ui/ProgressCircle/ProgressCircle.tsx +2 -2
  175. package/src/components/ui/Separator/Separator.mdx +14 -0
  176. package/src/components/ui/Separator/Separator.native.stories.tsx +42 -0
  177. package/src/components/ui/Separator/Separator.native.tsx +32 -0
  178. package/src/components/ui/Separator/Separator.stories.tsx +1 -1
  179. package/src/components/ui/Separator/Separator.tsx +2 -2
  180. package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +33 -0
  181. package/src/components/ui/Skeleton/Skeleton.native.tsx +41 -0
  182. package/src/components/ui/Skeleton/Skeleton.stories.tsx +1 -1
  183. package/src/components/ui/Skeleton/Skeleton.tsx +2 -3
  184. package/src/components/ui/SkipLink/SkipLink.stories.tsx +1 -1
  185. package/src/components/ui/SkipLink/SkipLink.tsx +5 -3
  186. package/src/components/ui/Table/Table.mdx +14 -0
  187. package/src/components/ui/Table/Table.stories.tsx +1 -1
  188. package/src/components/ui/Table/Table.tsx +2 -2
  189. package/src/components/ui/Tabs/Tabs.stories.tsx +1 -1
  190. package/src/components/ui/Tabs/Tabs.tsx +2 -2
  191. package/src/components/ui/Tooltip/Tooltip.mdx +14 -0
  192. package/src/components/ui/Tooltip/Tooltip.stories.tsx +1 -1
  193. package/src/components/ui/Tooltip/Tooltip.tsx +2 -2
  194. package/src/components/ui/Typography/Typography.mdx +13 -0
  195. package/src/components/ui/Typography/Typography.native.stories.tsx +38 -0
  196. package/src/components/ui/Typography/Typography.native.tsx +65 -0
  197. package/src/components/ui/Typography/Typography.stories.tsx +1 -1
  198. package/src/components/ui/Typography/Typography.tsx +2 -2
  199. package/src/css/bdc.css +8 -0
  200. package/src/index.ts +1 -0
  201. package/src/native/ThemeContext.tsx +28 -0
  202. package/src/native/tokens.ts +13 -0
  203. package/src/native.ts +39 -0
  204. package/src/react-augment.d.ts +13 -0
  205. package/src/stories/DesignSystem/DarkMode.mdx +130 -0
  206. package/src/types.ts +2 -0
  207. package/src/typings.d.ts +3 -0
  208. package/src/web-components/globals.ts +61 -0
  209. package/src/web-components/index.ts +12 -0
  210. package/src/web-components/interaction/BdsButton.stories.tsx +60 -0
  211. package/src/web-components/interaction/BdsPopover.stories.tsx +70 -0
  212. package/src/web-components/interaction/BdsToastProvider.stories.tsx +73 -0
  213. package/src/web-components/interaction/bds-button.spec.ts +95 -0
  214. package/src/web-components/interaction/bds-button.ts +293 -0
  215. package/src/web-components/interaction/bds-popover.spec.ts +126 -0
  216. package/src/web-components/interaction/bds-popover.ts +217 -0
  217. package/src/web-components/interaction/bds-toast-provider.spec.ts +122 -0
  218. package/src/web-components/interaction/bds-toast-provider.ts +211 -0
  219. package/src/web-components/test/helpers.ts +14 -0
  220. package/src/web-components/ui/BdsAlert.stories.tsx +60 -0
  221. package/src/web-components/ui/BdsBadge.stories.tsx +37 -0
  222. package/src/web-components/ui/bds-alert.spec.ts +109 -0
  223. package/src/web-components/ui/bds-alert.ts +209 -0
  224. package/src/web-components/ui/bds-badge.spec.ts +51 -0
  225. package/src/web-components/ui/bds-badge.ts +88 -0
@@ -3,7 +3,7 @@ import { Checkbox } from '../Checkbox';
3
3
  import { CheckboxGroup } from './CheckboxGroup';
4
4
 
5
5
  const meta = {
6
- title: 'Form/CheckboxGroup',
6
+ title: 'React/Form/CheckboxGroup',
7
7
  component: CheckboxGroup,
8
8
  } satisfies Meta<typeof CheckboxGroup>;
9
9
 
@@ -2,15 +2,15 @@ import { ReactNode, useId } from 'react';
2
2
  import css from './CheckboxGroup.module.css';
3
3
  import { Message } from '../atoms/Message';
4
4
  import { cn } from '@boostdev/design-system-foundation';
5
+ import type { WithClassName } from '../../../../types';
5
6
 
6
- export interface CheckboxGroupProps {
7
+ export interface CheckboxGroupProps extends WithClassName {
7
8
  legend: string;
8
9
  children: ReactNode;
9
10
  error?: string;
10
11
  hint?: string;
11
12
  required?: boolean;
12
13
  disabled?: boolean;
13
- className?: string;
14
14
  }
15
15
 
16
16
  export function CheckboxGroup({
@@ -29,6 +29,27 @@ Searchable select with type-ahead filtering. Combines a text input with a dropdo
29
29
 
30
30
  <ArgTypes of={Stories} />
31
31
 
32
+ ## CSS variables
33
+
34
+
35
+ <table>
36
+ <thead>
37
+ <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
38
+ </thead>
39
+ <tbody>
40
+ <tr><td>`--combobox_color_bg`</td><td>`var(--bds-color_bg)`</td><td>Input background colour</td></tr>
41
+ <tr><td>`--combobox_color`</td><td>`var(--bds-color_on-bg)`</td><td>Input text colour</td></tr>
42
+ <tr><td>`--combobox_border`</td><td>`none`</td><td>Input border</td></tr>
43
+ <tr><td>`--combobox_radius`</td><td>`var(--bdc-outline_radius)`</td><td>Input corner radius</td></tr>
44
+ <tr><td>`--combobox_shadow`</td><td>`var(--bdc-outline_shadow)`</td><td>Input box shadow</td></tr>
45
+ <tr><td>`--combobox_on-color`</td><td>`var(--bds-color_on-bg)`</td><td>Dropdown list text colour</td></tr>
46
+ <tr><td>`--combobox_listbox-border`</td><td>`none`</td><td>Dropdown list border</td></tr>
47
+ <tr><td>`--combobox_listbox-radius`</td><td>`var(--bdc-outline_radius)`</td><td>Dropdown list corner radius</td></tr>
48
+ <tr><td>`--combobox_listbox-shadow`</td><td>`var(--bds-shadow_m)`</td><td>Dropdown list box shadow</td></tr>
49
+ </tbody>
50
+ </table>
51
+
52
+
32
53
  ## Accessibility
33
54
 
34
55
  - Input uses `role="combobox"` with `aria-autocomplete="list"` and `aria-controls`
@@ -98,7 +98,7 @@
98
98
 
99
99
  .option.--highlighted {
100
100
  background-color: var(--bds-color_bg--subtle);
101
- color: var(--bds-color_interactive);
101
+ color: var(--bds-color_interactive_on-bg);
102
102
  }
103
103
 
104
104
  .option.--selected {
@@ -12,7 +12,7 @@ const options = [
12
12
  ];
13
13
 
14
14
  const meta = {
15
- title: 'Form/Combobox',
15
+ title: 'React/Form/Combobox',
16
16
  component: Combobox,
17
17
  } satisfies Meta<typeof Combobox>;
18
18
 
@@ -13,6 +13,7 @@ import { cn } from '@boostdev/design-system-foundation';
13
13
  import { InputContainer } from '../atoms/InputContainer';
14
14
  import { Label } from '../atoms/Label';
15
15
  import { Message } from '../atoms/Message';
16
+ import type { WithClassName } from '../../../../types';
16
17
 
17
18
  export interface ComboboxOption {
18
19
  value: string;
@@ -20,7 +21,7 @@ export interface ComboboxOption {
20
21
  disabled?: boolean;
21
22
  }
22
23
 
23
- interface ComboboxProps {
24
+ interface ComboboxProps extends WithClassName {
24
25
  label: ReactNode;
25
26
  name: string;
26
27
  options: ComboboxOption[];
@@ -30,7 +31,6 @@ interface ComboboxProps {
30
31
  disabled?: boolean;
31
32
  error?: string;
32
33
  hint?: string;
33
- className?: string;
34
34
  }
35
35
 
36
36
  export function Combobox({
@@ -31,6 +31,20 @@ File upload input with styled trigger button, hint, and error support.
31
31
 
32
32
  <ArgTypes of={Stories} />
33
33
 
34
+ ## CSS variables
35
+
36
+
37
+ <table>
38
+ <thead>
39
+ <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
40
+ </thead>
41
+ <tbody>
42
+ <tr><td>`--fileInput_color`</td><td>`var(--bds-color_on-bg)`</td><td>Text colour</td></tr>
43
+ <tr><td>`--fileInput_border-color`</td><td>`currentcolor`</td><td>Drop zone border colour</td></tr>
44
+ </tbody>
45
+ </table>
46
+
47
+
34
48
  ## Accessibility
35
49
 
36
50
  - Native `<input type="file">` — opens the OS file picker
@@ -59,7 +59,7 @@
59
59
  }
60
60
 
61
61
  .prompt strong {
62
- color: var(--bds-color_interactive);
62
+ color: var(--bds-color_interactive_on-bg);
63
63
  font-weight: var(--bds-font_weight--semibold);
64
64
  }
65
65
 
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { FileInput } from './FileInput';
3
3
 
4
4
  const meta = {
5
- title: 'Form/FileInput',
5
+ title: 'React/Form/FileInput',
6
6
  component: FileInput,
7
7
  } satisfies Meta<typeof FileInput>;
8
8
 
@@ -3,8 +3,9 @@ import css from './FileInput.module.css';
3
3
  import { Message } from '../atoms/Message';
4
4
  import { InputContainer } from '../atoms/InputContainer';
5
5
  import { cn } from '@boostdev/design-system-foundation';
6
+ import type { WithClassName } from '../../../../types';
6
7
 
7
- interface FileInputProps {
8
+ interface FileInputProps extends WithClassName {
8
9
  label: string;
9
10
  name: string;
10
11
  accept?: string;
@@ -14,7 +15,6 @@ interface FileInputProps {
14
15
  error?: string;
15
16
  hint?: string;
16
17
  onChange?: (files: FileList | null) => void;
17
- className?: string;
18
18
  }
19
19
 
20
20
  export function FileInput({
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { FormInput } from './FormInput';
3
3
 
4
4
  const meta = {
5
- title: 'Form/FormInput',
5
+ title: 'React/Form/FormInput',
6
6
  component: FormInput,
7
7
  } satisfies Meta<typeof FormInput>;
8
8
 
@@ -4,15 +4,15 @@ import { Label } from '../atoms/Label';
4
4
  import { Message } from '../atoms/Message';
5
5
  import { cn } from '@boostdev/design-system-foundation';
6
6
  import { InputContainer } from '../atoms/InputContainer';
7
+ import type { WithClassName } from '../../../../types';
7
8
 
8
- interface FormInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
9
+ interface FormInputProps extends WithClassName, Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
9
10
  label: ReactNode;
10
11
  name: string;
11
12
  type?: Exclude<HTMLInputTypeAttribute, 'checkbox' | 'radio' | 'file'>;
12
13
  ariaLabel?: string;
13
14
  error?: string;
14
15
  hint?: string;
15
- className?: string;
16
16
  }
17
17
 
18
18
  export function FormInput({
@@ -31,6 +31,22 @@ Numeric text input with increment/decrement stepper buttons. Enforces `min`, `ma
31
31
 
32
32
  <ArgTypes of={Stories} />
33
33
 
34
+ ## CSS variables
35
+
36
+
37
+ <table>
38
+ <thead>
39
+ <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
40
+ </thead>
41
+ <tbody>
42
+ <tr><td>`--numberInput_color`</td><td>`var(--bds-color_on-bg)`</td><td>Text colour</td></tr>
43
+ <tr><td>`--numberInput_border`</td><td>`none`</td><td>Border</td></tr>
44
+ <tr><td>`--numberInput_radius`</td><td>`var(--bdc-outline_radius)`</td><td>Corner radius</td></tr>
45
+ <tr><td>`--numberInput_shadow`</td><td>`var(--bdc-outline_shadow)`</td><td>Box shadow</td></tr>
46
+ </tbody>
47
+ </table>
48
+
49
+
34
50
  ## Accessibility
35
51
 
36
52
  - Renders `<input type="number">` — browser-native spinner support
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { NumberInput } from './NumberInput';
3
3
 
4
4
  const meta = {
5
- title: 'Form/NumberInput',
5
+ title: 'React/Form/NumberInput',
6
6
  component: NumberInput,
7
7
  } satisfies Meta<typeof NumberInput>;
8
8
 
@@ -4,8 +4,9 @@ import {Label} from '../atoms/Label';
4
4
  import {Message} from '../atoms/Message';
5
5
  import {InputContainer} from '../atoms/InputContainer';
6
6
  import {cn} from '@boostdev/design-system-foundation';
7
+ import type { WithClassName } from '../../../../types';
7
8
 
8
- interface NumberInputProps {
9
+ interface NumberInputProps extends WithClassName {
9
10
  label: string;
10
11
  name: string;
11
12
  value?: number;
@@ -17,7 +18,6 @@ interface NumberInputProps {
17
18
  error?: string;
18
19
  hint?: string;
19
20
  onChange?: (value: number) => void;
20
- className?: string;
21
21
  }
22
22
 
23
23
  export function NumberInput({
@@ -46,9 +46,9 @@ Single-select option within a group. Renders a styled native `<input type="radio
46
46
  <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
47
47
  </thead>
48
48
  <tbody>
49
- <tr><td>`--radio_color-active`</td><td>`var(--bds-active)`</td><td>Selected fill and border colour</td></tr>
50
- <tr><td>`--radio_color-on-active`</td><td>`var(--bds-on-active)`</td><td>Inner dot colour</td></tr>
51
- <tr><td>`--radio_description-color`</td><td>`var(--bds-on-bg--subtle)`</td><td>Description text colour</td></tr>
49
+ <tr><td>`--radio_color-active`</td><td>`var(--bdc-color_active)`</td><td>Selected fill and border colour</td></tr>
50
+ <tr><td>`--radio_color-on-active`</td><td>`var(--bdc-color_on-active)`</td><td>Inner dot colour</td></tr>
51
+ <tr><td>`--radio_description-color`</td><td>`var(--bds-color_on-bg--subtle)`</td><td>Description text colour</td></tr>
52
52
  </tbody>
53
53
  </table>
54
54
 
@@ -48,8 +48,8 @@
48
48
  }
49
49
 
50
50
  .radio:checked {
51
- background-color: var(--radio_color-active, var(--bds-color_green));
52
- --bdc_color: var(--radio_color-active, var(--bds-color_green));
51
+ background-color: var(--radio_color-active, var(--bdc-color_active));
52
+ --bdc_color: var(--radio_color-active, var(--bdc-color_active));
53
53
  }
54
54
 
55
55
  .radio:checked::after {
@@ -61,7 +61,7 @@
61
61
  width: 0.4em;
62
62
  height: 0.4em;
63
63
  border-radius: 50%;
64
- background-color: var(--radio_color-on-active, var(--bds-color_on-green));
64
+ background-color: var(--radio_color-on-active, var(--bdc-color_on-active));
65
65
  }
66
66
 
67
67
  .radio:focus {
@@ -0,0 +1,46 @@
1
+ import React, { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { ThemeProvider } from '../../../../native/ThemeContext';
4
+ import { Radio } from './Radio.native';
5
+
6
+ const meta = {
7
+ tags: ['!stable', 'alpha'],
8
+ title: 'React Native/Form/Radio',
9
+ component: Radio,
10
+ decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
11
+ parameters: { layout: 'padded' },
12
+ } satisfies Meta<typeof Radio>;
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof meta>;
16
+
17
+ export const Unchecked: Story = { args: { label: 'Option A', name: 'option', checked: false } };
18
+ export const Checked: Story = { args: { label: 'Option A', name: 'option', checked: true } };
19
+ export const Disabled: Story = { args: { label: 'Option A', name: 'option', disabled: true } };
20
+ export const WithError: Story = {
21
+ args: { label: 'Option A', name: 'option', error: 'Please select an option.' },
22
+ };
23
+ export const WithHint: Story = {
24
+ args: { label: 'Option A', name: 'option', hint: 'This is the recommended option.' },
25
+ };
26
+ export const Group: Story = {
27
+ render: () => {
28
+ const [selected, setSelected] = useState<string | null>(null);
29
+ const options = ['Option A', 'Option B', 'Option C'];
30
+ return (
31
+ <ThemeProvider>
32
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
33
+ {options.map(o => (
34
+ <Radio
35
+ key={o}
36
+ label={o}
37
+ name="group"
38
+ checked={selected === o}
39
+ onChange={() => setSelected(o)}
40
+ />
41
+ ))}
42
+ </div>
43
+ </ThemeProvider>
44
+ );
45
+ },
46
+ };
@@ -0,0 +1,79 @@
1
+ import { ReactNode } from 'react';
2
+ import { StyleSheet, View, Pressable, StyleProp, ViewStyle } from 'react-native';
3
+ import { spacing } from '../../../../native/tokens';
4
+ import { useTheme } from '../../../../native/ThemeContext';
5
+ import { Label } from '../atoms/Label.native';
6
+ import { Message } from '../atoms/Message.native';
7
+
8
+ interface RadioProps {
9
+ label: ReactNode;
10
+ name: string;
11
+ checked?: boolean;
12
+ onChange?: () => void;
13
+ disabled?: boolean;
14
+ error?: string;
15
+ hint?: string;
16
+ style?: StyleProp<ViewStyle>;
17
+ }
18
+
19
+ const DOT = 22;
20
+ const INNER = 10;
21
+
22
+ const styles = StyleSheet.create({
23
+ container: {
24
+ gap: spacing.xxs,
25
+ },
26
+ row: {
27
+ flexDirection: 'row',
28
+ alignItems: 'center',
29
+ gap: spacing.xs,
30
+ minHeight: 44,
31
+ },
32
+ outer: {
33
+ width: DOT,
34
+ height: DOT,
35
+ borderRadius: DOT / 2,
36
+ borderWidth: 2,
37
+ alignItems: 'center',
38
+ justifyContent: 'center',
39
+ },
40
+ inner: {
41
+ width: INNER,
42
+ height: INNER,
43
+ borderRadius: INNER / 2,
44
+ },
45
+ });
46
+
47
+ export function Radio({
48
+ label,
49
+ name,
50
+ checked = false,
51
+ onChange,
52
+ disabled = false,
53
+ error,
54
+ hint,
55
+ style,
56
+ }: RadioProps) {
57
+ const { colors } = useTheme();
58
+ const borderColor = error ? colors.colorError : checked ? colors.colorInteractive : colors.colorBgSubtle;
59
+
60
+ return (
61
+ <View style={[styles.container, style]}>
62
+ <Pressable
63
+ onPress={() => !disabled && onChange?.()}
64
+ accessibilityRole="radio"
65
+ accessibilityLabel={typeof label === 'string' ? label : name}
66
+ accessibilityState={{ checked, disabled }}
67
+ disabled={disabled}
68
+ style={({ pressed }) => [styles.row, pressed && { opacity: 0.7 }, disabled && { opacity: 0.4 }]}
69
+ >
70
+ <View style={[styles.outer, { borderColor }]}>
71
+ {checked && <View style={[styles.inner, { backgroundColor: colors.colorInteractive }]} />}
72
+ </View>
73
+ <Label label={label} />
74
+ </Pressable>
75
+ <Message type="error" message={error} />
76
+ <Message type="hint" message={hint} />
77
+ </View>
78
+ );
79
+ }
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { Radio } from './Radio';
3
3
 
4
4
  const meta = {
5
- title: 'Form/Radio',
5
+ title: 'React/Form/Radio',
6
6
  component: Radio,
7
7
  } satisfies Meta<typeof Radio>;
8
8
 
@@ -4,14 +4,14 @@ import { Message } from '../atoms/Message';
4
4
  import { Label } from '../atoms/Label';
5
5
  import { InputContainer } from '../atoms/InputContainer';
6
6
  import { cn } from '@boostdev/design-system-foundation';
7
+ import type { WithClassName } from '../../../../types';
7
8
 
8
- interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
9
+ interface RadioProps extends WithClassName, Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
9
10
  label: ReactNode;
10
11
  name: string;
11
12
  description?: string;
12
13
  error?: string;
13
14
  hint?: string;
14
- className?: string;
15
15
  }
16
16
 
17
17
  export function Radio({ label, name, description, error, hint, className, ...props }: RadioProps) {
@@ -0,0 +1,45 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './RadioGroup.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # RadioGroup
7
+
8
+ Groups multiple `Radio` inputs under a shared label. Renders a `<fieldset>` with a `<legend>` for accessible grouping.
9
+
10
+ ## When to use
11
+ - Mutually exclusive choices where exactly one option must be selected
12
+ - Any time two or more radio buttons share a common question or label
13
+
14
+ ## When not to use
15
+ - More than 6 options — use `Select` or `Combobox` to avoid overwhelming the user
16
+ - Independent on/off choices — use `CheckboxGroup`
17
+
18
+ ## Examples
19
+
20
+ ### Default
21
+ <Canvas of={Stories.Default} />
22
+
23
+ ### With error
24
+ <Canvas of={Stories.WithError} />
25
+
26
+ ### With hint
27
+ <Canvas of={Stories.WithHint} />
28
+
29
+ ### Required
30
+ <Canvas of={Stories.Required} />
31
+
32
+ ### Disabled
33
+ <Canvas of={Stories.Disabled} />
34
+
35
+ ## Props
36
+
37
+ <ArgTypes of={Stories} />
38
+
39
+ ## Accessibility
40
+
41
+ - Wraps radio buttons in a `<fieldset>` with a `<legend>` — screen readers announce the group label before each option
42
+ - `disabled` on the group sets the HTML `disabled` attribute on the `<fieldset>`, which disables all child inputs
43
+ - `aria-required` is set on the `<fieldset>` when `required` is true
44
+ - Error and hint messages are linked via `aria-describedby`
45
+ - Arrow keys move between options within the group; `Tab` moves focus out of the group
@@ -3,7 +3,7 @@ import { Radio } from '../Radio';
3
3
  import { RadioGroup } from './RadioGroup';
4
4
 
5
5
  const meta = {
6
- title: 'Form/RadioGroup',
6
+ title: 'React/Form/RadioGroup',
7
7
  component: RadioGroup,
8
8
  } satisfies Meta<typeof RadioGroup>;
9
9
 
@@ -2,15 +2,15 @@ import { ReactNode, useId } from 'react';
2
2
  import css from './RadioGroup.module.css';
3
3
  import { Message } from '../atoms/Message';
4
4
  import { cn } from '@boostdev/design-system-foundation';
5
+ import type { WithClassName } from '../../../../types';
5
6
 
6
- export interface RadioGroupProps {
7
+ export interface RadioGroupProps extends WithClassName {
7
8
  legend: string;
8
9
  children: ReactNode;
9
10
  error?: string;
10
11
  hint?: string;
11
12
  required?: boolean;
12
13
  disabled?: boolean;
13
- className?: string;
14
14
  }
15
15
 
16
16
  export function RadioGroup({
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { SegmentedControl } from './SegmentedControl';
4
4
 
5
5
  const meta = {
6
- title: 'Form/SegmentedControl',
6
+ title: 'React/Form/SegmentedControl',
7
7
  component: SegmentedControl,
8
8
  argTypes: {
9
9
  size: { control: 'radio', options: ['small', 'medium', 'large'] },
@@ -1,6 +1,7 @@
1
1
  import { InputHTMLAttributes, ReactNode, useId, useState } from 'react';
2
2
  import css from './SegmentedControl.module.css';
3
3
  import { cn } from '@boostdev/design-system-foundation';
4
+ import type { WithClassName } from '../../../../types';
4
5
 
5
6
  export interface SegmentedControlOption {
6
7
  value: string;
@@ -9,7 +10,7 @@ export interface SegmentedControlOption {
9
10
  }
10
11
 
11
12
  export interface SegmentedControlProps
12
- extends Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value' | 'size'> {
13
+ extends WithClassName, Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value' | 'size'> {
13
14
  name: string;
14
15
  options: SegmentedControlOption[];
15
16
  /** Controlled selected value */
@@ -19,7 +20,6 @@ export interface SegmentedControlProps
19
20
  onChange?: (value: string) => void;
20
21
  disabled?: boolean;
21
22
  size?: 'small' | 'medium' | 'large';
22
- className?: string;
23
23
  'aria-label'?: string;
24
24
  }
25
25
 
@@ -9,7 +9,7 @@ const options = [
9
9
  ];
10
10
 
11
11
  const meta = {
12
- title: 'Form/Select',
12
+ title: 'React/Form/Select',
13
13
  component: Select,
14
14
  } satisfies Meta<typeof Select>;
15
15
 
@@ -4,6 +4,7 @@ import { cn } from '@boostdev/design-system-foundation';
4
4
  import { InputContainer } from '../atoms/InputContainer';
5
5
  import { Label } from '../atoms/Label';
6
6
  import { Message } from '../atoms/Message';
7
+ import type { WithClassName } from '../../../../types';
7
8
 
8
9
  export interface SelectOption {
9
10
  value: string;
@@ -11,14 +12,13 @@ export interface SelectOption {
11
12
  disabled?: boolean;
12
13
  }
13
14
 
14
- interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'children'> {
15
+ interface SelectProps extends WithClassName, Omit<SelectHTMLAttributes<HTMLSelectElement>, 'children'> {
15
16
  label: ReactNode;
16
17
  name: string;
17
18
  options: SelectOption[];
18
19
  placeholder?: string;
19
20
  error?: string;
20
21
  hint?: string;
21
- className?: string;
22
22
  }
23
23
 
24
24
  export function Select({
@@ -46,7 +46,7 @@ Range input for selecting a numeric value within a bounded range.
46
46
  <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
47
47
  </thead>
48
48
  <tbody>
49
- <tr><td>`--slider_color-active`</td><td>`var(--bds-active)`</td><td>Filled track and thumb colour</td></tr>
49
+ <tr><td>`--slider_color-active`</td><td>`var(--bdc-color_active)`</td><td>Filled track and thumb colour</td></tr>
50
50
  </tbody>
51
51
  </table>
52
52
 
@@ -29,8 +29,8 @@
29
29
  border-radius: 999px;
30
30
  background: linear-gradient(
31
31
  to right,
32
- var(--slider_color-active, var(--bds-color_green)) 0%,
33
- var(--slider_color-active, var(--bds-color_green)) var(--slider_fill),
32
+ var(--slider_color-active, var(--bdc-color_active)) 0%,
33
+ var(--slider_color-active, var(--bdc-color_active)) var(--slider_fill),
34
34
  var(--bds-color_bg--subtle) var(--slider_fill),
35
35
  var(--bds-color_bg--subtle) 100%
36
36
  );
@@ -55,7 +55,7 @@
55
55
  width: var(--slider_thumb-size);
56
56
  height: var(--slider_thumb-size);
57
57
  border-radius: 50%;
58
- background-color: var(--slider_color-active, var(--bds-color_green));
58
+ background-color: var(--slider_color-active, var(--bdc-color_active));
59
59
  box-shadow: var(--bds-shadow_s);
60
60
  transition: var(--bds-animation_transition);
61
61
  cursor: pointer;
@@ -67,7 +67,7 @@
67
67
  height: var(--slider_thumb-size);
68
68
  border: none;
69
69
  border-radius: 50%;
70
- background-color: var(--slider_color-active, var(--bds-color_green));
70
+ background-color: var(--slider_color-active, var(--bdc-color_active));
71
71
  box-shadow: var(--bds-shadow_s);
72
72
  cursor: pointer;
73
73
  }
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { Slider } from './Slider';
3
3
 
4
4
  const meta = {
5
- title: 'Form/Slider',
5
+ title: 'React/Form/Slider',
6
6
  component: Slider,
7
7
  } satisfies Meta<typeof Slider>;
8
8
 
@@ -4,8 +4,9 @@ import { cn } from '@boostdev/design-system-foundation';
4
4
  import { InputContainer } from '../atoms/InputContainer';
5
5
  import { Label } from '../atoms/Label';
6
6
  import { Message } from '../atoms/Message';
7
+ import type { WithClassName } from '../../../../types';
7
8
 
8
- interface SliderProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
9
+ interface SliderProps extends WithClassName, Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
9
10
  label: string;
10
11
  name: string;
11
12
  min?: number;
@@ -13,7 +14,6 @@ interface SliderProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'
13
14
  showValue?: boolean;
14
15
  error?: string;
15
16
  hint?: string;
16
- className?: string;
17
17
  }
18
18
 
19
19
  export function Slider({