@boostdev/design-system-components 0.1.18 → 1.0.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.
Files changed (137) hide show
  1. package/AGENTS.md +65 -27
  2. package/README.md +64 -29
  3. package/dist/client.cjs +419 -242
  4. package/dist/client.css +1378 -1186
  5. package/dist/client.d.cts +43 -16
  6. package/dist/client.d.ts +43 -16
  7. package/dist/client.js +421 -246
  8. package/dist/index.cjs +419 -242
  9. package/dist/index.css +1378 -1186
  10. package/dist/index.d.cts +43 -16
  11. package/dist/index.d.ts +43 -16
  12. package/dist/index.js +421 -246
  13. package/package.json +9 -4
  14. package/src/components/interaction/Button/Button.mdx +8 -11
  15. package/src/components/interaction/Button/Button.module.css +74 -44
  16. package/src/components/interaction/Button/Button.stories.tsx +4 -4
  17. package/src/components/interaction/Button/Button.tsx +5 -5
  18. package/src/components/interaction/Button/index.ts +1 -0
  19. package/src/components/interaction/Command/Command.module.css +53 -38
  20. package/src/components/interaction/Command/Command.spec.tsx +24 -0
  21. package/src/components/interaction/Command/Command.tsx +5 -0
  22. package/src/components/interaction/Dialog/Dialog.mdx +2 -2
  23. package/src/components/interaction/Dialog/Dialog.module.css +20 -10
  24. package/src/components/interaction/Dialog/Dialog.spec.tsx +19 -0
  25. package/src/components/interaction/Dialog/Dialog.tsx +22 -0
  26. package/src/components/interaction/Drawer/Drawer.module.css +11 -11
  27. package/src/components/interaction/Drawer/Drawer.spec.tsx +30 -0
  28. package/src/components/interaction/Drawer/Drawer.tsx +7 -3
  29. package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +24 -19
  30. package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +4 -4
  31. package/src/components/interaction/Popover/Popover.module.css +17 -12
  32. package/src/components/interaction/Popover/Popover.spec.tsx +25 -4
  33. package/src/components/interaction/Popover/Popover.stories.tsx +1 -1
  34. package/src/components/interaction/Popover/Popover.tsx +5 -2
  35. package/src/components/interaction/Rating/Rating.module.css +3 -3
  36. package/src/components/interaction/Toast/Toast.module.css +19 -19
  37. package/src/components/interaction/Toast/Toast.tsx +1 -1
  38. package/src/components/interaction/form/Checkbox/Checkbox.mdx +2 -2
  39. package/src/components/interaction/form/Checkbox/Checkbox.module.css +22 -18
  40. package/src/components/interaction/form/Checkbox/Checkbox.tsx +2 -2
  41. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css +29 -0
  42. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.spec.tsx +87 -0
  43. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.stories.tsx +62 -0
  44. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +46 -0
  45. package/src/components/interaction/form/CheckboxGroup/index.ts +2 -0
  46. package/src/components/interaction/form/Combobox/Combobox.module.css +44 -34
  47. package/src/components/interaction/form/Combobox/Combobox.tsx +0 -1
  48. package/src/components/interaction/form/FileInput/FileInput.module.css +24 -16
  49. package/src/components/interaction/form/FileInput/FileInput.tsx +5 -2
  50. package/src/components/interaction/form/FormInput/FormInput.module.css +21 -16
  51. package/src/components/interaction/form/FormInput/FormInput.tsx +3 -0
  52. package/src/components/interaction/form/NumberInput/NumberInput.module.css +25 -19
  53. package/src/components/interaction/form/NumberInput/NumberInput.tsx +12 -3
  54. package/src/components/interaction/form/Radio/Radio.mdx +3 -3
  55. package/src/components/interaction/form/Radio/Radio.module.css +28 -24
  56. package/src/components/interaction/form/Radio/Radio.stories.tsx +1 -1
  57. package/src/components/interaction/form/Radio/Radio.tsx +2 -2
  58. package/src/components/interaction/form/RadioGroup/RadioGroup.module.css +29 -0
  59. package/src/components/interaction/form/RadioGroup/RadioGroup.spec.tsx +75 -0
  60. package/src/components/interaction/form/RadioGroup/RadioGroup.stories.tsx +64 -0
  61. package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +46 -0
  62. package/src/components/interaction/form/RadioGroup/index.ts +2 -0
  63. package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +5 -5
  64. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +26 -26
  65. package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +4 -0
  66. package/src/components/interaction/form/Select/Select.module.css +24 -19
  67. package/src/components/interaction/form/Select/Select.tsx +3 -0
  68. package/src/components/interaction/form/Slider/Slider.mdx +1 -1
  69. package/src/components/interaction/form/Slider/Slider.module.css +24 -24
  70. package/src/components/interaction/form/Slider/Slider.tsx +1 -0
  71. package/src/components/interaction/form/Switch/Switch.mdx +4 -4
  72. package/src/components/interaction/form/Switch/Switch.module.css +22 -16
  73. package/src/components/interaction/form/Switch/Switch.tsx +1 -1
  74. package/src/components/interaction/form/Textarea/Textarea.module.css +23 -18
  75. package/src/components/interaction/form/Textarea/Textarea.tsx +3 -0
  76. package/src/components/interaction/form/atoms/InputContainer.module.css +2 -2
  77. package/src/components/interaction/form/atoms/Label.module.css +2 -2
  78. package/src/components/interaction/form/atoms/Message.module.css +2 -2
  79. package/src/components/layout/ButtonGroup/ButtonGroup.module.css +2 -2
  80. package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +2 -2
  81. package/src/components/layout/ButtonGroup/ButtonGroup.tsx +8 -3
  82. package/src/components/layout/ButtonGroup/index.ts +1 -0
  83. package/src/components/layout/Card/Card.module.css +30 -16
  84. package/src/components/layout/Card/Card.tsx +1 -1
  85. package/src/components/layout/IconWrapper/IconWrapper.mdx +2 -2
  86. package/src/components/layout/IconWrapper/IconWrapper.module.css +5 -5
  87. package/src/components/layout/IconWrapper/IconWrapper.tsx +4 -3
  88. package/src/components/layout/IconWrapper/index.ts +1 -0
  89. package/src/components/layout/SectionHeader/SectionHeader.module.css +23 -23
  90. package/src/components/layout/SectionHeader/SectionHeader.spec.tsx +2 -2
  91. package/src/components/layout/SectionHeader/SectionHeader.tsx +2 -2
  92. package/src/components/ui/Accordion/Accordion.module.css +23 -18
  93. package/src/components/ui/Alert/Alert.module.css +31 -27
  94. package/src/components/ui/Avatar/Avatar.module.css +6 -6
  95. package/src/components/ui/Avatar/Avatar.tsx +1 -1
  96. package/src/components/ui/Badge/Badge.mdx +2 -2
  97. package/src/components/ui/Badge/Badge.module.css +15 -15
  98. package/src/components/ui/Breadcrumb/Breadcrumb.module.css +11 -11
  99. package/src/components/ui/Calendar/Calendar.module.css +39 -29
  100. package/src/components/ui/Calendar/Calendar.tsx +6 -2
  101. package/src/components/ui/Carousel/Carousel.module.css +15 -10
  102. package/src/components/ui/Carousel/Carousel.stories.tsx +1 -1
  103. package/src/components/ui/Collapsible/Collapsible.mdx +6 -6
  104. package/src/components/ui/Collapsible/Collapsible.module.css +18 -18
  105. package/src/components/ui/DescriptionList/DescriptionList.module.css +8 -8
  106. package/src/components/ui/Link/Link.module.css +14 -14
  107. package/src/components/ui/Loading/Loading.module.css +8 -2
  108. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +27 -23
  109. package/src/components/ui/Pagination/Pagination.module.css +22 -17
  110. package/src/components/ui/Progress/Progress.mdx +1 -1
  111. package/src/components/ui/Progress/Progress.module.css +10 -10
  112. package/src/components/ui/ProgressCircle/ProgressCircle.module.css +8 -8
  113. package/src/components/ui/Separator/Separator.module.css +1 -1
  114. package/src/components/ui/Separator/Separator.tsx +1 -1
  115. package/src/components/ui/Skeleton/Skeleton.module.css +5 -5
  116. package/src/components/ui/SkipLink/SkipLink.module.css +17 -12
  117. package/src/components/ui/Table/Table.module.css +35 -30
  118. package/src/components/ui/Table/Table.tsx +2 -1
  119. package/src/components/ui/Tabs/Tabs.module.css +17 -17
  120. package/src/components/ui/Tabs/Tabs.tsx +0 -1
  121. package/src/components/ui/Tooltip/Tooltip.module.css +12 -12
  122. package/src/components/ui/Tooltip/Tooltip.spec.tsx +3 -3
  123. package/src/components/ui/Tooltip/Tooltip.tsx +10 -2
  124. package/src/components/ui/Typography/Typography.module.css +18 -18
  125. package/src/css/bdc.css +66 -0
  126. package/src/css/index.css +5 -0
  127. package/src/index.ts +7 -0
  128. package/src/stories/DesignSystem/Borders.mdx +158 -0
  129. package/src/stories/DesignSystem/Colors.mdx +196 -0
  130. package/src/stories/DesignSystem/Elevation.mdx +127 -0
  131. package/src/stories/DesignSystem/Grid.mdx +140 -0
  132. package/src/stories/DesignSystem/Motion.mdx +96 -0
  133. package/src/stories/DesignSystem/Overview.mdx +99 -0
  134. package/src/stories/DesignSystem/Spacing.mdx +74 -0
  135. package/src/stories/DesignSystem/Typography.mdx +110 -0
  136. package/src/stories/Introduction.css +2 -2
  137. package/src/stories/Introduction.mdx +7 -7
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@boostdev/design-system-components",
3
- "version": "0.1.18",
3
+ "version": "1.0.1",
4
4
  "description": "BoostDev React component library: accessible, token-driven components built on @boostdev/design-system-foundation",
