@boostdev/design-system-components 1.0.3 → 1.1.1
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.
- package/AGENTS.md +51 -10
- package/dist/client.cjs +350 -174
- package/dist/client.css +751 -647
- package/dist/client.d.cts +62 -97
- package/dist/client.d.ts +62 -97
- package/dist/client.js +377 -211
- package/dist/index.cjs +350 -174
- package/dist/index.css +751 -647
- package/dist/index.d.cts +62 -97
- package/dist/index.d.ts +62 -97
- package/dist/index.js +377 -211
- package/dist/native/index.cjs +991 -0
- package/dist/native/index.d.cts +208 -0
- package/dist/native/index.d.ts +208 -0
- package/dist/native/index.js +968 -0
- package/dist/web-components.d.ts +304 -0
- package/dist/web-components.js +1978 -0
- package/package.json +27 -5
- package/src/components/interaction/Button/Button.module.css +11 -8
- package/src/components/interaction/Button/Button.native.mdx +72 -0
- package/src/components/interaction/Button/Button.native.spec.tsx +35 -0
- package/src/components/interaction/Button/Button.native.stories.tsx +42 -0
- package/src/components/interaction/Button/Button.native.tsx +95 -0
- package/src/components/interaction/Button/Button.stories.tsx +1 -1
- package/src/components/interaction/Button/Button.tsx +2 -2
- package/src/components/interaction/Command/Command.mdx +16 -0
- package/src/components/interaction/Command/Command.module.css +7 -7
- package/src/components/interaction/Command/Command.spec.tsx +37 -0
- package/src/components/interaction/Command/Command.stories.tsx +1 -1
- package/src/components/interaction/Command/Command.tsx +39 -4
- package/src/components/interaction/Dialog/Dialog.module.css +10 -13
- package/src/components/interaction/Dialog/Dialog.spec.tsx +53 -1
- package/src/components/interaction/Dialog/Dialog.stories.tsx +18 -1
- package/src/components/interaction/Dialog/Dialog.tsx +52 -18
- package/src/components/interaction/Drawer/Drawer.mdx +14 -0
- package/src/components/interaction/Drawer/Drawer.module.css +11 -11
- package/src/components/interaction/Drawer/Drawer.spec.tsx +48 -1
- package/src/components/interaction/Drawer/Drawer.stories.tsx +1 -1
- package/src/components/interaction/Drawer/Drawer.tsx +40 -6
- package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +6 -6
- package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +1 -1
- package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +2 -2
- package/src/components/interaction/Popover/Popover.mdx +17 -0
- package/src/components/interaction/Popover/Popover.module.css +132 -16
- package/src/components/interaction/Popover/Popover.spec.tsx +119 -20
- package/src/components/interaction/Popover/Popover.stories.tsx +1 -1
- package/src/components/interaction/Popover/Popover.tsx +69 -29
- package/src/components/interaction/Rating/Rating.stories.tsx +1 -1
- package/src/components/interaction/Rating/Rating.tsx +2 -2
- package/src/components/interaction/Toast/Toast.module.css +13 -8
- package/src/components/interaction/Toast/Toast.spec.tsx +21 -0
- package/src/components/interaction/Toast/Toast.stories.tsx +1 -1
- package/src/components/interaction/Toast/Toast.tsx +22 -2
- package/src/components/interaction/form/Checkbox/Checkbox.mdx +2 -2
- package/src/components/interaction/form/Checkbox/Checkbox.module.css +9 -9
- package/src/components/interaction/form/Checkbox/Checkbox.native.mdx +74 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.spec.tsx +42 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +36 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +80 -0
- package/src/components/interaction/form/Checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/interaction/form/Checkbox/Checkbox.tsx +2 -2
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +44 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.stories.tsx +1 -1
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +2 -2
- package/src/components/interaction/form/Combobox/Combobox.mdx +21 -0
- package/src/components/interaction/form/Combobox/Combobox.module.css +8 -9
- package/src/components/interaction/form/Combobox/Combobox.stories.tsx +1 -1
- package/src/components/interaction/form/Combobox/Combobox.tsx +2 -2
- package/src/components/interaction/form/FileInput/FileInput.mdx +14 -0
- package/src/components/interaction/form/FileInput/FileInput.module.css +5 -5
- package/src/components/interaction/form/FileInput/FileInput.stories.tsx +1 -1
- package/src/components/interaction/form/FileInput/FileInput.tsx +2 -2
- package/src/components/interaction/form/FormInput/FormInput.stories.tsx +1 -1
- package/src/components/interaction/form/FormInput/FormInput.tsx +2 -2
- package/src/components/interaction/form/NumberInput/NumberInput.mdx +16 -0
- package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -1
- package/src/components/interaction/form/NumberInput/NumberInput.stories.tsx +1 -1
- package/src/components/interaction/form/NumberInput/NumberInput.tsx +2 -2
- package/src/components/interaction/form/Radio/Radio.mdx +3 -3
- package/src/components/interaction/form/Radio/Radio.module.css +9 -9
- package/src/components/interaction/form/Radio/Radio.native.stories.tsx +46 -0
- package/src/components/interaction/form/Radio/Radio.native.tsx +79 -0
- package/src/components/interaction/form/Radio/Radio.stories.tsx +1 -1
- package/src/components/interaction/form/Radio/Radio.tsx +2 -2
- package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +45 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.stories.tsx +1 -1
- package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +2 -2
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +5 -6
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.stories.tsx +1 -1
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +2 -2
- package/src/components/interaction/form/Select/Select.module.css +3 -3
- package/src/components/interaction/form/Select/Select.stories.tsx +1 -1
- package/src/components/interaction/form/Select/Select.tsx +2 -2
- package/src/components/interaction/form/Slider/Slider.mdx +1 -1
- package/src/components/interaction/form/Slider/Slider.module.css +10 -10
- package/src/components/interaction/form/Slider/Slider.stories.tsx +1 -1
- package/src/components/interaction/form/Slider/Slider.tsx +2 -2
- package/src/components/interaction/form/Switch/Switch.mdx +4 -4
- package/src/components/interaction/form/Switch/Switch.module.css +11 -11
- package/src/components/interaction/form/Switch/Switch.native.spec.tsx +60 -0
- package/src/components/interaction/form/Switch/Switch.native.stories.tsx +35 -0
- package/src/components/interaction/form/Switch/Switch.native.tsx +59 -0
- package/src/components/interaction/form/Switch/Switch.stories.tsx +1 -1
- package/src/components/interaction/form/Switch/Switch.tsx +2 -2
- package/src/components/interaction/form/Textarea/Textarea.mdx +17 -0
- package/src/components/interaction/form/Textarea/Textarea.module.css +1 -1
- package/src/components/interaction/form/Textarea/Textarea.stories.tsx +1 -1
- package/src/components/interaction/form/Textarea/Textarea.tsx +2 -2
- package/src/components/interaction/form/atoms/InputContainer.tsx +2 -1
- package/src/components/interaction/form/atoms/Label.native.stories.tsx +18 -0
- package/src/components/interaction/form/atoms/Label.native.tsx +29 -0
- package/src/components/interaction/form/atoms/Label.tsx +5 -3
- package/src/components/interaction/form/atoms/Message.native.spec.tsx +30 -0
- package/src/components/interaction/form/atoms/Message.native.stories.tsx +22 -0
- package/src/components/interaction/form/atoms/Message.native.tsx +34 -0
- package/src/components/interaction/form/atoms/Message.tsx +5 -3
- package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +50 -0
- package/src/components/layout/ButtonGroup/ButtonGroup.native.tsx +34 -0
- package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +1 -1
- package/src/components/layout/ButtonGroup/ButtonGroup.tsx +2 -2
- package/src/components/layout/Card/Card.native.stories.tsx +53 -0
- package/src/components/layout/Card/Card.native.tsx +89 -0
- package/src/components/layout/Card/Card.stories.tsx +1 -1
- package/src/components/layout/Card/Card.tsx +2 -2
- package/src/components/layout/IconWrapper/IconWrapper.mdx +2 -2
- package/src/components/layout/IconWrapper/IconWrapper.module.css +2 -2
- package/src/components/layout/IconWrapper/IconWrapper.native.spec.tsx +39 -0
- package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +41 -0
- package/src/components/layout/IconWrapper/IconWrapper.native.tsx +20 -0
- package/src/components/layout/IconWrapper/IconWrapper.stories.tsx +1 -1
- package/src/components/layout/IconWrapper/IconWrapper.tsx +2 -2
- package/src/components/layout/SectionHeader/SectionHeader.mdx +13 -0
- package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +38 -0
- package/src/components/layout/SectionHeader/SectionHeader.native.tsx +79 -0
- package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +1 -1
- package/src/components/layout/SectionHeader/SectionHeader.tsx +2 -2
- package/src/components/ui/Accordion/Accordion.mdx +14 -0
- package/src/components/ui/Accordion/Accordion.module.css +5 -5
- package/src/components/ui/Accordion/Accordion.stories.tsx +1 -1
- package/src/components/ui/Accordion/Accordion.tsx +2 -2
- package/src/components/ui/Alert/Alert.native.mdx +62 -0
- package/src/components/ui/Alert/Alert.native.stories.tsx +43 -0
- package/src/components/ui/Alert/Alert.native.tsx +94 -0
- package/src/components/ui/Alert/Alert.stories.tsx +1 -1
- package/src/components/ui/Alert/Alert.tsx +2 -2
- package/src/components/ui/Avatar/Avatar.native.mdx +50 -0
- package/src/components/ui/Avatar/Avatar.native.spec.tsx +47 -0
- package/src/components/ui/Avatar/Avatar.native.stories.tsx +34 -0
- package/src/components/ui/Avatar/Avatar.native.tsx +85 -0
- package/src/components/ui/Avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/Avatar/Avatar.tsx +2 -2
- package/src/components/ui/Badge/Badge.mdx +2 -2
- package/src/components/ui/Badge/Badge.native.mdx +54 -0
- package/src/components/ui/Badge/Badge.native.stories.tsx +36 -0
- package/src/components/ui/Badge/Badge.native.tsx +50 -0
- package/src/components/ui/Badge/Badge.stories.tsx +1 -1
- package/src/components/ui/Badge/Badge.tsx +2 -2
- package/src/components/ui/Breadcrumb/Breadcrumb.stories.tsx +1 -1
- package/src/components/ui/Breadcrumb/Breadcrumb.tsx +2 -2
- package/src/components/ui/Calendar/Calendar.mdx +16 -0
- package/src/components/ui/Calendar/Calendar.module.css +7 -7
- package/src/components/ui/Calendar/Calendar.stories.tsx +1 -1
- package/src/components/ui/Calendar/Calendar.tsx +2 -2
- package/src/components/ui/Carousel/Carousel.module.css +5 -5
- package/src/components/ui/Carousel/Carousel.stories.tsx +1 -1
- package/src/components/ui/Carousel/Carousel.tsx +2 -2
- package/src/components/ui/Collapsible/Collapsible.module.css +4 -4
- package/src/components/ui/Collapsible/Collapsible.stories.tsx +1 -1
- package/src/components/ui/Collapsible/Collapsible.tsx +2 -2
- package/src/components/ui/DescriptionList/DescriptionList.stories.tsx +1 -1
- package/src/components/ui/DescriptionList/DescriptionList.tsx +2 -2
- package/src/components/ui/Link/Link.mdx +14 -0
- package/src/components/ui/Link/Link.module.css +2 -2
- package/src/components/ui/Link/Link.stories.tsx +1 -1
- package/src/components/ui/Link/Link.tsx +2 -2
- package/src/components/ui/Loading/Loading.module.css +7 -7
- package/src/components/ui/Loading/Loading.native.spec.tsx +24 -0
- package/src/components/ui/Loading/Loading.native.stories.tsx +33 -0
- package/src/components/ui/Loading/Loading.native.tsx +29 -0
- package/src/components/ui/Loading/Loading.stories.tsx +1 -1
- package/src/components/ui/Loading/Loading.tsx +2 -2
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +3 -3
- package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +39 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +76 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +1 -1
- package/src/components/ui/NotificationBanner/NotificationBanner.tsx +2 -2
- package/src/components/ui/Pagination/Pagination.module.css +7 -7
- package/src/components/ui/Pagination/Pagination.stories.tsx +1 -1
- package/src/components/ui/Pagination/Pagination.tsx +2 -2
- package/src/components/ui/Progress/Progress.mdx +1 -1
- package/src/components/ui/Progress/Progress.module.css +6 -6
- package/src/components/ui/Progress/Progress.native.stories.tsx +34 -0
- package/src/components/ui/Progress/Progress.native.tsx +84 -0
- package/src/components/ui/Progress/Progress.stories.tsx +1 -1
- package/src/components/ui/Progress/Progress.tsx +2 -2
- package/src/components/ui/ProgressCircle/ProgressCircle.module.css +1 -1
- package/src/components/ui/ProgressCircle/ProgressCircle.stories.tsx +1 -1
- package/src/components/ui/ProgressCircle/ProgressCircle.tsx +2 -2
- package/src/components/ui/Separator/Separator.mdx +14 -0
- package/src/components/ui/Separator/Separator.module.css +3 -3
- package/src/components/ui/Separator/Separator.native.stories.tsx +42 -0
- package/src/components/ui/Separator/Separator.native.tsx +32 -0
- package/src/components/ui/Separator/Separator.stories.tsx +1 -1
- package/src/components/ui/Separator/Separator.tsx +2 -2
- package/src/components/ui/Skeleton/Skeleton.module.css +1 -1
- package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +33 -0
- package/src/components/ui/Skeleton/Skeleton.native.tsx +41 -0
- package/src/components/ui/Skeleton/Skeleton.stories.tsx +1 -1
- package/src/components/ui/Skeleton/Skeleton.tsx +2 -3
- package/src/components/ui/SkipLink/SkipLink.stories.tsx +1 -1
- package/src/components/ui/SkipLink/SkipLink.tsx +5 -3
- package/src/components/ui/Table/Table.mdx +14 -0
- package/src/components/ui/Table/Table.module.css +9 -9
- package/src/components/ui/Table/Table.stories.tsx +1 -1
- package/src/components/ui/Table/Table.tsx +2 -2
- package/src/components/ui/Tabs/Tabs.module.css +3 -3
- package/src/components/ui/Tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/Tabs/Tabs.tsx +2 -2
- package/src/components/ui/Tooltip/Tooltip.mdx +14 -0
- package/src/components/ui/Tooltip/Tooltip.module.css +8 -8
- package/src/components/ui/Tooltip/Tooltip.stories.tsx +1 -1
- package/src/components/ui/Tooltip/Tooltip.tsx +2 -2
- package/src/components/ui/Typography/Typography.mdx +13 -0
- package/src/components/ui/Typography/Typography.native.mdx +56 -0
- package/src/components/ui/Typography/Typography.native.stories.tsx +38 -0
- package/src/components/ui/Typography/Typography.native.tsx +65 -0
- package/src/components/ui/Typography/Typography.stories.tsx +1 -1
- package/src/components/ui/Typography/Typography.tsx +2 -2
- package/src/css/bdc.css +8 -0
- package/src/index.ts +1 -0
- package/src/native/ThemeContext.tsx +28 -0
- package/src/native/tokens.ts +13 -0
- package/src/native.ts +39 -0
- package/src/polyfill-invoker-commands.ts +68 -0
- package/src/react-augment.d.ts +32 -0
- package/src/stories/DesignSystem/DarkMode.mdx +130 -0
- package/src/stories/ReactNative.mdx +121 -0
- package/src/types.ts +2 -0
- package/src/typings.d.ts +3 -0
- package/src/web-components/globals.ts +61 -0
- package/src/web-components/index.ts +12 -0
- package/src/web-components/interaction/BdsButton.mdx +106 -0
- package/src/web-components/interaction/BdsButton.stories.tsx +60 -0
- package/src/web-components/interaction/BdsPopover.mdx +120 -0
- package/src/web-components/interaction/BdsPopover.stories.tsx +70 -0
- package/src/web-components/interaction/BdsToastProvider.mdx +94 -0
- package/src/web-components/interaction/BdsToastProvider.stories.tsx +73 -0
- package/src/web-components/interaction/bds-button.spec.ts +95 -0
- package/src/web-components/interaction/bds-button.ts +293 -0
- package/src/web-components/interaction/bds-popover.spec.ts +174 -0
- package/src/web-components/interaction/bds-popover.ts +229 -0
- package/src/web-components/interaction/bds-toast-provider.spec.ts +122 -0
- package/src/web-components/interaction/bds-toast-provider.ts +211 -0
- package/src/web-components/test/helpers.ts +22 -0
- package/src/web-components/ui/BdsAlert.mdx +90 -0
- package/src/web-components/ui/BdsAlert.stories.tsx +60 -0
- package/src/web-components/ui/BdsBadge.mdx +74 -0
- package/src/web-components/ui/BdsBadge.stories.tsx +37 -0
- package/src/web-components/ui/bds-alert.spec.ts +109 -0
- package/src/web-components/ui/bds-alert.ts +209 -0
- package/src/web-components/ui/bds-badge.spec.ts +51 -0
- package/src/web-components/ui/bds-badge.ts +88 -0
|
@@ -32,29 +32,29 @@
|
|
|
32
32
|
|
|
33
33
|
/* Placement: top (default) */
|
|
34
34
|
.--placement_top {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
inset-block-end: calc(100% + var(--bds-space_xs));
|
|
36
|
+
inset-inline-start: 50%;
|
|
37
37
|
transform: translateX(-50%);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/* Placement: bottom */
|
|
41
41
|
.--placement_bottom {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
inset-block-start: calc(100% + var(--bds-space_xs));
|
|
43
|
+
inset-inline-start: 50%;
|
|
44
44
|
transform: translateX(-50%);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/* Placement: left */
|
|
48
48
|
.--placement_left {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
inset-inline-end: calc(100% + var(--bds-space_xs));
|
|
50
|
+
inset-block-start: 50%;
|
|
51
51
|
transform: translateY(-50%);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/* Placement: right */
|
|
55
55
|
.--placement_right {
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
inset-inline-start: calc(100% + var(--bds-space_xs));
|
|
57
|
+
inset-block-start: 50%;
|
|
58
58
|
transform: translateY(-50%);
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -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,56 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Typography.native.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Typography (React Native)
|
|
7
|
+
|
|
8
|
+
React Native typography component wrapping `Text` with semantic variants. Use instead of bare `<Text>` to ensure consistent sizing, weight, and line-height from the design token scale.
|
|
9
|
+
|
|
10
|
+
> **Status: alpha** — API may change before stable release.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Typography } from '@boostdev/components';
|
|
16
|
+
|
|
17
|
+
<Typography variant="h1">Page title</Typography>
|
|
18
|
+
<Typography variant="body">Body copy text.</Typography>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
### All variants
|
|
24
|
+
<Canvas of={Stories.AllVariants} />
|
|
25
|
+
|
|
26
|
+
### Heading 1
|
|
27
|
+
<Canvas of={Stories.Heading1} />
|
|
28
|
+
|
|
29
|
+
### Heading 2
|
|
30
|
+
<Canvas of={Stories.Heading2} />
|
|
31
|
+
|
|
32
|
+
### Heading 3
|
|
33
|
+
<Canvas of={Stories.Heading3} />
|
|
34
|
+
|
|
35
|
+
### Body
|
|
36
|
+
<Canvas of={Stories.Body} />
|
|
37
|
+
|
|
38
|
+
### Body small
|
|
39
|
+
<Canvas of={Stories.BodySmall} />
|
|
40
|
+
|
|
41
|
+
## Props
|
|
42
|
+
|
|
43
|
+
<ArgTypes of={Stories} />
|
|
44
|
+
|
|
45
|
+
## Props reference
|
|
46
|
+
|
|
47
|
+
| Prop | Type | Default | Description |
|
|
48
|
+
|------|------|---------|-------------|
|
|
49
|
+
| `variant` | `"h1" \| "h2" \| "h3" \| "body" \| "body_s"` | `"body"` | Typographic scale |
|
|
50
|
+
| `children` | `ReactNode` | — | Text content |
|
|
51
|
+
| `style` | `StyleProp<TextStyle>` | — | Override text styles |
|
|
52
|
+
|
|
53
|
+
## Accessibility
|
|
54
|
+
|
|
55
|
+
- `h1`/`h2`/`h3` variants set `accessibilityRole="header"` automatically
|
|
56
|
+
- Override with the `accessibilityRole` prop if the visual hierarchy doesn't match the semantic hierarchy
|
|
@@ -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
|
@@ -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,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoker Commands API polyfill (Baseline 2025).
|
|
3
|
+
*
|
|
4
|
+
* Handles `commandfor` / `command` attributes on <button> elements for browsers
|
|
5
|
+
* that don't yet support the native API. Dispatches a synthetic `command` event
|
|
6
|
+
* on the target element before performing the action — identical to native
|
|
7
|
+
* behaviour — so any existing `command` event listeners continue to work.
|
|
8
|
+
*
|
|
9
|
+
* Supports the dialog-specific commands used by this library:
|
|
10
|
+
* command="show-modal" → dialog.showModal()
|
|
11
|
+
* command="close" → dialog.close()
|
|
12
|
+
*
|
|
13
|
+
* Installation is idempotent and SSR-safe. The function is called automatically
|
|
14
|
+
* when the Dialog component is imported; consumers do not need to call it.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
let installed = false;
|
|
18
|
+
|
|
19
|
+
export function installInvokerCommandsPolyfill(): void {
|
|
20
|
+
if (installed) return;
|
|
21
|
+
if (typeof window === 'undefined') return;
|
|
22
|
+
if ('command' in HTMLButtonElement.prototype) return; // native support present
|
|
23
|
+
|
|
24
|
+
installed = true;
|
|
25
|
+
|
|
26
|
+
document.addEventListener('click', (e: MouseEvent) => {
|
|
27
|
+
const button = (e.target as Element | null)?.closest<HTMLButtonElement>('button[commandfor]');
|
|
28
|
+
if (!button) return;
|
|
29
|
+
|
|
30
|
+
const targetId = button.getAttribute('commandfor');
|
|
31
|
+
const command = button.getAttribute('command');
|
|
32
|
+
if (!targetId || !command) return;
|
|
33
|
+
|
|
34
|
+
const target = document.getElementById(targetId);
|
|
35
|
+
if (!target) return;
|
|
36
|
+
|
|
37
|
+
// Dispatch a synthetic command event that mirrors the native CommandEvent.
|
|
38
|
+
const commandEvent = new Event('command', { cancelable: true, bubbles: false });
|
|
39
|
+
Object.defineProperty(commandEvent, 'command', { value: command, configurable: true });
|
|
40
|
+
Object.defineProperty(commandEvent, 'source', { value: button, configurable: true });
|
|
41
|
+
const notCancelled = target.dispatchEvent(commandEvent);
|
|
42
|
+
|
|
43
|
+
if (!notCancelled) return;
|
|
44
|
+
|
|
45
|
+
if (target instanceof HTMLDialogElement) {
|
|
46
|
+
if (command === 'show-modal' && !target.open) {
|
|
47
|
+
target.showModal();
|
|
48
|
+
} else if (command === 'close' && target.open) {
|
|
49
|
+
target.close();
|
|
50
|
+
}
|
|
51
|
+
} else if (target.hasAttribute('popover') && 'showPopover' in target) {
|
|
52
|
+
// Popover API commands — Popover API is Baseline 2024 so showPopover exists
|
|
53
|
+
// in any browser that lacks native invoker command support.
|
|
54
|
+
const popover = target as HTMLElement & {
|
|
55
|
+
showPopover(): void;
|
|
56
|
+
hidePopover(): void;
|
|
57
|
+
togglePopover(force?: boolean): boolean;
|
|
58
|
+
};
|
|
59
|
+
if (command === 'toggle-popover') {
|
|
60
|
+
popover.togglePopover();
|
|
61
|
+
} else if (command === 'show-popover') {
|
|
62
|
+
popover.showPopover();
|
|
63
|
+
} else if (command === 'hide-popover') {
|
|
64
|
+
popover.hidePopover();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
|
|
14
|
+
// Invoker Commands API (Baseline 2025) — not yet in @types/react.
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
16
|
+
interface ButtonHTMLAttributes<T> {
|
|
17
|
+
commandfor?: string;
|
|
18
|
+
command?: string;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// CommandEvent and its entry in the DOM event map — not yet in TypeScript's lib.dom.
|
|
23
|
+
declare global {
|
|
24
|
+
interface CommandEvent extends Event {
|
|
25
|
+
readonly command: string;
|
|
26
|
+
readonly source: Element;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface HTMLElementEventMap {
|
|
30
|
+
command: CommandEvent;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -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.
|