@boostdev/design-system-components 1.2.5 → 1.2.7
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 +25 -6
- package/dist/client.cjs +60 -54
- package/dist/client.css +553 -540
- package/dist/client.d.cts +7 -5
- package/dist/client.d.ts +7 -5
- package/dist/client.js +60 -54
- package/dist/index.cjs +60 -54
- package/dist/index.css +553 -540
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +60 -54
- package/package.json +1 -1
- package/src/components/interaction/Button/Button.mdx +11 -3
- package/src/components/interaction/Button/Button.module.css +5 -5
- package/src/components/interaction/Command/Command.mdx +1 -0
- package/src/components/interaction/Command/Command.module.css +1 -1
- package/src/components/interaction/Drawer/Drawer.mdx +2 -0
- package/src/components/interaction/Drawer/Drawer.module.css +1 -1
- package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +19 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +2 -2
- package/src/components/interaction/Rating/Rating.mdx +14 -0
- package/src/components/interaction/Rating/Rating.module.css +2 -2
- package/src/components/interaction/Toast/Toast.mdx +12 -4
- package/src/components/interaction/Toast/Toast.spec.tsx +11 -0
- package/src/components/interaction/Toast/Toast.tsx +11 -4
- package/src/components/interaction/form/Combobox/Combobox.mdx +2 -0
- package/src/components/interaction/form/Combobox/Combobox.module.css +3 -3
- package/src/components/interaction/form/FileInput/FileInput.mdx +2 -0
- package/src/components/interaction/form/FileInput/FileInput.module.css +2 -2
- package/src/components/interaction/form/NumberInput/NumberInput.mdx +4 -0
- package/src/components/interaction/form/NumberInput/NumberInput.module.css +3 -3
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +1 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +1 -1
- package/src/components/interaction/form/Slider/Slider.mdx +4 -0
- package/src/components/interaction/form/Slider/Slider.module.css +8 -8
- package/src/components/interaction/form/Switch/Switch.mdx +3 -0
- package/src/components/interaction/form/Switch/Switch.module.css +1 -1
- package/src/components/interaction/form/Textarea/Textarea.mdx +1 -1
- package/src/components/interaction/form/Textarea/Textarea.module.css +1 -1
- package/src/components/layout/ButtonGroup/ButtonGroup.mdx +2 -0
- package/src/components/layout/Card/Card.mdx +17 -6
- package/src/components/layout/Card/Card.module.css +12 -8
- package/src/components/layout/Card/Card.spec.tsx +30 -0
- package/src/components/layout/Card/Card.stories.tsx +3 -1
- package/src/components/layout/Card/Card.tsx +12 -8
- package/src/components/layout/SectionHeader/SectionHeader.mdx +10 -2
- package/src/components/layout/SectionHeader/SectionHeader.module.css +29 -17
- package/src/components/layout/SectionHeader/SectionHeader.spec.tsx +5 -0
- package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +2 -1
- package/src/components/layout/SectionHeader/SectionHeader.tsx +1 -1
- package/src/components/ui/Accordion/Accordion.mdx +1 -0
- package/src/components/ui/Accordion/Accordion.module.css +1 -1
- package/src/components/ui/Alert/Alert.mdx +6 -3
- package/src/components/ui/Alert/Alert.module.css +1 -1
- package/src/components/ui/Calendar/Calendar.mdx +5 -0
- package/src/components/ui/Calendar/Calendar.module.css +5 -5
- package/src/components/ui/Carousel/Carousel.mdx +14 -0
- package/src/components/ui/Carousel/Carousel.module.css +1 -1
- package/src/components/ui/NotificationBanner/NotificationBanner.mdx +18 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +1 -1
- package/src/components/ui/Pagination/Pagination.mdx +21 -0
- package/src/components/ui/Pagination/Pagination.module.css +5 -5
- package/src/components/ui/Table/Table.mdx +2 -0
- package/src/components/ui/Table/Table.module.css +2 -2
- package/src/components/ui/Tabs/Tabs.mdx +17 -0
- package/src/components/ui/Tabs/Tabs.module.css +3 -3
- package/src/css/bdc.css +1 -0
|
@@ -12,6 +12,8 @@ Primary interactive element for triggering actions. Renders as `<button>` or `<a
|
|
|
12
12
|
- Secondary or ghost actions (`ghost`)
|
|
13
13
|
- Navigation that should look like an action (`href` → renders as `<a>`)
|
|
14
14
|
|
|
15
|
+
> **Form actions:** When using buttons in a form (submit, reset, cancel), always wrap them in a `ButtonGroup` component. This provides consistent spacing, alignment, and responsive layout for form actions. Use `variant="flow"` for step navigation or `variant="card"` for single-form submissions.
|
|
16
|
+
|
|
15
17
|
## When not to use
|
|
16
18
|
- Navigation to another page when it reads naturally as a link — use `Link` instead
|
|
17
19
|
- When the action is purely icon-based without a label — ensure `aria-label` is set
|
|
@@ -53,11 +55,17 @@ Primary interactive element for triggering actions. Renders as `<button>` or `<a
|
|
|
53
55
|
|
|
54
56
|
<table>
|
|
55
57
|
<thead>
|
|
56
|
-
<tr><th>Variable</th><th>Description</th></tr>
|
|
58
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
57
59
|
</thead>
|
|
58
60
|
<tbody>
|
|
59
|
-
<tr><td>`--button_color`</td><td>Identity colour — fills default, outlines
|
|
60
|
-
<tr><td>`--button_on-color`</td><td>Text/icon colour on the button. Pair with `--button_color`.</td></tr>
|
|
61
|
+
<tr><td>`--button_color`</td><td>`var(--bds-color_interactive)`</td><td>Identity colour — fills default, outlines outline. Set this to re-theme.</td></tr>
|
|
62
|
+
<tr><td>`--button_on-color`</td><td>`var(--bds-color_on-interactive)`</td><td>Text/icon colour on the button. Pair with `--button_color`.</td></tr>
|
|
63
|
+
<tr><td>`--button_color--on-bg`</td><td>`var(--bds-color_interactive_on-bg)`</td><td>Text-safe variant of `--button_color` for outline variant text/border</td></tr>
|
|
64
|
+
<tr><td>`--button_radius`</td><td>`var(--bds-border_radius--full)`</td><td>Button border radius</td></tr>
|
|
65
|
+
<tr><td>`--button_bg--hover`</td><td>`var(--bds-color_bg)`</td><td>Default variant background on hover</td></tr>
|
|
66
|
+
<tr><td>`--button_text--hover`</td><td>`var(--bds-color_interactive_on-bg)`</td><td>Default variant text colour on hover</td></tr>
|
|
67
|
+
<tr><td>`--button_bg--outline-hover`</td><td>`var(--button_color)`</td><td>Outline variant background on hover</td></tr>
|
|
68
|
+
<tr><td>`--button_text--outline-hover`</td><td>`var(--button_on-color)`</td><td>Outline variant text colour on hover</td></tr>
|
|
61
69
|
</tbody>
|
|
62
70
|
</table>
|
|
63
71
|
|
|
@@ -140,9 +140,9 @@
|
|
|
140
140
|
/* Default hover: outline appears, bg becomes page surface */
|
|
141
141
|
@media (hover: hover) and (pointer: fine) {
|
|
142
142
|
.button.--default:hover {
|
|
143
|
-
--button_bg: var(--bds-color_bg);
|
|
144
|
-
--button_text: var(--bds-color_interactive_on-bg);
|
|
145
|
-
--button_pulse-color: var(--bds-color_interactive_on-bg);
|
|
143
|
+
--button_bg: var(--button_bg--hover, var(--bds-color_bg));
|
|
144
|
+
--button_text: var(--button_text--hover, var(--bds-color_interactive_on-bg));
|
|
145
|
+
--button_pulse-color: var(--button_pulse-color--hover, var(--bds-color_interactive_on-bg));
|
|
146
146
|
--bdc_color: currentcolor;
|
|
147
147
|
}
|
|
148
148
|
}
|
|
@@ -151,8 +151,8 @@
|
|
|
151
151
|
@media (hover: hover) and (pointer: fine) {
|
|
152
152
|
.button.--outline:hover,
|
|
153
153
|
.button.--ghost:hover {
|
|
154
|
-
--button_bg: var(--button_color);
|
|
155
|
-
--button_text: var(--button_on-color);
|
|
154
|
+
--button_bg: var(--button_bg--outline-hover, var(--button_color));
|
|
155
|
+
--button_text: var(--button_text--outline-hover, var(--button_on-color));
|
|
156
156
|
--bdc_color: transparent;
|
|
157
157
|
}
|
|
158
158
|
}
|
|
@@ -32,6 +32,7 @@ Keyboard-first command palette for searching and executing actions. Commonly tri
|
|
|
32
32
|
<tr><td>`--command_on-color`</td><td>`var(--bds-color_on-bg)`</td><td>Text and icon colour</td></tr>
|
|
33
33
|
<tr><td>`--command_shadow`</td><td>`var(--bds-shadow_xl)`</td><td>Panel box shadow</td></tr>
|
|
34
34
|
<tr><td>`--command_border`</td><td>`none`</td><td>Panel border</td></tr>
|
|
35
|
+
<tr><td>`--command_item-bg--active`</td><td>`var(--bds-color_bg--subtle)`</td><td>Active/highlighted item background</td></tr>
|
|
35
36
|
</tbody>
|
|
36
37
|
</table>
|
|
37
38
|
|
|
@@ -38,6 +38,8 @@ Slide-in panel from the left or right edge of the viewport. Use for navigation,
|
|
|
38
38
|
<tbody>
|
|
39
39
|
<tr><td>`--drawer_color`</td><td>`var(--bds-color_bg)`</td><td>Panel background colour</td></tr>
|
|
40
40
|
<tr><td>`--drawer_on-color`</td><td>`var(--bds-color_on-bg)`</td><td>Text colour</td></tr>
|
|
41
|
+
<tr><td>`--drawer_close-radius`</td><td>`var(--bds-border_radius--circle)`</td><td>Close button border radius</td></tr>
|
|
42
|
+
<tr><td>`--drawer_close-bg--hover`</td><td>`var(--bds-color_bg--subtle)`</td><td>Close button background on hover</td></tr>
|
|
41
43
|
</tbody>
|
|
42
44
|
</table>
|
|
43
45
|
|
|
@@ -33,6 +33,25 @@ Floating menu with a list of selectable actions, triggered by a button. Supports
|
|
|
33
33
|
|
|
34
34
|
<ArgTypes of={Stories} />
|
|
35
35
|
|
|
36
|
+
## CSS variables
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
<table>
|
|
40
|
+
<thead>
|
|
41
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
42
|
+
</thead>
|
|
43
|
+
<tbody>
|
|
44
|
+
<tr><td>`--dropdown_border`</td><td>`none`</td><td>Menu container border</td></tr>
|
|
45
|
+
<tr><td>`--dropdown_radius`</td><td>`var(--bdc-outline_radius)`</td><td>Menu container corner radius</td></tr>
|
|
46
|
+
<tr><td>`--dropdown_color`</td><td>`var(--bds-color_bg)`</td><td>Menu background colour</td></tr>
|
|
47
|
+
<tr><td>`--dropdown_on-color`</td><td>`var(--bds-color_on-bg)`</td><td>Menu text colour</td></tr>
|
|
48
|
+
<tr><td>`--dropdown_shadow`</td><td>`var(--bds-shadow_m)`</td><td>Menu box shadow</td></tr>
|
|
49
|
+
<tr><td>`--dropdown_item-bg--hover`</td><td>`var(--bds-color_bg--subtle)`</td><td>Menu item background on hover</td></tr>
|
|
50
|
+
<tr><td>`--dropdown_item-color--hover`</td><td>`var(--bds-color_interactive_on-bg)`</td><td>Menu item text colour on hover</td></tr>
|
|
51
|
+
</tbody>
|
|
52
|
+
</table>
|
|
53
|
+
|
|
54
|
+
|
|
36
55
|
## Accessibility
|
|
37
56
|
|
|
38
57
|
- Implements ARIA menu pattern: `role="menu"`, `role="menuitem"`
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
|
|
65
65
|
@media (hover: hover) and (pointer: fine) {
|
|
66
66
|
.item:not(:disabled):hover {
|
|
67
|
-
background-color: var(--bds-color_bg--subtle);
|
|
68
|
-
color: var(--bds-color_interactive_on-bg);
|
|
67
|
+
background-color: var(--dropdown_item-bg--hover, var(--bds-color_bg--subtle));
|
|
68
|
+
color: var(--dropdown_item-color--hover, var(--bds-color_interactive_on-bg));
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -32,6 +32,20 @@ Read-only star rating display. Use to show an existing score; for user input, co
|
|
|
32
32
|
|
|
33
33
|
<ArgTypes of={Stories} />
|
|
34
34
|
|
|
35
|
+
## CSS variables
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
<table>
|
|
39
|
+
<thead>
|
|
40
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
41
|
+
</thead>
|
|
42
|
+
<tbody>
|
|
43
|
+
<tr><td>`--rating_color`</td><td>`var(--bds-color_bg)`</td><td>Empty star colour</td></tr>
|
|
44
|
+
<tr><td>`--rating_color--filled`</td><td>`var(--bds-color_warning)`</td><td>Filled star colour</td></tr>
|
|
45
|
+
</tbody>
|
|
46
|
+
</table>
|
|
47
|
+
|
|
48
|
+
|
|
35
49
|
## Accessibility
|
|
36
50
|
|
|
37
51
|
- Renders as a `<span>` with `role="img"` and `aria-label="X out of Y stars"`
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
.rating {
|
|
3
3
|
display: inline-flex;
|
|
4
4
|
gap: var(--bds-space_xxxs);
|
|
5
|
-
color: var(--bds-color_bg);
|
|
5
|
+
color: var(--rating_color, var(--bds-color_bg));
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
.star {
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.--filled {
|
|
14
|
-
color: var(--bds-color_warning);
|
|
14
|
+
color: var(--rating_color--filled, var(--bds-color_warning));
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -17,20 +17,26 @@ Transient feedback notification that auto-dismisses after 5 seconds. Provided vi
|
|
|
17
17
|
|
|
18
18
|
## Setup
|
|
19
19
|
|
|
20
|
-
Wrap your app (or layout root) once with `ToastProvider
|
|
20
|
+
Wrap your app (or layout root) once with `ToastProvider`. Since Toast uses React hooks, it requires a client boundary in Next.js:
|
|
21
21
|
|
|
22
22
|
```tsx
|
|
23
|
-
|
|
23
|
+
// Next.js App Router — use the /client entry inside a 'use client' boundary:
|
|
24
|
+
import { ToastProvider } from '@boostdev/design-system-components/client';
|
|
24
25
|
|
|
26
|
+
// Non-Next.js / bundler apps:
|
|
27
|
+
import { ToastProvider } from '@boostdev/design-system-components';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
25
31
|
<ToastProvider>
|
|
26
32
|
<App />
|
|
27
33
|
</ToastProvider>
|
|
28
34
|
```
|
|
29
35
|
|
|
30
|
-
Then call `showToast` anywhere inside:
|
|
36
|
+
Then call `showToast` anywhere inside a client component:
|
|
31
37
|
|
|
32
38
|
```tsx
|
|
33
|
-
import { useToast } from '@boostdev/components';
|
|
39
|
+
import { useToast } from '@boostdev/design-system-components/client';
|
|
34
40
|
|
|
35
41
|
const { showToast } = useToast();
|
|
36
42
|
|
|
@@ -39,6 +45,8 @@ showToast('Something went wrong', 'error');
|
|
|
39
45
|
showToast('New message received', 'info');
|
|
40
46
|
```
|
|
41
47
|
|
|
48
|
+
> **Next.js RSC note:** The root entry (`@boostdev/design-system-components`) is safe to import in Server Components for server-safe components like `Typography` or `Badge`. Toast itself is client-only and must be used inside a `'use client'` boundary.
|
|
49
|
+
|
|
42
50
|
## Examples
|
|
43
51
|
|
|
44
52
|
### Success
|
|
@@ -59,4 +59,15 @@ describe('Toast', () => {
|
|
|
59
59
|
expect(() => render(<Bad />)).toThrow('useToast must be used within a ToastProvider');
|
|
60
60
|
errorSpy.mockRestore();
|
|
61
61
|
});
|
|
62
|
+
|
|
63
|
+
it('shares a single lazy-initialised context between provider and consumer (RSC-safe)', () => {
|
|
64
|
+
// The lazy getToastContext() pattern defers createContext() until first render
|
|
65
|
+
// so the module can be safely evaluated in Next.js Server Components.
|
|
66
|
+
// This test verifies provider and consumer share the same context instance.
|
|
67
|
+
let captured: ReturnType<typeof useToast> | undefined;
|
|
68
|
+
const Capture = () => { captured = useToast(); return null; };
|
|
69
|
+
render(<ToastProvider><Capture /></ToastProvider>);
|
|
70
|
+
expect(captured).toBeDefined();
|
|
71
|
+
expect(typeof captured!.showToast).toBe('function');
|
|
72
|
+
});
|
|
62
73
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect, createContext, useContext, ReactNode, useCallback, useMemo, useRef } from 'react';
|
|
1
|
+
import { useState, useEffect, createContext, useContext, ReactNode, useCallback, useMemo, useRef, Context } from 'react';
|
|
2
2
|
import css from './Toast.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
4
|
|
|
@@ -14,9 +14,16 @@ interface ToastContextType {
|
|
|
14
14
|
showToast: (message: string, variant: ToastVariant) => void;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
// Lazy-initialised so the module can be evaluated in Next.js Server Components
|
|
18
|
+
// without calling `createContext` at import time (which crashes RSC).
|
|
19
|
+
let _ToastContext: Context<ToastContextType | undefined>;
|
|
20
|
+
function getToastContext(): Context<ToastContextType | undefined> {
|
|
21
|
+
_ToastContext ??= createContext<ToastContextType | undefined>(undefined);
|
|
22
|
+
return _ToastContext;
|
|
23
|
+
}
|
|
18
24
|
|
|
19
25
|
export function ToastProvider({ children }: { children: ReactNode }) {
|
|
26
|
+
const ToastContext = getToastContext();
|
|
20
27
|
const [toasts, setToasts] = useState<Toast[]>([]);
|
|
21
28
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
22
29
|
|
|
@@ -84,8 +91,8 @@ function ToastItem({ toast, onRemove }: { toast: Toast; onRemove: () => void })
|
|
|
84
91
|
);
|
|
85
92
|
}
|
|
86
93
|
|
|
87
|
-
export function useToast() {
|
|
88
|
-
const context = useContext(
|
|
94
|
+
export function useToast(): ToastContextType {
|
|
95
|
+
const context = useContext(getToastContext());
|
|
89
96
|
if (!context) {
|
|
90
97
|
throw new Error('useToast must be used within a ToastProvider');
|
|
91
98
|
}
|
|
@@ -46,6 +46,8 @@ Searchable select with type-ahead filtering. Combines a text input with a dropdo
|
|
|
46
46
|
<tr><td>`--combobox_listbox-border`</td><td>`none`</td><td>Dropdown list border</td></tr>
|
|
47
47
|
<tr><td>`--combobox_listbox-radius`</td><td>`var(--bdc-outline_radius)`</td><td>Dropdown list corner radius</td></tr>
|
|
48
48
|
<tr><td>`--combobox_listbox-shadow`</td><td>`var(--bds-shadow_m)`</td><td>Dropdown list box shadow</td></tr>
|
|
49
|
+
<tr><td>`--combobox_option-bg--highlighted`</td><td>`var(--bds-color_bg--subtle)`</td><td>Highlighted option background</td></tr>
|
|
50
|
+
<tr><td>`--combobox_option-color--highlighted`</td><td>`var(--bds-color_interactive_on-bg)`</td><td>Highlighted option text colour</td></tr>
|
|
49
51
|
</tbody>
|
|
50
52
|
</table>
|
|
51
53
|
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
padding: var(--bds-space_xxs) 0;
|
|
74
74
|
border: var(--combobox_listbox-border, none);
|
|
75
75
|
--bdc_color: currentcolor;
|
|
76
|
-
--bdc-outline_radius: var(--bds-border_radius--
|
|
76
|
+
--bdc-outline_radius: var(--bds-border_radius--s);
|
|
77
77
|
|
|
78
78
|
border-radius: var(--combobox_listbox-radius, var(--bdc-outline_radius));
|
|
79
79
|
background-color: var(--combobox_color, var(--bds-color_bg));
|
|
@@ -92,8 +92,8 @@
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
.option.--highlighted {
|
|
95
|
-
background-color: var(--bds-color_bg--subtle);
|
|
96
|
-
color: var(--bds-color_interactive_on-bg);
|
|
95
|
+
background-color: var(--combobox_option-bg--highlighted, var(--bds-color_bg--subtle));
|
|
96
|
+
color: var(--combobox_option-color--highlighted, var(--bds-color_interactive_on-bg));
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
.option.--selected {
|
|
@@ -41,6 +41,8 @@ File upload input with styled trigger button, hint, and error support.
|
|
|
41
41
|
<tbody>
|
|
42
42
|
<tr><td>`--fileInput_color`</td><td>`var(--bds-color_on-bg)`</td><td>Text colour</td></tr>
|
|
43
43
|
<tr><td>`--fileInput_border-color`</td><td>`currentcolor`</td><td>Drop zone border colour</td></tr>
|
|
44
|
+
<tr><td>`--fileInput_border-color--hover`</td><td>`var(--bds-color_interactive)`</td><td>Drop zone border colour on hover</td></tr>
|
|
45
|
+
<tr><td>`--fileInput_bg--hover`</td><td>`rgb(from var(--bds-color_interactive) r g b / 4%)`</td><td>Drop zone background on hover</td></tr>
|
|
44
46
|
</tbody>
|
|
45
47
|
</table>
|
|
46
48
|
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
|
|
81
81
|
@media (hover: hover) and (pointer: fine) {
|
|
82
82
|
.dropZone:not(.isDisabled):hover {
|
|
83
|
-
border-color: var(--bds-color_interactive);
|
|
84
|
-
background-color: rgb(from var(--bds-color_interactive) r g b / 4%);
|
|
83
|
+
border-color: var(--fileInput_border-color--hover, var(--bds-color_interactive));
|
|
84
|
+
background-color: var(--fileInput_bg--hover, rgb(from var(--bds-color_interactive) r g b / 4%));
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -43,6 +43,10 @@ Numeric text input with increment/decrement stepper buttons. Enforces `min`, `ma
|
|
|
43
43
|
<tr><td>`--numberInput_border`</td><td>`none`</td><td>Border</td></tr>
|
|
44
44
|
<tr><td>`--numberInput_radius`</td><td>`var(--bdc-outline_radius)`</td><td>Corner radius</td></tr>
|
|
45
45
|
<tr><td>`--numberInput_shadow`</td><td>`var(--bdc-outline_shadow)`</td><td>Box shadow</td></tr>
|
|
46
|
+
<tr><td>`--numberInput_stepper-color`</td><td>`var(--bds-color_interactive)`</td><td>Stepper button icon colour</td></tr>
|
|
47
|
+
<tr><td>`--numberInput_stepper-bg`</td><td>`var(--bds-color_bg--subtle)`</td><td>Stepper button background</td></tr>
|
|
48
|
+
<tr><td>`--numberInput_stepper-bg--hover`</td><td>`var(--bds-color_bg--subtle)`</td><td>Stepper button background on hover</td></tr>
|
|
49
|
+
<tr><td>`--numberInput_stepper-opacity--hover`</td><td>`0.7`</td><td>Stepper button opacity on hover</td></tr>
|
|
46
50
|
</tbody>
|
|
47
51
|
</table>
|
|
48
52
|
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
justify-content: center;
|
|
59
59
|
padding: var(--bds-space_xs);
|
|
60
60
|
margin: 1px;
|
|
61
|
-
background-color: var(--bds-color_bg--subtle);
|
|
61
|
+
background-color: var(--numberInput_stepper-bg, var(--bds-color_bg--subtle));
|
|
62
62
|
color: var(--numberInput_color, var(--bds-color_on-bg));
|
|
63
63
|
cursor: pointer;
|
|
64
64
|
transition: var(--bds-animation_transition);
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
|
|
89
89
|
@media (hover: hover) and (pointer: fine) {
|
|
90
90
|
.stepper:not(:disabled):hover {
|
|
91
|
-
background-color: var(--bds-color_bg--subtle);
|
|
92
|
-
opacity: 0.7;
|
|
91
|
+
background-color: var(--numberInput_stepper-bg--hover, var(--bds-color_bg--subtle));
|
|
92
|
+
opacity: var(--numberInput_stepper-opacity--hover, 0.7);
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -103,6 +103,7 @@ When `selectedIndex` is omitted, the component auto-detects the active item from
|
|
|
103
103
|
| `--control_label-color` | `var(--bds-color_on-bg--subtle)` | Inactive item text colour |
|
|
104
104
|
| `--control_label-color--active` | `var(--bds-color_on-interactive)` (filled) / `var(--bds-color_interactive)` (outline) | Active item text colour |
|
|
105
105
|
| `--control_label-color--hover` | `var(--bds-color_on-bg)` | Hovered inactive item text colour |
|
|
106
|
+
| `--control_indicator-color` | `var(--bds-color_interactive)` | Outline indicator border colour (outline variant) |
|
|
106
107
|
|
|
107
108
|
## Accessibility
|
|
108
109
|
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
|
|
110
110
|
.control.--variant_outline .indicator {
|
|
111
111
|
display: block;
|
|
112
|
-
box-shadow: inset 0 0 0 2px var(--bds-color_interactive);
|
|
112
|
+
box-shadow: inset 0 0 0 2px var(--control_indicator-color, var(--bds-color_interactive));
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
@media (hover: hover) and (pointer: fine) {
|
|
@@ -47,6 +47,10 @@ Range input for selecting a numeric value within a bounded range.
|
|
|
47
47
|
</thead>
|
|
48
48
|
<tbody>
|
|
49
49
|
<tr><td>`--slider_color-active`</td><td>`var(--bdc-color_active)`</td><td>Filled track and thumb colour</td></tr>
|
|
50
|
+
<tr><td>`--slider_track-bg`</td><td>`var(--bds-color_bg--subtle)`</td><td>Unfilled track background</td></tr>
|
|
51
|
+
<tr><td>`--slider_track-radius`</td><td>`var(--bds-border_radius--full)`</td><td>Track border radius</td></tr>
|
|
52
|
+
<tr><td>`--slider_thumb-radius`</td><td>`var(--bds-border_radius--circle)`</td><td>Thumb border radius</td></tr>
|
|
53
|
+
<tr><td>`--slider_color--error`</td><td>`var(--bds-color_error)`</td><td>Track and thumb colour in error state</td></tr>
|
|
50
54
|
</tbody>
|
|
51
55
|
</table>
|
|
52
56
|
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
to right,
|
|
32
32
|
var(--slider_color-active, var(--bdc-color_active)) 0%,
|
|
33
33
|
var(--slider_color-active, var(--bdc-color_active)) var(--slider_fill),
|
|
34
|
-
var(--bds-color_bg--subtle) var(--slider_fill),
|
|
35
|
-
var(--bds-color_bg--subtle) 100%
|
|
34
|
+
var(--slider_track-bg, var(--bds-color_bg--subtle)) var(--slider_fill),
|
|
35
|
+
var(--slider_track-bg, var(--bds-color_bg--subtle)) 100%
|
|
36
36
|
);
|
|
37
37
|
cursor: pointer;
|
|
38
38
|
transition: var(--bds-animation_transition);
|
|
@@ -75,19 +75,19 @@
|
|
|
75
75
|
.sliderError {
|
|
76
76
|
background: linear-gradient(
|
|
77
77
|
to right,
|
|
78
|
-
var(--bds-color_error) 0%,
|
|
79
|
-
var(--bds-color_error) var(--slider_fill),
|
|
80
|
-
var(--bds-color_bg--subtle) var(--slider_fill),
|
|
81
|
-
var(--bds-color_bg--subtle) 100%
|
|
78
|
+
var(--slider_color--error, var(--bds-color_error)) 0%,
|
|
79
|
+
var(--slider_color--error, var(--bds-color_error)) var(--slider_fill),
|
|
80
|
+
var(--slider_track-bg, var(--bds-color_bg--subtle)) var(--slider_fill),
|
|
81
|
+
var(--slider_track-bg, var(--bds-color_bg--subtle)) 100%
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
.sliderError::-webkit-slider-thumb {
|
|
86
|
-
background-color: var(--bds-color_error);
|
|
86
|
+
background-color: var(--slider_color--error, var(--bds-color_error));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
.sliderError::-moz-range-thumb {
|
|
90
|
-
background-color: var(--bds-color_error);
|
|
90
|
+
background-color: var(--slider_color--error, var(--bds-color_error));
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -56,6 +56,9 @@ Toggle control for binary settings that take immediate effect — without requir
|
|
|
56
56
|
<tr><td>`--switch_thumb-bg`</td><td>`var(--bds-color_grey)`</td><td>Thumb colour when off (also track outline)</td></tr>
|
|
57
57
|
<tr><td>`--switch_track-bg--active`</td><td>`var(--bdc-color_active--subtle)`</td><td>Track background when on</td></tr>
|
|
58
58
|
<tr><td>`--switch_thumb-bg--active`</td><td>`var(--bdc-color_active--strong)`</td><td>Thumb colour when on (also track outline)</td></tr>
|
|
59
|
+
<tr><td>`--switch_track-radius`</td><td>`var(--bds-border_radius--full)`</td><td>Track border radius</td></tr>
|
|
60
|
+
<tr><td>`--switch_thumb-radius`</td><td>`var(--bds-border_radius--circle)`</td><td>Thumb border radius</td></tr>
|
|
61
|
+
<tr><td>`--switch_border-color--error`</td><td>`var(--bds-color_error)`</td><td>Track outline colour in error state</td></tr>
|
|
59
62
|
</tbody>
|
|
60
63
|
</table>
|
|
61
64
|
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
|
|
79
79
|
/* Error state — declared before checked/focus so higher-specificity states can override */
|
|
80
80
|
.switch.switchError + .track {
|
|
81
|
-
--bdc_color: var(--bds-color_error);
|
|
81
|
+
--bdc_color: var(--switch_border-color--error, var(--bds-color_error));
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/* Checked state via CSS sibling combinator */
|
|
@@ -45,7 +45,7 @@ Multi-line text input with label, hint, and error support.
|
|
|
45
45
|
<tr><td>`--textarea_color_bg`</td><td>`var(--bds-color_bg)`</td><td>Background colour</td></tr>
|
|
46
46
|
<tr><td>`--textarea_color`</td><td>`var(--bds-color_on-bg)`</td><td>Text colour</td></tr>
|
|
47
47
|
<tr><td>`--textarea_border`</td><td>`none`</td><td>Border</td></tr>
|
|
48
|
-
<tr><td>`--textarea_radius`</td><td>`var(--bdc-outline_radius)
|
|
48
|
+
<tr><td>`--textarea_radius`</td><td>`var(--bdc-outline_radius--l)` (1.5em)</td><td>Corner radius</td></tr>
|
|
49
49
|
<tr><td>`--textarea_shadow`</td><td>`var(--bdc-outline_shadow)`</td><td>Box shadow</td></tr>
|
|
50
50
|
</tbody>
|
|
51
51
|
</table>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
padding: var(--bds-space_s);
|
|
12
12
|
border: var(--textarea_border, none);
|
|
13
13
|
--bdc_color: currentcolor;
|
|
14
|
-
--bdc-outline_radius: var(--
|
|
14
|
+
--bdc-outline_radius: var(--bdc-outline_radius--l);
|
|
15
15
|
|
|
16
16
|
border-radius: var(--textarea_radius, var(--bdc-outline_radius));
|
|
17
17
|
box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--textarea_shadow, var(--bdc-outline_shadow));
|
|
@@ -7,6 +7,8 @@ import * as Stories from './ButtonGroup.stories';
|
|
|
7
7
|
|
|
8
8
|
Layout wrapper that arranges `Button` children according to a contextual variant. Handles spacing, alignment, and direction.
|
|
9
9
|
|
|
10
|
+
> **Recommended:** Always wrap buttons in a `ButtonGroup` when they appear inside a form. This ensures consistent spacing and alignment for form actions like submit, cancel, and navigation.
|
|
11
|
+
|
|
10
12
|
## Variants
|
|
11
13
|
|
|
12
14
|
|
|
@@ -5,7 +5,7 @@ import * as Stories from './Card.stories';
|
|
|
5
5
|
|
|
6
6
|
# Card
|
|
7
7
|
|
|
8
|
-
Contained surface for grouping related content. Three visual variants provide different levels of elevation. When `onClick` is supplied, renders as a `<button>`.
|
|
8
|
+
Contained surface for grouping related content. Three visual variants provide different levels of elevation. When `onClick` is supplied, renders as a `<button>`. Use the `as` prop to render as any element or component (e.g. `<a>`, `<section>`, or a Next.js `Link`).
|
|
9
9
|
|
|
10
10
|
## When to use
|
|
11
11
|
- Group content that belongs together (product listing, profile summary, dashboard widget)
|
|
@@ -31,6 +31,12 @@ Contained surface for grouping related content. Three visual variants provide di
|
|
|
31
31
|
### Clickable
|
|
32
32
|
<Canvas of={Stories.Clickable} />
|
|
33
33
|
|
|
34
|
+
### As a link
|
|
35
|
+
<Canvas of={Stories.AsLink} />
|
|
36
|
+
|
|
37
|
+
### As a section
|
|
38
|
+
<Canvas of={Stories.AsSection} />
|
|
39
|
+
|
|
34
40
|
## Props
|
|
35
41
|
|
|
36
42
|
<ArgTypes of={Stories} />
|
|
@@ -40,13 +46,17 @@ Contained surface for grouping related content. Three visual variants provide di
|
|
|
40
46
|
|
|
41
47
|
<table>
|
|
42
48
|
<thead>
|
|
43
|
-
<tr><th>Variable</th><th>Description</th></tr>
|
|
49
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
44
50
|
</thead>
|
|
45
51
|
<tbody>
|
|
46
|
-
<tr><td>`--
|
|
47
|
-
<tr><td>`--card_on-bg`</td><td>Card text colour</td></tr>
|
|
48
|
-
<tr><td>`--
|
|
49
|
-
<tr><td>`--
|
|
52
|
+
<tr><td>`--card_color`</td><td>`var(--bds-color_bg)`</td><td>Card background colour</td></tr>
|
|
53
|
+
<tr><td>`--card_on-color`</td><td>`var(--bds-color_on-bg)`</td><td>Card text colour</td></tr>
|
|
54
|
+
<tr><td>`--card_padding`</td><td>varies by `padding` prop</td><td>Card padding (overrides the variant default)</td></tr>
|
|
55
|
+
<tr><td>`--card_border`</td><td>`none`</td><td>Border (outlined variant)</td></tr>
|
|
56
|
+
<tr><td>`--card_shadow--clickable-hover`</td><td>`var(--bds-shadow_xl)`</td><td>Box shadow on hover for clickable cards</td></tr>
|
|
57
|
+
<tr><td>`--card_shadow--default-hover`</td><td>`var(--bds-shadow_m)`</td><td>Box shadow on hover for default variant</td></tr>
|
|
58
|
+
<tr><td>`--card_shadow--elevated-hover`</td><td>`var(--bds-shadow_2xl)`</td><td>Box shadow on hover for elevated variant</td></tr>
|
|
59
|
+
<tr><td>`--card_shadow--outlined-hover`</td><td>`var(--bds-shadow_s)`</td><td>Box shadow on hover for outlined variant</td></tr>
|
|
50
60
|
</tbody>
|
|
51
61
|
</table>
|
|
52
62
|
|
|
@@ -55,4 +65,5 @@ Contained surface for grouping related content. Three visual variants provide di
|
|
|
55
65
|
|
|
56
66
|
- Static card renders as `<div>` — no interactive role
|
|
57
67
|
- Clickable card renders as `<button>` — keyboard and screen reader accessible
|
|
68
|
+
- When using `as="a"`, provide a valid `href`; when using a router `Link`, ensure it forwards `className` and `children`
|
|
58
69
|
- Ensure clickable cards have a meaningful accessible name (via visible text content)
|
|
@@ -27,19 +27,23 @@
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
.card.--padding-none {
|
|
30
|
-
padding: 0;
|
|
30
|
+
padding: var(--card_padding, 0);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
.card.--padding-small {
|
|
34
|
-
padding: var(--bds-space_s);
|
|
34
|
+
padding: var(--card_padding, var(--bds-space_s));
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
.card.--padding-medium {
|
|
38
|
-
padding: var(--bds-space_m);
|
|
38
|
+
padding: var(--card_padding, var(--bds-space_m));
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
.card.--padding-large {
|
|
42
|
-
padding: var(--bds-space_l);
|
|
42
|
+
padding: var(--card_padding, var(--bds-space_l));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.card.--padding-extra-large {
|
|
46
|
+
padding: var(--card_padding, var(--bds-space_xl));
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
.card.--text-start {
|
|
@@ -67,19 +71,19 @@
|
|
|
67
71
|
@media (hover: hover) and (pointer: fine) {
|
|
68
72
|
.card.--clickable:hover {
|
|
69
73
|
transform: translateY(-2px);
|
|
70
|
-
box-shadow: var(--bds-shadow_xl);
|
|
74
|
+
box-shadow: var(--card_shadow--clickable-hover, var(--bds-shadow_xl));
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
.card.--default:hover {
|
|
74
|
-
box-shadow: var(--bds-shadow_m);
|
|
78
|
+
box-shadow: var(--card_shadow--default-hover, var(--bds-shadow_m));
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
.card.--elevated:hover {
|
|
78
|
-
box-shadow: var(--bds-shadow_2xl);
|
|
82
|
+
box-shadow: var(--card_shadow--elevated-hover, var(--bds-shadow_2xl));
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
.card.--outlined:hover {
|
|
82
|
-
box-shadow: var(--bds-shadow_s);
|
|
86
|
+
box-shadow: var(--card_shadow--outlined-hover, var(--bds-shadow_s));
|
|
83
87
|
}
|
|
84
88
|
}
|
|
85
89
|
}
|
|
@@ -30,4 +30,34 @@ describe('Card', () => {
|
|
|
30
30
|
const { container } = render(<Card className="custom">Content</Card>);
|
|
31
31
|
expect(container.firstChild).toHaveClass('custom');
|
|
32
32
|
});
|
|
33
|
+
|
|
34
|
+
it('renders as a custom element via the as prop', () => {
|
|
35
|
+
const { container } = render(<Card as="section">Section card</Card>);
|
|
36
|
+
expect(container.firstChild?.nodeName).toBe('SECTION');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('renders as an anchor when as="a"', () => {
|
|
40
|
+
render(<Card as="a" href="/details">Link card</Card>);
|
|
41
|
+
const link = screen.getByRole('link');
|
|
42
|
+
expect(link).toHaveAttribute('href', '/details');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('renders as a custom component via the as prop', () => {
|
|
46
|
+
const CustomWrapper = ({ children, ...props }: { children: React.ReactNode; className?: string }) => (
|
|
47
|
+
<article {...props} data-testid="custom">{children}</article>
|
|
48
|
+
);
|
|
49
|
+
render(<Card as={CustomWrapper}>Custom</Card>);
|
|
50
|
+
expect(screen.getByTestId('custom')).toBeInTheDocument();
|
|
51
|
+
expect(screen.getByTestId('custom').nodeName).toBe('ARTICLE');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('prefers as prop over onClick button fallback', () => {
|
|
55
|
+
const { container } = render(<Card as="div" onClick={() => {}}>Content</Card>);
|
|
56
|
+
expect(container.firstChild?.nodeName).toBe('DIV');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('renders the extra-large padding variant', () => {
|
|
60
|
+
const { container } = render(<Card padding="extra-large">Content</Card>);
|
|
61
|
+
expect(container.firstChild).toHaveClass('--padding-extra-large');
|
|
62
|
+
});
|
|
33
63
|
});
|