5
5
  "keywords": [
6
6
  "React",
7
7
  "component",
8
- "library:",
8
+ "library",
9
9
  "accessible",
10
10
  "token-driven",
11
11
  "components",
@@ -36,6 +36,7 @@
36
36
  "require": "./dist/client.cjs"
37
37
  },
38
38
  "./css": "./dist/index.css",
39
+ "./css/bdc": "./src/css/bdc.css",
39
40
  "./css/accordion": "./src/components/ui/Accordion/Accordion.module.css",
40
41
  "./css/alert": "./src/components/ui/Alert/Alert.module.css",
41
42
  "./css/avatar": "./src/components/ui/Avatar/Avatar.module.css",
@@ -43,6 +44,7 @@
43
44
  "./css/breadcrumb": "./src/components/ui/Breadcrumb/Breadcrumb.module.css",
44
45
  "./css/calendar": "./src/components/ui/Calendar/Calendar.module.css",
45
46
  "./css/carousel": "./src/components/ui/Carousel/Carousel.module.css",
47
+ "./css/collapsible": "./src/components/ui/Collapsible/Collapsible.module.css",
46
48
  "./css/description-list": "./src/components/ui/DescriptionList/DescriptionList.module.css",
47
49
  "./css/link": "./src/components/ui/Link/Link.module.css",
