@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
@@ -1,6 +1,7 @@
1
1
  import { ReactNode } from 'react';
2
2
  import css from './Table.module.css';
3
3
  import { cn } from '@boostdev/design-system-foundation';
4
+ import type { WithClassName } from '../../../types';
4
5
 
5
6
  export interface TableColumn<Row> {
6
7
  key: string;
@@ -9,14 +10,13 @@ export interface TableColumn<Row> {
9
10
  render?: (row: Row) => ReactNode;
10
11
  }
11
12
 
12
- interface TableProps<Row extends Record<string, unknown>> {
13
+ interface TableProps<Row extends Record<string, unknown>> extends WithClassName {
13
14
  columns: TableColumn<Row>[];
14
15
  rows: Row[];
15
16
  caption?: string;
16
17
  sortKey?: string;
17
18
  sortDirection?: 'asc' | 'desc';
18
19
  onSort?: (key: string, direction: 'asc' | 'desc') => void;
19
- className?: string;
20
20
  }
21
21
 
22
22
  export function Table<Row extends Record<string, unknown>>({
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { Tabs } from './Tabs';
3
3
 
4
4
  const meta = {
5
- title: 'UI/Tabs',
5
+ title: 'React/UI/Tabs',
6
6
  component: Tabs,
7
7
  } satisfies Meta<typeof Tabs>;
8
8
 
@@ -1,6 +1,7 @@
1
1
  import { KeyboardEvent, ReactNode, useId, useRef, useState } from 'react';
2
2
  import css from './Tabs.module.css';
3
3
  import { cn } from '@boostdev/design-system-foundation';
4
+ import type { WithClassName } from '../../../types';
4
5
 
5
6
  export interface TabItem {
6
7
  id: string;
@@ -9,10 +10,9 @@ export interface TabItem {
9
10
  disabled?: boolean;
10
11
  }
11
12
 
12
- interface TabsProps {
13
+ interface TabsProps extends WithClassName {
13
14
  tabs: TabItem[];
14
15
  defaultTab?: string;
15
- className?: string;
16
16
  }
17
17
 
18
18
  export function Tabs({ tabs, defaultTab, className }: Readonly<TabsProps>) {
@@ -36,6 +36,20 @@ Short contextual label that appears on hover or focus. Use to supplement icon bu
36
36
 
37
37
  <ArgTypes of={Stories} />
38
38
 
39
+ ## CSS variables
40
+
41
+
42
+ <table>
43
+ <thead>
44
+ <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
45
+ </thead>
46
+ <tbody>
47
+ <tr><td>`--tooltip_bg`</td><td>`var(--bds-color_on-bg)`</td><td>Tooltip background colour</td></tr>
48
+ <tr><td>`--tooltip_text`</td><td>`var(--bds-color_bg)`</td><td>Tooltip text colour</td></tr>
49
+ </tbody>
50
+ </table>
51
+
52
+
39
53
  ## Accessibility
40
54
 
41
55
  - Tooltip content is linked via `aria-describedby` on the trigger element
@@ -3,7 +3,7 @@ import { Tooltip } from './Tooltip';
3
3
  import { Button } from '../../interaction/Button/Button';
4
4
 
5
5
  const meta = {
6
- title: 'UI/Tooltip',
6
+ title: 'React/UI/Tooltip',
7
7
  component: Tooltip,
8
8
  argTypes: {
9
9
  placement: { control: 'radio', options: ['top', 'bottom', 'left', 'right'] },
@@ -1,12 +1,12 @@
1
1
  import { ReactElement, ReactNode, cloneElement, isValidElement, useId, useState } from 'react';
2
2
  import css from './Tooltip.module.css';
3
3
  import { cn } from '@boostdev/design-system-foundation';
4
+ import type { WithClassName } from '../../../types';
4
5
 
5
- interface TooltipProps {
6
+ interface TooltipProps extends WithClassName {
6
7
  content: ReactNode;
7
8
  placement?: 'top' | 'bottom' | 'left' | 'right';
8
9
  children: ReactElement;
9
- className?: string;
10
10
  }
11
11
 
12
12
  export function Tooltip({
@@ -35,6 +35,19 @@ Renders a text element with a design-system variant. Decouples visual style from
35
35
 
36
36
  <ArgTypes of={Stories} />
37
37
 
38
+ ## CSS variables
39
+
40
+
41
+ <table>
42
+ <thead>
43
+ <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
44
+ </thead>
45
+ <tbody>
46
+ <tr><td>`--typography_color`</td><td>`var(--bds-color_on-bg)`</td><td>Text colour override</td></tr>
47
+ </tbody>
48
+ </table>
49
+
50
+
38
51
  ## Accessibility
39
52
 
40
53
  - Defaults to the semantically correct element for each variant (`h1`–`h3`, `p`)
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { ThemeProvider } from '../../../native/ThemeContext';
4
+ import { Typography } from './Typography.native';
5
+
6
+ const meta = {
7
+ tags: ['!stable', 'alpha'],
8
+ title: 'React Native/UI/Typography',
9
+ component: Typography,
10
+ decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
11
+ parameters: { layout: 'padded' },
12
+ argTypes: {
13
+ variant: { control: 'select', options: ['h1', 'h2', 'h3', 'body', 'body_s'] },
14
+ },
15
+ } satisfies Meta<typeof Typography>;
16
+
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const Heading1: Story = { args: { children: 'Heading 1', variant: 'h1' } };
21
+ export const Heading2: Story = { args: { children: 'Heading 2', variant: 'h2' } };
22
+ export const Heading3: Story = { args: { children: 'Heading 3', variant: 'h3' } };
23
+ export const Body: Story = { args: { children: 'The quick brown fox jumps over the lazy dog.', variant: 'body' } };
24
+ export const BodySmall: Story = { args: { children: 'The quick brown fox jumps over the lazy dog.', variant: 'body_s' } };
25
+ export const AllVariants: Story = {
26
+ args: { children: '' },
27
+ render: () => (
28
+ <ThemeProvider>
29
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
30
+ <Typography variant="h1">Heading 1</Typography>
31
+ <Typography variant="h2">Heading 2</Typography>
32
+ <Typography variant="h3">Heading 3</Typography>
33
+ <Typography variant="body">Body — The quick brown fox jumps over the lazy dog.</Typography>
34
+ <Typography variant="body_s">Body Small — The quick brown fox jumps over the lazy dog.</Typography>
35
+ </div>
36
+ </ThemeProvider>
37
+ ),
38
+ };
@@ -0,0 +1,65 @@
1
+ import { ReactNode } from 'react';
2
+ import { StyleSheet, Text, StyleProp, TextStyle } from 'react-native';
3
+ import { font, lineHeight } from '../../../native/tokens';
4
+ import { useTheme } from '../../../native/ThemeContext';
5
+
6
+ type TypographyVariant = 'h1' | 'h2' | 'h3' | 'body' | 'body_s';
7
+
8
+ interface TypographyProps {
9
+ variant?: TypographyVariant;
10
+ children: ReactNode;
11
+ style?: StyleProp<TextStyle>;
12
+ }
13
+
14
+ const styles = StyleSheet.create({
15
+ base: {
16
+ fontFamily: font.family.body,
17
+ fontWeight: font.weight.body,
18
+ },
19
+ h1: {
20
+ fontSize: font.size.heading1,
21
+ fontFamily: font.family.heading,
22
+ fontWeight: font.weight.heading,
23
+ lineHeight: lineHeight.heading1,
24
+ },
25
+ h2: {
26
+ fontSize: font.size.heading2,
27
+ fontFamily: font.family.heading,
28
+ fontWeight: font.weight.heading,
29
+ lineHeight: lineHeight.heading2,
30
+ },
31
+ h3: {
32
+ fontSize: font.size.heading3,
33
+ fontFamily: font.family.heading,
34
+ fontWeight: font.weight.heading,
35
+ lineHeight: lineHeight.heading3,
36
+ },
37
+ body: {
38
+ fontSize: font.size.body,
39
+ lineHeight: lineHeight.body,
40
+ },
41
+ body_s: {
42
+ fontSize: font.size.bodyS,
43
+ lineHeight: lineHeight.bodyS,
44
+ },
45
+ });
46
+
47
+ const accessibilityRoles: Record<TypographyVariant, 'header' | 'text'> = {
48
+ h1: 'header',
49
+ h2: 'header',
50
+ h3: 'header',
51
+ body: 'text',
52
+ body_s: 'text',
53
+ };
54
+
55
+ export function Typography({ variant = 'body', children, style }: TypographyProps) {
56
+ const { colors } = useTheme();
57
+ return (
58
+ <Text
59
+ accessibilityRole={accessibilityRoles[variant]}
60
+ style={[styles.base, styles[variant], { color: colors.colorOnBg }, style]}
61
+ >
62
+ {children}
63
+ </Text>
64
+ );
65
+ }
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { Typography } from './Typography';
3
3
 
4
4
  const meta = {
5
- title: 'UI/Typography',
5
+ title: 'React/UI/Typography',
6
6
  component: Typography,
7
7
  argTypes: {
8
8
  variant: { control: 'select', options: ['h1', 'h2', 'h3', 'body', 'body_s'] },
@@ -1,14 +1,14 @@
1
1
  import { ReactNode, ElementType } from 'react';
2
2
  import css from './Typography.module.css';
3
3
  import { cn } from '@boostdev/design-system-foundation';
4
+ import type { WithClassName } from '../../../types';
4
5
 
5
6
  type TypographyVariant = 'h1' | 'h2' | 'h3' | 'body' | 'body_s';
6
7
 
7
- interface TypographyProps {
8
+ interface TypographyProps extends WithClassName {
8
9
  variant?: TypographyVariant;
9
10
  component?: ElementType;
10
11
  children: ReactNode;
11
- className?: string;
12
12
  }
13
13
 
14
14
  const variantToElement: Record<TypographyVariant, ElementType> = {
package/src/css/bdc.css CHANGED
@@ -55,6 +55,14 @@
55
55
  --bdc_color--focus: var(--bds-color_interactive);
56
56
  --bdc_color--error: var(--bds-color_error);
57
57
 
58
+ /* Active / selected state colours — override these to theme all filled form controls globally */
59
+ --bdc-color_active: var(--bds-color_green);
60
+ --bdc-color_on-active: var(--bds-color_on-green);
61
+ --bdc-color_active--subtle: var(--bds-color_green--subtle);
62
+ --bdc-color_on-active--subtle: var(--bds-color_on-green--subtle);
63
+ --bdc-color_active--strong: var(--bds-color_green--strong);
64
+ --bdc-color_on-active--strong: var(--bds-color_on-green--strong);
65
+
58
66
  /* Outline geometry */
59
67
  --bdc-outline_width: 1px;
60
68
  --bdc-outline_offset: -1px; /* inset — appears inside the element like a traditional border */
package/src/index.ts CHANGED
@@ -73,3 +73,4 @@ export type { IconWrapperProps } from './components/layout/IconWrapper';
73
73
 
74
74
  // Utilities
75
75
  export { cn } from '@boostdev/design-system-foundation';
76
+ export type { WithClassName } from './types';
@@ -0,0 +1,28 @@
1
+ import { createContext, ReactNode, useContext, useMemo } from 'react';
2
+ import { useColorScheme } from 'react-native';
3
+ import { colors } from './tokens';
4
+
5
+ type ThemeColors = typeof colors.light | typeof colors.dark;
6
+
7
+ interface ThemeContextValue {
8
+ colors: ThemeColors;
9
+ isDark: boolean;
10
+ }
11
+
12
+ const ThemeContext = createContext<ThemeContextValue | null>(null);
13
+
14
+ export function ThemeProvider({ children }: { children: ReactNode }) {
15
+ const scheme = useColorScheme();
16
+ const isDark = scheme === 'dark';
17
+ const value = useMemo<ThemeContextValue>(
18
+ () => ({ colors: isDark ? colors.dark : colors.light, isDark }),
19
+ [isDark],
20
+ );
21
+ return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
22
+ }
23
+
24
+ export function useTheme(): ThemeContextValue {
25
+ const ctx = useContext(ThemeContext);
26
+ if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
27
+ return ctx;
28
+ }
@@ -0,0 +1,13 @@
1
+ import { colors, spacing, font, border, zIndex, animation } from '@boostdev/design-system-foundation/native';
2
+
3
+ export { colors, spacing, font, border, zIndex, animation };
4
+
5
+ /** Absolute lineHeight values for React Native (ratio × fontSize). */
6
+ export const lineHeight = {
7
+ display: Math.round(font.size.display * font.lineHeight.display),
8
+ heading1: Math.round(font.size.heading1 * font.lineHeight.heading),
9
+ heading2: Math.round(font.size.heading2 * font.lineHeight.heading),
10
+ heading3: Math.round(font.size.heading3 * font.lineHeight.heading),
11
+ body: Math.round(font.size.body * font.lineHeight.body),
12
+ bodyS: Math.round(font.size.bodyS * font.lineHeight.body),
13
+ } as const;
package/src/native.ts ADDED
@@ -0,0 +1,39 @@
1
+ // React Native component barrel
2
+ // Explicitly imports *.native.tsx variants so tsup compiles them into dist/native/index.js.
3
+ // Metro resolves this via the "react-native" export condition in package.json.
4
+
5
+ // Infrastructure
6
+ export { ThemeProvider, useTheme } from './native/ThemeContext';
7
+
8
+ // UI
9
+ export { Typography } from './components/ui/Typography/Typography.native';
10
+ export { Badge } from './components/ui/Badge/Badge.native';
11
+ export { Avatar } from './components/ui/Avatar/Avatar.native';
12
+ export { Separator } from './components/ui/Separator/Separator.native';
13
+ export { Loading } from './components/ui/Loading/Loading.native';
14
+ export { Progress } from './components/ui/Progress/Progress.native';
15
+ export { Skeleton } from './components/ui/Skeleton/Skeleton.native';
16
+ export { Alert } from './components/ui/Alert/Alert.native';
17
+ export { NotificationBanner } from './components/ui/NotificationBanner/NotificationBanner.native';
18
+
19
+ // Layout
20
+ export { Card } from './components/layout/Card/Card.native';
21
+ export { SectionHeader } from './components/layout/SectionHeader/SectionHeader.native';
22
+ export { IconWrapper } from './components/layout/IconWrapper/IconWrapper.native';
23
+ export type { IconWrapperProps } from './components/layout/IconWrapper/IconWrapper.native';
24
+ export { ButtonGroup } from './components/layout/ButtonGroup/ButtonGroup.native';
25
+ export type { ButtonGroupProps } from './components/layout/ButtonGroup/ButtonGroup.native';
26
+
27
+ // Interaction
28
+ export { Button } from './components/interaction/Button/Button.native';
29
+ export type { ButtonProps } from './components/interaction/Button/Button.native';
30
+ export { Switch } from './components/interaction/form/Switch/Switch.native';
31
+ export { Checkbox } from './components/interaction/form/Checkbox/Checkbox.native';
32
+ export { Radio } from './components/interaction/form/Radio/Radio.native';
33
+
34
+ // Atoms
35
+ export { Label } from './components/interaction/form/atoms/Label.native';
36
+ export { Message } from './components/interaction/form/atoms/Message.native';
37
+
38
+ // Tokens (re-exported for convenience)
39
+ export { colors, spacing, font, border, zIndex, animation, lineHeight } from './native/tokens';
@@ -0,0 +1,13 @@
1
+ // This file MUST be a module (not a global script) so that `declare module 'react'`
2
+ // is treated as an augmentation that merges with @types/react rather than as an
3
+ // ambient module declaration that would shadow @types/react entirely.
4
+ // The `export {}` below is what makes TypeScript treat this as a module.
5
+ export {};
6
+
7
+ // CSS Anchor Positioning properties — not yet in React's CSSProperties types.
8
+ declare module 'react' {
9
+ interface CSSProperties {
10
+ anchorName?: string;
11
+ positionAnchor?: string;
12
+ }
13
+ }
@@ -0,0 +1,130 @@
1
+ import { Meta } from '@storybook/blocks';
2
+
3
+ <Meta title="Design System/Dark Mode" />
4
+
5
+ # Dark Mode
6
+
7
+ All components adapt to dark mode automatically. No extra CSS imports or component changes are needed — switching the theme updates every token at once.
8
+
9
+ ## How it works
10
+
11
+ The design system foundation ships two sets of semantic tokens: one for light mode and one for dark mode. When dark mode is active, the `--bds-color_*` tokens remap to their dark counterparts. Because every component is built on these tokens, the entire UI responds in a single cascade update.
12
+
13
+ ## Enabling dark mode
14
+
15
+ ### Via `data-theme` attribute (recommended)
16
+
17
+ Set `data-theme="dark"` on a container element — typically `<html>` for a global switch, or any wrapper for a scoped one.
18
+
19
+ ```html
20
+ <!-- global dark mode -->
21
+ <html data-theme="dark">
22
+
23
+ <!-- scoped dark mode (only this subtree) -->
24
+ <div data-theme="dark">
25
+ <Button>Dark button</Button>
26
+ </div>
27
+ ```
28
+
29
+ Remove the attribute (or set `data-theme="light"`) to return to light mode.
30
+
31
+ ### Via `prefers-color-scheme` (automatic)
32
+
33
+ No attribute needed. When a user has dark mode enabled at the OS level, the design system tokens switch automatically:
34
+
35
+ ```css
36
+ /* this is built into @boostdev/design-system-foundation — nothing to add */
37
+ @media (prefers-color-scheme: dark) {
38
+ :root { /* dark token values */ }
39
+ }
40
+ ```
41
+
42
+ `data-theme` always takes precedence over `prefers-color-scheme`, so you can override the system preference.
43
+
44
+ ## Building a theme toggle
45
+
46
+ A minimal React hook that syncs with the system preference and lets the user override it:
47
+
48
+ ```tsx
49
+ import { useEffect, useState } from 'react';
50
+
51
+ type Theme = 'light' | 'dark';
52
+
53
+ function getSystemTheme(): Theme {
54
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
55
+ }
56
+
57
+ export function useTheme() {
58
+ const [theme, setTheme] = useState<Theme>(
59
+ () => (localStorage.getItem('theme') as Theme) ?? getSystemTheme()
60
+ );
61
+
62
+ useEffect(() => {
63
+ document.documentElement.setAttribute('data-theme', theme);
64
+ localStorage.setItem('theme', theme);
65
+ }, [theme]);
66
+
67
+ const toggle = () => setTheme(t => (t === 'dark' ? 'light' : 'dark'));
68
+
69
+ return { theme, toggle };
70
+ }
71
+ ```
72
+
73
+ Usage:
74
+
75
+ ```tsx
76
+ function ThemeToggle() {
77
+ const { theme, toggle } = useTheme();
78
+ return (
79
+ <Button onClick={toggle}>
80
+ {theme === 'dark' ? 'Switch to light' : 'Switch to dark'}
81
+ </Button>
82
+ );
83
+ }
84
+ ```
85
+
86
+ ## Customising tokens in dark mode
87
+
88
+ Override any token in `@layer tokens.override` — the rule applies regardless of the active theme unless you scope it:
89
+
90
+ ```css
91
+ @import "@boostdev/design-system-foundation/css";
92
+
93
+ /* override for all themes */
94
+ @layer tokens.override {
95
+ :root {
96
+ --bds-color_green: oklch(60% 0.15 145);
97
+ }
98
+ }
99
+
100
+ /* override for dark mode only */
101
+ @layer tokens.override {
102
+ [data-theme="dark"],
103
+ @media (prefers-color-scheme: dark) {
104
+ :root:not([data-theme="light"]) {
105
+ --bds-color_green: oklch(75% 0.15 145);
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## Component-level dark mode overrides
112
+
113
+ Use component CSS variables to restyle a specific component in dark mode without touching global tokens:
114
+
115
+ ```css
116
+ /* make all Tooltips use a high-contrast dark style in both themes */
117
+ .my-app {
118
+ --tooltip_bg: #1a1a1a;
119
+ --tooltip_text: #f5f5f5;
120
+ }
121
+
122
+ /* only override in dark mode */
123
+ [data-theme="dark"] .my-card {
124
+ --card_color: #1e1e1e;
125
+ }
126
+ ```
127
+
128
+ ## Testing dark mode in Storybook
129
+
130
+ Use the **Theme** toolbar toggle in the top bar to switch between light and dark mode while browsing stories. This sets `data-theme` on the story canvas root.
package/src/types.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** Allows consumers to pass a CSS class to the component's root element. */
2
+ export type WithClassName = { className?: string | undefined };
package/src/typings.d.ts CHANGED
@@ -2,3 +2,6 @@ declare module '*.module.css' {
2
2
  const classes: Record<string, string>;
3
3
  export default classes;
4
4
  }
5
+
6
+ // CSS Anchor Positioning augmentation lives in react-augment.d.ts — see that
7
+ // file for why it must be a separate module file rather than declared here.
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Injects the bdc border-decoration @property rules into the document's adopted stylesheets.
3
+ *
4
+ * @property declarations must live at the document level — they cannot be scoped to a Shadow DOM.
5
+ * This module is imported once by src/web-components/index.ts and runs as a side effect.
6
+ */
7
+
8
+ const bdcCss = `
9
+ @property --bdc_color {
10
+ syntax: '<color>';
11
+ inherits: false;
12
+ initial-value: currentcolor;
13
+ }
14
+
15
+ @property --bdc-outline_width {
16
+ syntax: '<length>';
17
+ inherits: false;
18
+ initial-value: 1px;
19
+ }
20
+
21
+ @property --bdc-outline_offset {
22
+ syntax: '<length>';
23
+ inherits: false;
24
+ initial-value: -1px;
25
+ }
26
+
27
+ @property --bdc-outline_radius {
28
+ syntax: '<length-percentage>';
29
+ inherits: false;
30
+ initial-value: 0;
31
+ }
32
+
33
+ @property --bdc-outline_shadow {
34
+ syntax: '*';
35
+ inherits: false;
36
+ initial-value: none;
37
+ }
38
+
39
+ @layer component {
40
+ :root {
41
+ --bdc-outline_border: none;
42
+
43
+ --bdc_color: currentcolor;
44
+ --bdc_color--focus: var(--bds-color_interactive);
45
+ --bdc_color--error: var(--bds-color_error);
46
+
47
+ --bdc-outline_width: 1px;
48
+ --bdc-outline_offset: -1px;
49
+ --bdc-outline_radius: var(--bds-border_radius--xs);
50
+ --bdc-outline_shadow: none;
51
+ }
52
+ }
53
+ `;
54
+
55
+ // Guard against double-injection during hot-reload or test re-evaluation.
56
+ if (!('__bdcInjected' in document)) {
57
+ Object.defineProperty(document, '__bdcInjected', { value: true });
58
+ const sheet = new CSSStyleSheet();
59
+ sheet.replaceSync(bdcCss);
60
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
61
+ }
@@ -0,0 +1,12 @@
1
+ // Inject global bdc @property declarations into the document.
2
+ // Must run before any component that uses --bdc_color / --bdc-outline_* tokens.
3
+ import './globals';
4
+
5
+ // Phase 1 — proof of concept components
6
+ export { BdsBadge } from './ui/bds-badge';
7
+ export { BdsButton } from './interaction/bds-button';
8
+ export { BdsAlert } from './ui/bds-alert';
9
+
10
+ // Phase 2 — layout-driven components
11
+ export { BdsPopover } from './interaction/bds-popover';
12
+ export { BdsToastProvider } from './interaction/bds-toast-provider';
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import type { ButtonVariant, ButtonSize } from './bds-button';
4
+ import '../index'; // auto-registers all custom elements
5
+
6
+ // Thin React wrapper so Storybook controls work with the custom element's props
7
+ function BdsButton({
8
+ variant = 'default',
9
+ size = 'medium',
10
+ disabled,
11
+ href,
12
+ hasPulse,
13
+ children,
14
+ }: {
15
+ variant?: ButtonVariant;
16
+ size?: ButtonSize;
17
+ disabled?: boolean;
18
+ href?: string;
19
+ hasPulse?: boolean;
20
+ children?: React.ReactNode;
21
+ }) {
22
+ return React.createElement(
23
+ 'bds-button',
24
+ { variant, size, disabled: disabled || undefined, href, 'has-pulse': hasPulse || undefined },
25
+ children,
26
+ );
27
+ }
28
+
29
+ const meta = {
30
+ title: 'Web Components/Interaction/Button',
31
+ component: BdsButton,
32
+ tags: ['!stable', 'experimental'],
33
+ parameters: { layout: 'centered' },
34
+ argTypes: {
35
+ variant: { control: 'select', options: ['default', 'ghost'] },
36
+ size: { control: 'select', options: ['small', 'medium', 'large'] },
37
+ },
38
+ } satisfies Meta<typeof BdsButton>;
39
+
40
+ export default meta;
41
+ type Story = StoryObj<typeof meta>;
42
+
43
+ export const Default: Story = { args: { children: 'Default', variant: 'default' } };
44
+ export const Ghost: Story = { args: { children: 'Ghost', variant: 'ghost' } };
45
+ export const Small: Story = { args: { children: 'Small', size: 'small' } };
46
+ export const Medium: Story = { args: { children: 'Medium', size: 'medium' } };
47
+ export const Large: Story = { args: { children: 'Large', size: 'large' } };
48
+ export const WithPulse: Story = { args: { children: 'Pulsing', hasPulse: true } };
49
+ export const Disabled: Story = { args: { children: 'Disabled', disabled: true } };
50
+ export const AllVariants: Story = {
51
+ render: () => (
52
+ <div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap', alignItems: 'center' }}>
53
+ {(['default', 'ghost'] as const).flatMap(v =>
54
+ (['small', 'medium', 'large'] as const).map(s => (
55
+ <BdsButton key={`${v}-${s}`} variant={v} size={s}>{v} {s}</BdsButton>
56
+ )),
57
+ )}
58
+ </div>
59
+ ),
60
+ };