48
50
  "./css/loading": "./src/components/ui/Loading/Loading.module.css",
@@ -66,11 +68,14 @@
66
68
  "./css/rating": "./src/components/interaction/Rating/Rating.module.css",
67
69
  "./css/toast": "./src/components/interaction/Toast/Toast.module.css",
68
70
  "./css/form/checkbox": "./src/components/interaction/form/Checkbox/Checkbox.module.css",
71
+ "./css/form/checkbox-group": "./src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css",
69
72
  "./css/form/combobox": "./src/components/interaction/form/Combobox/Combobox.module.css",
70
73
  "./css/form/file-input": "./src/components/interaction/form/FileInput/FileInput.module.css",
71
74
  "./css/form/form-input": "./src/components/interaction/form/FormInput/FormInput.module.css",
72
75
  "./css/form/number-input": "./src/components/interaction/form/NumberInput/NumberInput.module.css",
73
76
  "./css/form/radio": "./src/components/interaction/form/Radio/Radio.module.css",
77
+ "./css/form/radio-group": "./src/components/interaction/form/RadioGroup/RadioGroup.module.css",
78
+ "./css/form/segmented-control": "./src/components/interaction/form/SegmentedControl/SegmentedControl.module.css",
74
79
  "./css/form/select": "./src/components/interaction/form/Select/Select.module.css",
75
80
  "./css/form/slider": "./src/components/interaction/form/Slider/Slider.module.css",
76
81
  "./css/form/switch": "./src/components/interaction/form/Switch/Switch.module.css",
@@ -105,7 +110,7 @@
105
110
  "changelog:unreleased": "pnpm exec git-cliff --unreleased"
106
111
  },
107
112
  "devDependencies": {
108
- "@boostdev/design-system-foundation": "^0.1.13",
113
+ "@boostdev/design-system-foundation": "^1.0.1",
109
114
  "@modelcontextprotocol/sdk": "^1.27.0",
110
115
  "@storybook/addon-docs": "8.6.14",
111
116
  "@storybook/addon-essentials": "^8.6.14",
@@ -135,7 +140,7 @@
135
140
  "vitest": "^3"
136
141
  },
137
142
  "peerDependencies": {
138
- "@boostdev/design-system-foundation": ">=0.1.0",
143
+ "@boostdev/design-system-foundation": ">=1.0.1",
139
144
  "react": ">=18",
140
145
  "react-dom": ">=18"
141
146
  },
@@ -8,8 +8,8 @@ import * as Stories from './Button.stories';
8
8
  Primary interactive element for triggering actions. Renders as `<button>` or `<a>` depending on whether `href` is provided.
9
9
 
10
10
  ## When to use
11
- - The main action on a page or dialog (`primary`)
12
- - Secondary or destructive actions (`secondary`)
11
+ - The main action on a page or dialog (`default`)
12
+ - Secondary or ghost actions (`ghost`)
13
13
  - Navigation that should look like an action (`href` → renders as `<a>`)
14
14
 
15
15
  ## When not to use
@@ -21,11 +21,11 @@ Primary interactive element for triggering actions. Renders as `<button>` or `<a
21
21
  ### All variants & sizes
22
22
  <Canvas of={Stories.AllVariants} />
23
23
 
24
- ### Primary
25
- <Canvas of={Stories.Primary} />
24
+ ### Default
25
+ <Canvas of={Stories.Default} />
26
26
 
27
- ### Secondary
28
- <Canvas of={Stories.Secondary} />
27
+ ### Ghost
28
+ <Canvas of={Stories.Ghost} />
29
29
 
30
30
  ### Sizes
31
31
  <Canvas of={Stories.Small} />
@@ -56,11 +56,8 @@ Primary interactive element for triggering actions. Renders as `<button>` or `<a
56
56
  <tr><th>Variable</th><th>Description</th></tr>
57
57
  </thead>
58
58
  <tbody>
59
- <tr><td>`--button_bg`</td><td>Background colour</td></tr>
60
- <tr><td>`--button_color`</td><td>Text colour</td></tr>
61
- <tr><td>`--button_border-color`</td><td>Border colour</td></tr>
62
- <tr><td>`--button_bg--hover`</td><td>Background on hover</td></tr>
63
- <tr><td>`--button_color--hover`</td><td>Text colour on hover</td></tr>
59
+ <tr><td>`--button_color`</td><td>Identity colour — fills default, outlines ghost. Set this to re-theme.</td></tr>
60
+ <tr><td>`--button_on-color`</td><td>Text/icon colour on the button. Pair with `--button_color`.</td></tr>
64
61
  </tbody>
65
62
  </table>
66
63
 
@@ -1,17 +1,29 @@
1
+ @keyframes fadeZoom {
2
+ 0%, 100% {
3
+ opacity: 1;
4
+ transform: scale(1);
5
+ }
6
+
7
+ 50% {
8
+ opacity: 0.75;
9
+ transform: scale(1.15);
10
+ }
11
+ }
12
+
1
13
  @keyframes pulse {
2
14
  0% {
3
15
  transform: scale(1);
4
- box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color, var(--color_cta)) r g b / 50%);
16
+ box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color, var(--button_color, var(--bds-cta))) r g b / 50%);
5
17
  }
6
18
 
7
19
  70% {
8
20
  transform: scale(1.01);
9
- box-shadow: 0 0 0 var(--space_m) rgb(from var(--button_pulse-color, var(--color_cta)) r g b / 0%);
21
+ box-shadow: 0 0 0 var(--bds-space_m) rgb(from var(--button_pulse-color, var(--button_color, var(--bds-cta))) r g b / 0%);
10
22
  }
11
23
 
12
24
  100% {
13
25
  transform: scale(1);
14
- box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color, var(--color_cta)) r g b / 0%);
26
+ box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color, var(--button_color, var(--bds-cta))) r g b / 0%);
15
27
  }
16
28
  }
17
29
 
@@ -21,54 +33,62 @@
21
33
  all: unset;
22
34
  position: relative;
23
35
  font: inherit;
24
- padding-inline: var(--space_l);
36
+ padding-inline: var(--bds-space_l);
25
37
  display: inline-flex;
26
- gap: var(--space_xs);
27
- border-radius: var(--button_border_radius--s, var(--border_radius--s));
38
+ gap: var(--bds-space_xs);
39
+ border-radius: var(--button_border_radius--s, var(--bds-border_radius--s));
28
40
  align-items: center;
29
41
  cursor: pointer;
30
- scroll-padding-block-end: var(--space_l);
42
+ scroll-padding-block-end: var(--bds-space_l);
31
43
  justify-content: center;
32
44
  height: var(--button_height, 3em);
33
45
  font-size: var(--button_font-size, inherit);
34
46
  white-space: nowrap;
35
47
  line-height: 1;
36
- background-color: var(--button_bg, var(--color_cta));
37
- color: var(--button_text, var(--color_on-cta));
38
- border: 2px solid var(--button_border-color, var(--color_cta));
39
- transition: var(--animation_transition);
40
- }
41
48
 
42
- .button.--primary {
43
- --button_border-color: transparent;
49
+ /* Colour model: override --button_color + --button_on-color to re-theme */
50
+ --button_color: var(--bds-cta);
51
+ --button_on-color: var(--bds-on-cta);
52
+
53
+ background-color: var(--button_bg, var(--button_color));
54
+ color: var(--button_text, var(--button_on-color));
55
+ border: var(--button_border, none);
56
+ --bdc_color: transparent;
57
+
58
+ outline: var(--bdc-outline_width) solid var(--bdc_color);
59
+ outline-offset: var(--bdc-outline_offset);
60
+ transition: var(--bds-animation_transition), --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing);
44
61
  }
45
62
 
46
- .button.--secondary {
47
- --button_bg: var(--color_bg);
48
- --button_text: var(--color_on-bg);
49
- --button_border-color: var(--button_text);
63
+ /* Default: filled with --button_color, no visible border */
64
+ .button.--default { /* inherits base — no overrides needed */ }
65
+
66
+ /* Ghost: transparent background, border and text use --button_color */
67
+ .button.--ghost {
68
+ --button_bg: transparent;
69
+ --button_text: var(--button_color);
70
+ --bdc_color: var(--button_color);
50
71
  }
51
72
 
52
73
  .button.--size_small {
53
74
  --button_height: 2.25em;
54
- --button_font-size: var(--font_size--body--s);
55
- --button_border_radius--s: var(--border_radius--xs);
75
+ --button_font-size: var(--bds-font_size--body--s);
76
+ --button_border_radius--s: var(--bds-border_radius--xs);
56
77
 
57
- padding-inline: var(--space_m);
78
+ padding-inline: var(--bds-space_m);
58
79
  }
59
80
 
60
81
  .button.--size_medium {
61
82
  --button_height: 3em;
62
83
 
63
- padding-inline: var(--space_l);
84
+ padding-inline: var(--bds-space_l);
64
85
  }
65
86
 
66
87
  .button.--size_large {
67
- --button_font-size: var(--font_size--heading-3);
68
-
69
- padding-inline: var(--space_xl);
70
- --button_border_radius--s: var(--border_radius--m);
88
+ --button_font-size: var(--bds-font_size--heading-3);
89
+ --button_border_radius--s: var(--bds-border_radius--m);
71
90
 
91
+ padding-inline: var(--bds-space_xl);
72
92
  }
73
93
 
74
94
  .button.--hasPulse {
@@ -87,42 +107,52 @@
87
107
  flex-shrink: 0;
88
108
  }
89
109
 
90
- .prefix {
110
+ .iconStart {
91
111
  display: flex;
92
112
  align-items: center;
93
- margin-inline-start: calc(var(--space_s) * -1);
94
- transition: var(--animation_transition);
113
+ margin-inline-start: calc(var(--bds-space_s) * -1);
114
+ transition: var(--bds-animation_transition);
95
115
  }
96
116
 
97
- .suffix {
117
+ .iconEnd {
98
118
  display: flex;
99
119
  align-items: center;
100
- margin-inline-end: calc(var(--space_s) * -1);
101
- transition: var(--animation_transition);
120
+ margin-inline-end: calc(var(--bds-space_s) * -1);
121
+ transition: var(--bds-animation_transition);
102
122
  }
103
123
 
104
124
  @media (hover: hover) and (pointer: fine) {
105
- .button:hover .prefix svg {
106
- animation: 2s infinite 'fadeZoom' ease-out;
125
+ .button:hover .iconStart svg {
126
+ animation: fadeZoom 2s ease-out infinite;
107
127
  }
108
128
 
109
- .button:hover .suffix svg {
110
- animation: 2s infinite 'fadeZoom' ease-out;
129
+ .button:hover .iconEnd svg {
130
+ animation: fadeZoom 2s ease-out infinite;
111
131
  }
112
132
  }
113
133
 
114
134
  .button:focus-visible {
115
- outline-offset: var(--outline_offset);
116
- outline: var(--outline_default);
117
- border-radius: var(--border_radius--s);
135
+ outline: var(--bds-outline_default);
136
+ outline-offset: var(--bds-outline_offset);
137
+ border-radius: var(--bds-border_radius--s);
138
+ }
139
+
140
+ /* Default hover: outline appears, bg becomes page surface */
141
+ @media (hover: hover) and (pointer: fine) {
142
+ .button.--default:hover {
143
+ --button_bg: var(--bds-bg);
144
+ --button_text: var(--bds-interactive);
145
+ --button_pulse-color: var(--bds-interactive);
146
+ --bdc_color: currentcolor;
147
+ }
118
148
  }
119
149
 
150
+ /* Ghost hover: fills with --button_color */
120
151
  @media (hover: hover) and (pointer: fine) {
121
- .button:hover {
122
- --button_bg: var(--color_bg);
123
- --button_text: var(--color_interactive);
124
- --button_pulse-color: var(--color_interactive);
125
- --button_border-color: currentcolor;
152
+ .button.--ghost:hover {
153
+ --button_bg: var(--button_color);
154
+ --button_text: var(--button_on-color);
155
+ --bdc_color: transparent;
126
156
  }
127
157
  }
128
158
  }
@@ -5,7 +5,7 @@ const meta = {
5
5
  title: 'Interaction/Button',
6
6
  component: Button,
7
7
  argTypes: {
8
- variant: { control: 'select', options: ['primary', 'secondary'] },
8
+ variant: { control: 'select', options: ['default', 'ghost'] },
9
9
  size: { control: 'select', options: ['small', 'medium', 'large'] },
10
10
  onClick: { action: 'clicked' },
11
11
  },
@@ -14,8 +14,8 @@ const meta = {
14
14
  export default meta;
15
15
  type Story = StoryObj<typeof meta>;
16
16
 
17
- export const Primary: Story = { args: { children: 'Primary', variant: 'primary' } };
18
- export const Secondary: Story = { args: { children: 'Secondary', variant: 'secondary' } };
17
+ export const Default: Story = { args: { children: 'Default', variant: 'default' } };
18
+ export const Ghost: Story = { args: { children: 'Ghost', variant: 'ghost' } };
19
19
  export const Small: Story = { args: { children: 'Small', size: 'small' } };
20
20
  export const Medium: Story = { args: { children: 'Medium', size: 'medium' } };
21
21
  export const Large: Story = { args: { children: 'Large', size: 'large' } };
@@ -32,7 +32,7 @@ export const WithIcons: Story = {
32
32
  export const AllVariants: Story = {
33
33
  render: () => (
34
34
  <div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap', alignItems: 'center' }}>
35
- {(['primary', 'secondary'] as const).map(v => (
35
+ {(['default', 'ghost'] as const).map(v => (
36
36
  ['small', 'medium', 'large'].map(s => (
37
37
  <Button key={`${v}-${s}`} variant={v} size={s as 'small' | 'medium' | 'large'}>{v} {s}</Button>
38
38
  ))
@@ -2,9 +2,9 @@ import {ButtonHTMLAttributes, MouseEventHandler, ReactNode} from 'react';
2
2
  import css from './Button.module.css';
3
3
  import { cn } from '@boostdev/design-system-foundation';
4
4
 
5
- interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement | HTMLAnchorElement> {
5
+ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement | HTMLAnchorElement> {
6
6
  href?: string;
7
- variant?: 'primary' | 'secondary';
7
+ variant?: 'default' | 'ghost';
8
8
  type?: 'button' | 'submit' | 'reset';
9
9
  size?: 'small' | 'medium' | 'large';
10
10
  iconStart?: ReactNode;
@@ -23,7 +23,7 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement | HTMLAncho
23
23
  export function Button({
24
24
  children,
25
25
  className,
26
- variant = 'primary',
26
+ variant = 'default',
27
27
  type = 'button',
28
28
  iconStart,
29
29
  iconEnd,
@@ -58,9 +58,9 @@ export function Button({
58
58
 
59
59
  const content = (
60
60
  <>
61
- {Boolean(iconStart) && <span className={css.prefix}>{iconStart}</span>}
61
+ {Boolean(iconStart) && <span className={css.iconStart}>{iconStart}</span>}
62
62
  {children}
63
- {Boolean(iconEnd) && <span className={css.suffix}>{iconEnd}</span>}
63
+ {Boolean(iconEnd) && <span className={css.iconEnd}>{iconEnd}</span>}
64
64
  </>
65
65
  );
66
66
 
@@ -1 +1,2 @@
1
1
  export { Button } from './Button';
2
+ export type { ButtonProps } from './Button';
@@ -2,7 +2,7 @@
2
2
  .dialog {
3
3
  padding: 0;
4
4
  border: none;
5
- border-radius: var(--border_radius--m);
5
+ border-radius: var(--bds-border_radius--m);
6
6
  background: transparent;
7
7
  width: min(40rem, 90vw);
8
8
  max-height: 70vh;
@@ -11,27 +11,32 @@
11
11
  }
12
12
 
13
13
  .dialog::backdrop {
14
- background-color: var(--command_backdrop-color, rgb(0 0 0 / 50%));
14
+ background-color: var(--color_backdrop, rgb(0 0 0 / 50%));
15
15
  backdrop-filter: blur(3px);
16
16
  }
17
17
 
18
18
  .palette {
19
19
  display: flex;
20
20
  flex-direction: column;
21
- background-color: var(--command_color, var(--color_bg));
22
- color: var(--command_on-color, var(--color_on-bg));
23
- border-radius: var(--border_radius--m);
24
- box-shadow: var(--shadow_xl);
21
+ background-color: var(--command_color, var(--bds-bg));
22
+ color: var(--command_on-color, var(--bds-on-bg));
23
+ border-radius: var(--bds-border_radius--m);
24
+ box-shadow: var(--command_shadow, var(--bds-shadow_xl));
25
25
  overflow: hidden;
26
26
  max-height: 70vh;
27
- border: 1px solid currentcolor;
27
+ border: var(--command_border, none);
28
+ --bdc_color: currentcolor;
29
+ --bdc-outline_radius: var(--bds-border_radius--m);
30
+
31
+ outline: var(--bdc-outline_width) solid var(--bdc_color);
32
+ outline-offset: var(--bdc-outline_offset);
28
33
  }
29
34
 
30
35
  .searchRow {
31
36
  display: flex;
32
37
  align-items: center;
33
- gap: var(--space_xs);
34
- padding: var(--space_s) var(--space_m);
38
+ gap: var(--bds-space_xs);
39
+ padding: var(--bds-space_s) var(--bds-space_m);
35
40
  border-block-end: 1px solid currentcolor;
36
41
  flex-shrink: 0;
37
42
  }
@@ -39,7 +44,7 @@
39
44
  .searchIcon {
40
45
  width: 1.25rem;
41
46
  height: 1.25rem;
42
- color: var(--color_on-bg--muted);
47
+ color: var(--bds-on-bg);
43
48
  flex-shrink: 0;
44
49
  }
45
50
 
@@ -47,31 +52,36 @@
47
52
  flex: 1;
48
53
  border: none;
49
54
  outline: none;
50
- font-family: var(--font_family--body);
51
- font-size: var(--font_size--body);
52
- color: var(--color_on-bg);
55
+ font-family: var(--bds-font_family--body);
56
+ font-size: var(--bds-font_size--body);
57
+ color: var(--bds-on-bg);
53
58
  background: transparent;
54
59
  min-width: 0;
55
60
  }
56
61
 
57
62
  .search::placeholder {
58
- color: var(--color_on-bg--muted);
63
+ color: var(--bds-on-bg);
59
64
  }
60
65
 
61
66
  .escHint {
62
- font-size: var(--font_size--body--s);
63
- color: var(--color_on-bg--muted);
64
- border: 1px solid currentcolor;
65
- border-radius: var(--border_radius--xs);
67
+ font-size: var(--bds-font_size--body--s);
68
+ color: var(--bds-on-bg);
69
+ border-radius: var(--bds-border_radius--xs);
66
70
  padding: 0.1em 0.4em;
67
71
  flex-shrink: 0;
72
+
73
+ --bdc_color: currentcolor;
74
+
75
+ border: none;
76
+ outline: var(--bdc-outline_width) solid var(--bdc_color);
77
+ outline-offset: var(--bdc-outline_offset);
68
78
  }
69
79
 
70
80
  .list {
71
81
  overflow-y: auto;
72
82
  list-style: none;
73
83
  margin: 0;
74
- padding: var(--space_xs) 0;
84
+ padding: var(--bds-space_xs) 0;
75
85
  }
76
86
 
77
87
  .groupList {
@@ -81,10 +91,10 @@
81
91
  }
82
92
 
83
93
  .group {
84
- padding: var(--space_xs) var(--space_m);
85
- font-size: var(--font_size--body--s);
86
- font-weight: var(--font_weight--semibold);
87
- color: var(--color_on-bg--muted);
94
+ padding: var(--bds-space_xs) var(--bds-space_m);
95
+ font-size: var(--bds-font_size--body--s);
96
+ font-weight: var(--bds-font_weight--semibold);
97
+ color: var(--bds-on-bg);
88
98
  text-transform: uppercase;
89
99
  letter-spacing: 0.05em;
90
100
  }
@@ -92,39 +102,44 @@
92
102
  .item {
93
103
  display: flex;
94
104
  align-items: center;
95
- gap: var(--space_s);
96
- padding: var(--space_s) var(--space_m);
105
+ gap: var(--bds-space_s);
106
+ padding: var(--bds-space_s) var(--bds-space_m);
97
107
  cursor: pointer;
98
- transition: background-color var(--animation_transition-duration) var(--animation_easing);
108
+ transition: background-color var(--bds-animation_transition-duration) var(--bds-animation_easing);
99
109
  }
100
110
 
101
111
  .itemActive {
102
- background-color: var(--color_bg--subtle);
112
+ background-color: var(--bds-bg--subtle);
103
113
  }
104
114
 
105
115
  .itemLabel {
106
116
  flex: 1;
107
- font-size: var(--font_size--body);
108
- color: var(--color_on-bg);
117
+ font-size: var(--bds-font_size--body);
118
+ color: var(--bds-on-bg);
109
119
  }
110
120
 
111
121
  .itemDesc {
112
- font-size: var(--font_size--body--s);
113
- color: var(--color_on-bg--muted);
122
+ font-size: var(--bds-font_size--body--s);
123
+ color: var(--bds-on-bg);
114
124
  }
115
125
 
116
126
  .shortcut {
117
- font-size: var(--font_size--body--s);
118
- color: var(--color_on-bg--muted);
119
- border: 1px solid currentcolor;
120
- border-radius: var(--border_radius--xs);
127
+ font-size: var(--bds-font_size--body--s);
128
+ color: var(--bds-on-bg);
129
+ border-radius: var(--bds-border_radius--xs);
121
130
  padding: 0.1em 0.4em;
131
+
132
+ --bdc_color: currentcolor;
133
+
134
+ border: none;
135
+ outline: var(--bdc-outline_width) solid var(--bdc_color);
136
+ outline-offset: var(--bdc-outline_offset);
122
137
  }
123
138
 
124
139
  .empty {
125
- padding: var(--space_l) var(--space_m);
140
+ padding: var(--bds-space_l) var(--bds-space_m);
126
141
  text-align: center;
127
- color: var(--color_on-bg--muted);
128
- font-size: var(--font_size--body);
142
+ color: var(--bds-on-bg);
143
+ font-size: var(--bds-font_size--body);
129
144
  }
130
145
  }
@@ -75,4 +75,28 @@ describe('Command', () => {
75
75
  unmount();
76
76
  expect(document.body.style.overflow).toBe('');
77
77
  });
78
+
79
+ it('has aria-modal="true" when open', () => {
80
+ render(<Command isOpen items={items} onClose={() => {}} />);
81
+ const dialog = document.querySelector('dialog');
82
+ expect(dialog).toHaveAttribute('aria-modal', 'true');
83
+ });
84
+
85
+ it('has aria-label on the dialog', () => {
86
+ render(<Command isOpen items={items} onClose={() => {}} />);
87
+ const dialog = document.querySelector('dialog');
88
+ expect(dialog).toHaveAttribute('aria-label', 'Command palette');
89
+ });
90
+
91
+ it('restores focus to trigger element on close', () => {
92
+ const trigger = document.createElement('button');
93
+ document.body.appendChild(trigger);
94
+ trigger.focus();
95
+
96
+ const { rerender } = render(<Command isOpen items={items} onClose={() => {}} />);
97
+ rerender(<Command isOpen={false} items={items} onClose={() => {}} />);
98
+ expect(document.activeElement).toBe(trigger);
99
+
100
+ document.body.removeChild(trigger);
101
+ });
78
102
  });
@@ -51,10 +51,13 @@ export function Command({
51
51
  return map;
52
52
  }, [filtered]);
53
53
 
54
+ const triggerRef = useRef<Element | null>(null);
55
+
54
56
  useEffect(() => {
55
57
  const dialog = dialogRef.current;
56
58
  if (!dialog) return;
57
59
  if (isOpen) {
60
+ triggerRef.current = document.activeElement;
58
61
  dialog.showModal();
59
62
  document.body.style.overflow = 'hidden';
60
63
  setQuery('');
@@ -63,6 +66,7 @@ export function Command({
63
66
  } else if (dialog.open) {
64
67
  dialog.close();
65
68
  document.body.style.overflow = '';
69
+ (triggerRef.current as HTMLElement | null)?.focus();
66
70
  }
67
71
  return () => {
68
72
  document.body.style.overflow = '';
@@ -104,6 +108,7 @@ export function Command({
104
108
  ref={dialogRef}
105
109
  className={cn(css.dialog, className)}
106
110
  aria-label="Command palette"
111
+ aria-modal="true"
107
112
  onCancel={handleCancel}
108
113
  >
109
114
  <div className={css.palette} onKeyDown={handleKeyDown}>
@@ -35,8 +35,8 @@ Modal overlay using the native `<dialog>` element. Traps focus inside the dialog
35
35
  <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
36
36
  </thead>
37
37
  <tbody>
38
- <tr><td>`--dialog_color`</td><td>`var(--color_grey--subtle)`</td><td>Dialog background</td></tr>
39
- <tr><td>`--dialog_on-color`</td><td>`var(--color_on-grey--subtle)`</td><td>Dialog text colour</td></tr>
38
+ <tr><td>`--dialog_color`</td><td>`var(--bds-grey--subtle)`</td><td>Dialog background</td></tr>
39
+ <tr><td>`--dialog_on-color`</td><td>`var(--bds-on-grey--subtle)`</td><td>Dialog text colour</td></tr>
40
40
  <tr><td>`--dialog_backdrop-color`</td><td>`rgb(0 0 0 / 50%)`</td><td>Backdrop overlay colour</td></tr>
41
41
  </tbody>
42
42
  </table>
@@ -1,11 +1,17 @@
1
1
  @layer component {
2
2
  .dialog {
3
- font-size: var(--font_size--body--s);
4
- border: 2px solid currentcolor;
5
- background-color: var(--dialog_color ,var(--color_grey--subtle));
6
- color: var(--dialog_on-color, var(--color_on-grey--subtle));
7
- border-radius: var(--border_radius--m);
8
- padding: var(--space_l);
3
+ font-size: var(--bds-font_size--body--s);
4
+ border: var(--dialog_border, none);
5
+ --bdc_color: currentcolor;
6
+ --bdc-outline_radius: var(--bds-border_radius--m);
7
+
8
+ outline: var(--bdc-outline_width) solid var(--bdc_color);
9
+ outline-offset: var(--bdc-outline_offset);
10
+ border-radius: var(--dialog_radius, var(--bdc-outline_radius));
11
+ box-shadow: var(--dialog_shadow, var(--bdc-outline_shadow));
12
+ background-color: var(--dialog_color ,var(--bds-grey--subtle));
13
+ color: var(--dialog_on-color, var(--bds-on-grey--subtle));
14
+ padding: var(--bds-space_l);
9
15
  max-width: 90svw;
10
16
  max-height: 90svh;
11
17
  overflow: visible;
@@ -13,7 +19,7 @@
13
19
 
14
20
  .dialogContent {
15
21
  display: grid;
16
- gap: var(--grid_gap);
22
+ gap: var(--bds-grid_gap);
17
23
  overflow-y: auto;
18
24
  min-height: 0;
19
25
  flex: 1;
@@ -33,9 +39,13 @@
33
39
  align-items: center;
34
40
  justify-content: center;
35
41
  border-radius: 50%;
36
- background-color: var(--color_bg);
42
+ background-color: var(--bds-bg);
37
43
  color: currentcolor;
38
- border: 2px solid currentcolor;
44
+ border: var(--dialog_close-border, none);
45
+ --bdc_color: currentcolor;
46
+
47
+ outline: var(--bdc-outline_width) solid var(--bdc_color);
48
+ outline-offset: var(--bdc-outline_offset);
39
49
  cursor: pointer;
40
50
  }
41
51
 
@@ -45,7 +55,7 @@
45
55
  }
46
56
 
47
57
  .dialog::backdrop {
48
- background-color: var(--dialog_backdrop-color, rgb(0 0 0 / 50%));
58
+ background-color: var(--color_backdrop, rgb(0 0 0 / 50%));
49
59
  backdrop-filter: blur(3px);
50
60
  }
51
61
  }