@boostdev/design-system-components 0.1.12 → 0.1.14

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 (121) hide show
  1. package/AGENTS.md +14 -2
  2. package/README.md +35 -5
  3. package/dist/client.cjs +54 -50
  4. package/dist/client.css +498 -509
  5. package/dist/client.d.cts +5 -3
  6. package/dist/client.d.ts +5 -3
  7. package/dist/client.js +54 -50
  8. package/dist/index.cjs +54 -50
  9. package/dist/index.css +498 -509
  10. package/dist/index.d.cts +5 -3
  11. package/dist/index.d.ts +5 -3
  12. package/dist/index.js +54 -50
  13. package/package.json +6 -2
  14. package/src/components/interaction/Button/Button.mdx +73 -0
  15. package/src/components/interaction/Button/Button.module.css +12 -20
  16. package/src/components/interaction/Button/Button.stories.tsx +0 -1
  17. package/src/components/interaction/Command/Command.mdx +28 -0
  18. package/src/components/interaction/Command/Command.module.css +2 -1
  19. package/src/components/interaction/Command/Command.stories.tsx +3 -3
  20. package/src/components/interaction/Dialog/Dialog.mdx +57 -0
  21. package/src/components/interaction/Dialog/Dialog.module.css +6 -0
  22. package/src/components/interaction/Dialog/Dialog.stories.tsx +0 -1
  23. package/src/components/interaction/Drawer/Drawer.mdx +35 -0
  24. package/src/components/interaction/Drawer/Drawer.module.css +3 -9
  25. package/src/components/interaction/Drawer/Drawer.stories.tsx +3 -3
  26. package/src/components/interaction/Drawer/Drawer.tsx +5 -3
  27. package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +40 -0
  28. package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +5 -5
  29. package/src/components/interaction/Popover/Popover.mdx +34 -0
  30. package/src/components/interaction/Popover/Popover.stories.tsx +3 -3
  31. package/src/components/interaction/Rating/Rating.mdx +38 -0
  32. package/src/components/interaction/Rating/Rating.stories.tsx +0 -1
  33. package/src/components/interaction/Toast/Toast.mdx +78 -0
  34. package/src/components/interaction/Toast/Toast.module.css +21 -7
  35. package/src/components/interaction/Toast/Toast.stories.tsx +0 -1
  36. package/src/components/interaction/form/Checkbox/Checkbox.mdx +57 -0
  37. package/src/components/interaction/form/Checkbox/Checkbox.stories.tsx +0 -1
  38. package/src/components/interaction/form/Combobox/Combobox.mdx +37 -0
  39. package/src/components/interaction/form/Combobox/Combobox.stories.tsx +0 -1
  40. package/src/components/interaction/form/FileInput/FileInput.mdx +38 -0
  41. package/src/components/interaction/form/FileInput/FileInput.stories.tsx +0 -1
  42. package/src/components/interaction/form/FormInput/FormInput.mdx +54 -0
  43. package/src/components/interaction/form/FormInput/FormInput.stories.tsx +0 -1
  44. package/src/components/interaction/form/NumberInput/NumberInput.mdx +38 -0
  45. package/src/components/interaction/form/NumberInput/NumberInput.stories.tsx +0 -1
  46. package/src/components/interaction/form/Radio/Radio.mdx +60 -0
  47. package/src/components/interaction/form/Radio/Radio.module.css +24 -11
  48. package/src/components/interaction/form/Radio/Radio.stories.tsx +5 -5
  49. package/src/components/interaction/form/Radio/Radio.tsx +6 -2
  50. package/src/components/interaction/form/Select/Select.mdx +58 -0
  51. package/src/components/interaction/form/Select/Select.stories.tsx +0 -1
  52. package/src/components/interaction/form/Slider/Slider.mdx +58 -0
  53. package/src/components/interaction/form/Slider/Slider.stories.tsx +0 -1
  54. package/src/components/interaction/form/Switch/Switch.mdx +61 -0
  55. package/src/components/interaction/form/Switch/Switch.module.css +6 -10
  56. package/src/components/interaction/form/Switch/Switch.stories.tsx +0 -1
  57. package/src/components/interaction/form/Textarea/Textarea.mdx +41 -0
  58. package/src/components/interaction/form/Textarea/Textarea.stories.tsx +0 -1
  59. package/src/components/layout/ButtonGroup/ButtonGroup.mdx +47 -0
  60. package/src/components/layout/ButtonGroup/ButtonGroup.module.css +10 -10
  61. package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +1 -2
  62. package/src/components/layout/Card/Card.mdx +58 -0
  63. package/src/components/layout/Card/Card.stories.tsx +0 -1
  64. package/src/components/layout/IconWrapper/IconWrapper.mdx +39 -0
  65. package/src/components/layout/IconWrapper/IconWrapper.stories.tsx +0 -1
  66. package/src/components/layout/SectionHeader/SectionHeader.mdx +38 -0
  67. package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +0 -1
  68. package/src/components/ui/Accordion/Accordion.mdx +40 -0
  69. package/src/components/ui/Accordion/Accordion.stories.tsx +0 -1
  70. package/src/components/ui/Alert/Alert.mdx +63 -0
  71. package/src/components/ui/Alert/Alert.module.css +3 -7
  72. package/src/components/ui/Alert/Alert.stories.tsx +0 -1
  73. package/src/components/ui/Avatar/Avatar.mdx +53 -0
  74. package/src/components/ui/Avatar/Avatar.module.css +4 -8
  75. package/src/components/ui/Avatar/Avatar.stories.tsx +0 -1
  76. package/src/components/ui/Badge/Badge.mdx +62 -0
  77. package/src/components/ui/Badge/Badge.module.css +4 -10
  78. package/src/components/ui/Badge/Badge.stories.tsx +0 -1
  79. package/src/components/ui/Breadcrumb/Breadcrumb.mdx +36 -0
  80. package/src/components/ui/Breadcrumb/Breadcrumb.stories.tsx +0 -1
  81. package/src/components/ui/Calendar/Calendar.mdx +34 -0
  82. package/src/components/ui/Calendar/Calendar.stories.tsx +0 -1
  83. package/src/components/ui/Carousel/Carousel.mdx +31 -0
  84. package/src/components/ui/Carousel/Carousel.stories.tsx +0 -1
  85. package/src/components/ui/DescriptionList/DescriptionList.mdx +28 -0
  86. package/src/components/ui/DescriptionList/DescriptionList.stories.tsx +0 -1
  87. package/src/components/ui/Link/Link.mdx +44 -0
  88. package/src/components/ui/Link/Link.module.css +2 -5
  89. package/src/components/ui/Link/Link.stories.tsx +0 -1
  90. package/src/components/ui/Loading/Loading.mdx +52 -0
  91. package/src/components/ui/Loading/Loading.stories.tsx +0 -1
  92. package/src/components/ui/NotificationBanner/NotificationBanner.mdx +45 -0
  93. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +3 -7
  94. package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +2 -2
  95. package/src/components/ui/Pagination/Pagination.mdx +39 -0
  96. package/src/components/ui/Pagination/Pagination.module.css +3 -7
  97. package/src/components/ui/Pagination/Pagination.stories.tsx +0 -1
  98. package/src/components/ui/Progress/Progress.mdx +52 -0
  99. package/src/components/ui/Progress/Progress.module.css +1 -3
  100. package/src/components/ui/Progress/Progress.stories.tsx +0 -1
  101. package/src/components/ui/ProgressCircle/ProgressCircle.mdx +46 -0
  102. package/src/components/ui/ProgressCircle/ProgressCircle.stories.tsx +0 -1
  103. package/src/components/ui/Separator/Separator.mdx +29 -0
  104. package/src/components/ui/Separator/Separator.stories.tsx +0 -1
  105. package/src/components/ui/Skeleton/Skeleton.mdx +36 -0
  106. package/src/components/ui/Skeleton/Skeleton.stories.tsx +0 -1
  107. package/src/components/ui/SkipLink/SkipLink.mdx +34 -0
  108. package/src/components/ui/SkipLink/SkipLink.stories.tsx +0 -1
  109. package/src/components/ui/Table/Table.mdx +38 -0
  110. package/src/components/ui/Table/Table.stories.tsx +0 -1
  111. package/src/components/ui/Tabs/Tabs.mdx +40 -0
  112. package/src/components/ui/Tabs/Tabs.module.css +2 -5
  113. package/src/components/ui/Tabs/Tabs.stories.tsx +0 -1
  114. package/src/components/ui/Tooltip/Tooltip.mdx +43 -0
  115. package/src/components/ui/Tooltip/Tooltip.module.css +2 -5
  116. package/src/components/ui/Tooltip/Tooltip.stories.tsx +6 -6
  117. package/src/components/ui/Typography/Typography.mdx +41 -0
  118. package/src/components/ui/Typography/Typography.stories.tsx +0 -1
  119. package/src/static/logo.svg +8 -0
  120. package/src/stories/Introduction.css +17 -0
  121. package/src/stories/Introduction.mdx +129 -0
@@ -0,0 +1,73 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Button.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Button
7
+
8
+ Primary interactive element for triggering actions. Renders as `<button>` or `<a>` depending on whether `href` is provided.
9
+
10
+ ## When to use
11
+ - The main action on a page or dialog (`primary`)
12
+ - Secondary or destructive actions (`secondary`)
13
+ - Navigation that should look like an action (`href` → renders as `<a>`)
14
+
15
+ ## When not to use
16
+ - Navigation to another page when it reads naturally as a link — use `Link` instead
17
+ - When the action is purely icon-based without a label — ensure `aria-label` is set
18
+
19
+ ## Examples
20
+
21
+ ### All variants & sizes
22
+ <Canvas of={Stories.AllVariants} />
23
+
24
+ ### Primary
25
+ <Canvas of={Stories.Primary} />
26
+
27
+ ### Secondary
28
+ <Canvas of={Stories.Secondary} />
29
+
30
+ ### Sizes
31
+ <Canvas of={Stories.Small} />
32
+ <Canvas of={Stories.Medium} />
33
+ <Canvas of={Stories.Large} />
34
+
35
+ ### As a link
36
+ <Canvas of={Stories.AsLink} />
37
+
38
+ ### With icons
39
+ <Canvas of={Stories.WithIcons} />
40
+
41
+ ### Pulse (CTA emphasis)
42
+ <Canvas of={Stories.WithPulse} />
43
+
44
+ ### Disabled
45
+ <Canvas of={Stories.Disabled} />
46
+
47
+ ## Props
48
+
49
+ <ArgTypes of={Stories} />
50
+
51
+ ## CSS variables
52
+
53
+
54
+ <table>
55
+ <thead>
56
+ <tr><th>Variable</th><th>Description</th></tr>
57
+ </thead>
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>
64
+ </tbody>
65
+ </table>
66
+
67
+
68
+ ## Accessibility
69
+
70
+ - Always provide children or `aria-label` for icon-only buttons
71
+ - Disabled state sets `aria-disabled="true"` and removes the element from the tab order
72
+ - When rendered as `<a>`, `href` must point to a valid destination
73
+ - `hasPulse` animation respects `prefers-reduced-motion`
@@ -1,55 +1,45 @@
1
1
  @keyframes pulse {
2
2
  0% {
3
3
  transform: scale(1);
4
- box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color) r g b / 50%);
4
+ box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color, var(--color_cta)) r g b / 50%);
5
5
  }
6
6
 
7
7
  70% {
8
8
  transform: scale(1.01);
9
- box-shadow: 0 0 0 var(--space_m) rgb(from var(--button_pulse-color) r g b / 0%);
9
+ box-shadow: 0 0 0 var(--space_m) rgb(from var(--button_pulse-color, var(--color_cta)) r g b / 0%);
10
10
  }
11
11
 
12
12
  100% {
13
13
  transform: scale(1);
14
- box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color) r g b / 0%);
14
+ box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color, var(--color_cta)) r g b / 0%);
15
15
  }
16
16
  }
17
17
 
18
18
  @layer component {
19
19
  .button,
20
20
  .button[href] {
21
- /* Component tokens — override from a parent context or inline style */
22
- --button_height: 3em;
23
- --button_font-size: inherit;
24
- --button_bg: var(--color_cta);
25
- --button_text: var(--color_on-cta);
26
- --button_border-color: var(--color_cta);
27
- --button_pulse-color: var(--button_bg);
28
-
29
21
  all: unset;
30
22
  position: relative;
31
23
  font: inherit;
32
24
  padding-inline: var(--space_l);
33
25
  display: inline-flex;
34
26
  gap: var(--space_xs);
35
- border-radius: var(--border_radius--s);
27
+ border-radius: var(--button_border_radius--s, var(--border_radius--s));
36
28
  align-items: center;
37
29
  cursor: pointer;
38
30
  scroll-padding-block-end: var(--space_l);
39
31
  justify-content: center;
40
- height: var(--button_height);
41
- font-size: var(--button_font-size);
32
+ height: var(--button_height, 3em);
33
+ font-size: var(--button_font-size, inherit);
42
34
  white-space: nowrap;
43
35
  line-height: 1;
44
- background-color: var(--button_bg);
45
- color: var(--button_text);
46
- border: 2px solid var(--button_border-color);
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));
47
39
  transition: var(--animation_transition);
48
40
  }
49
41
 
50
42
  .button.--primary {
51
- --button_bg: var(--color_cta);
52
- --button_text: var(--color_on-cta);
53
43
  --button_border-color: transparent;
54
44
  }
55
45
 
@@ -62,6 +52,7 @@
62
52
  .button.--size_small {
63
53
  --button_height: 2.25em;
64
54
  --button_font-size: var(--font_size--body--s);
55
+ --button_border_radius--s: var(--border_radius--xs);
65
56
 
66
57
  padding-inline: var(--space_m);
67
58
  }
@@ -73,10 +64,11 @@
73
64
  }
74
65
 
75
66
  .button.--size_large {
76
- --button_height: 3.75em;
77
67
  --button_font-size: var(--font_size--heading-3);
78
68
 
79
69
  padding-inline: var(--space_xl);
70
+ --button_border_radius--s: var(--border_radius--m);
71
+
80
72
  }
81
73
 
82
74
  .button.--hasPulse {
@@ -4,7 +4,6 @@ import { Button } from './Button';
4
4
  const meta = {
5
5
  title: 'Interaction/Button',
6
6
  component: Button,
7
- tags: ['autodocs'],
8
7
  argTypes: {
9
8
  variant: { control: 'select', options: ['primary', 'secondary'] },
10
9
  size: { control: 'select', options: ['small', 'medium', 'large'] },
@@ -0,0 +1,28 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Command.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Command
7
+
8
+ Keyboard-first command palette for searching and executing actions. Commonly triggered by `⌘K`.
9
+
10
+ ## When to use
11
+ - Applications with many actions that power users access via keyboard
12
+ - Global search combining navigation, actions, and settings
13
+
14
+ ## Examples
15
+
16
+ ### Default
17
+ <Canvas of={Stories.Default} />
18
+
19
+ ## Props
20
+
21
+ <ArgTypes of={Stories} />
22
+
23
+ ## Accessibility
24
+
25
+ - Input has `role="combobox"` with `aria-controls` pointing to the results list
26
+ - Results list uses `role="listbox"` with `role="option"` items
27
+ - `aria-activedescendant` tracks the highlighted option
28
+ - Keyboard: `↑`/`↓` to navigate, `Enter` to execute, `Escape` to dismiss
@@ -11,7 +11,8 @@
11
11
  }
12
12
 
13
13
  .dialog::backdrop {
14
- background: rgb(0 0 0 / 50%);
14
+ background-color: var(--command_backdrop-color, rgb(0 0 0 / 50%));
15
+ backdrop-filter: blur(3px);
15
16
  }
16
17
 
17
18
  .palette {
@@ -2,11 +2,11 @@ import { useState } from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { Command } from './Command';
4
4
  import type { CommandItem } from './Command';
5
+ import { Button } from '../Button/Button';
5
6
 
6
7
  const meta = {
7
8
  title: 'Interaction/Command',
8
9
  component: Command,
9
- tags: ['autodocs'],
10
10
  } satisfies Meta<typeof Command>;
11
11
 
12
12
  export default meta;
@@ -25,9 +25,9 @@ export const Default: Story = {
25
25
  const [open, setOpen] = useState(false);
26
26
  return (
27
27
  <>
28
- <button type="button" onClick={() => setOpen(true)}>
28
+ <Button onClick={() => setOpen(true)}>
29
29
  Open Command Palette <kbd>⌘K</kbd>
30
- </button>
30
+ </Button>
31
31
  <Command isOpen={open} items={items} onClose={() => setOpen(false)} />
32
32
  </>
33
33
  );
@@ -0,0 +1,57 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Dialog.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Dialog
7
+
8
+ Modal overlay using the native `<dialog>` element. Traps focus inside the dialog while open and renders a dimmed, blurred backdrop.
9
+
10
+ ## When to use
11
+ - Confirmations, alerts, and short focused tasks that need the user's attention before continuing
12
+ - Forms that are secondary to the main page flow
13
+
14
+ ## When not to use
15
+ - Complex workflows with many steps — consider a dedicated page or a `Drawer`
16
+ - Non-critical information — use a `Popover` or inline content instead
17
+
18
+ ## Examples
19
+
20
+ ### Default (open)
21
+ <Canvas of={Stories.Default} />
22
+
23
+ ### Interactive (controlled)
24
+ <Canvas of={Stories.Interactive} />
25
+
26
+ ## Props
27
+
28
+ <ArgTypes of={Stories} />
29
+
30
+ ## CSS variables
31
+
32
+
33
+ <table>
34
+ <thead>
35
+ <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
36
+ </thead>
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>
40
+ <tr><td>`--dialog_backdrop-color`</td><td>`rgb(0 0 0 / 50%)`</td><td>Backdrop overlay colour</td></tr>
41
+ </tbody>
42
+ </table>
43
+
44
+
45
+ ## Behaviour
46
+
47
+ - Opens via `showModal()` — provides native focus trapping and `Escape` key to close
48
+ - Backdrop click calls `onClose`
49
+ - Body scroll is locked while open (`overflow: hidden`)
50
+ - `onClose` is also called on `Escape` via the native `cancel` event
51
+
52
+ ## Accessibility
53
+
54
+ - Native `<dialog>` provides `role="dialog"` and `aria-modal="true"` automatically
55
+ - Focus is moved to the first focusable element inside on open
56
+ - `Escape` key closes the dialog
57
+ - Add `aria-labelledby` pointing to the dialog title for screen readers
@@ -8,6 +8,7 @@
8
8
  padding: var(--space_l);
9
9
  max-width: 90svw;
10
10
  max-height: 90svh;
11
+ overflow: visible;
11
12
  }
12
13
 
13
14
  .dialogContent {
@@ -42,4 +43,9 @@
42
43
  width: 1.25em;
43
44
  height: 1.25em;
44
45
  }
46
+
47
+ .dialog::backdrop {
48
+ background-color: var(--dialog_backdrop-color, rgb(0 0 0 / 50%));
49
+ backdrop-filter: blur(3px);
50
+ }
45
51
  }
@@ -6,7 +6,6 @@ import { Button } from '../Button/Button';
6
6
  const meta = {
7
7
  title: 'Interaction/Dialog',
8
8
  component: Dialog,
9
- tags: ['autodocs'],
10
9
  } satisfies Meta<typeof Dialog>;
11
10
 
12
11
  export default meta;
@@ -0,0 +1,35 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Drawer.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Drawer
7
+
8
+ Slide-in panel from the left or right edge of the viewport. Use for navigation, filters, or secondary task flows that should not interrupt the main content.
9
+
10
+ ## When to use
11
+ - Mobile navigation panels
12
+ - Filter sidebars in data-heavy views
13
+ - Multi-step flows that are secondary to the main page
14
+
15
+ ## When not to use
16
+ - Simple confirmations or short prompts — use `Dialog`
17
+ - Content that always needs to be visible — use a sidebar layout instead
18
+
19
+ ## Examples
20
+
21
+ ### Left
22
+ <Canvas of={Stories.Left} />
23
+
24
+ ### Right
25
+ <Canvas of={Stories.Right} />
26
+
27
+ ## Props
28
+
29
+ <ArgTypes of={Stories} />
30
+
31
+ ## Accessibility
32
+
33
+ - Uses `<dialog>` internally — provides native focus trapping and `Escape` to close
34
+ - `aria-label` on the dialog describes the drawer's purpose
35
+ - Body scroll locked while open
@@ -14,7 +14,8 @@
14
14
  }
15
15
 
16
16
  .drawer::backdrop {
17
- background: rgb(0 0 0 / 50%);
17
+ background-color: var(--drawer_backdrop-color, rgb(0 0 0 / 50%));
18
+ backdrop-filter: blur(3px);
18
19
  }
19
20
 
20
21
  .panel {
@@ -28,7 +29,7 @@
28
29
  color: var(--drawer_on-color, var(--color_on-bg));
29
30
  box-shadow: var(--shadow_xl);
30
31
  overflow: hidden;
31
- transition: transform var(--animation_transition-duration) var(--animation_easing);
32
+ transition: var(--animation_transition);
32
33
  }
33
34
 
34
35
  .--side_right .panel {
@@ -48,13 +49,6 @@
48
49
  flex-shrink: 0;
49
50
  }
50
51
 
51
- .title {
52
- margin: 0;
53
- font-size: var(--font_size--heading--s);
54
- font-weight: var(--font_weight--semibold);
55
- line-height: var(--font_line-height--heading);
56
- }
57
-
58
52
  .closeButton {
59
53
  all: unset;
60
54
  display: flex;
@@ -1,11 +1,11 @@
1
1
  import { useState } from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { Drawer } from './Drawer';
4
+ import { Button } from '../Button/Button';
4
5
 
5
6
  const meta = {
6
7
  title: 'Interaction/Drawer',
7
8
  component: Drawer,
8
- tags: ['autodocs'],
9
9
  } satisfies Meta<typeof Drawer>;
10
10
 
11
11
  export default meta;
@@ -16,7 +16,7 @@ export const Right: Story = {
16
16
  const [open, setOpen] = useState(false);
17
17
  return (
18
18
  <>
19
- <button type="button" onClick={() => setOpen(true)}>Open right drawer</button>
19
+ <Button onClick={() => setOpen(true)}>Open right drawer</Button>
20
20
  <Drawer isOpen={open} onClose={() => setOpen(false)} title="Settings" side="right">
21
21
  <p>Drawer content goes here. You can put any React content inside.</p>
22
22
  </Drawer>
@@ -30,7 +30,7 @@ export const Left: Story = {
30
30
  const [open, setOpen] = useState(false);
31
31
  return (
32
32
  <>
33
- <button type="button" onClick={() => setOpen(true)}>Open left drawer</button>
33
+ <Button onClick={() => setOpen(true)}>Open left drawer</Button>
34
34
  <Drawer isOpen={open} onClose={() => setOpen(false)} title="Navigation" side="left">
35
35
  <nav>
36
36
  <ul>
@@ -5,10 +5,11 @@ import { cn } from '@boostdev/design-system-foundation';
5
5
  interface DrawerProps {
6
6
  isOpen: boolean;
7
7
  onClose: () => void;
8
- title: string;
8
+ title: ReactNode;
9
9
  children: ReactNode;
10
10
  side?: 'left' | 'right';
11
11
  className?: string;
12
+ arialLabel?: string;
12
13
  }
13
14
 
14
15
  export function Drawer({
@@ -17,6 +18,7 @@ export function Drawer({
17
18
  title,
18
19
  children,
19
20
  side = 'right',
21
+ arialLabel = '',
20
22
  className,
21
23
  }: Readonly<DrawerProps>) {
22
24
  const dialogRef = useRef<HTMLDialogElement>(null);
@@ -46,13 +48,13 @@ export function Drawer({
46
48
  <dialog
47
49
  ref={dialogRef}
48
50
  className={cn(css.drawer, css[`--side_${side}`], className)}
49
- aria-label={title}
51
+ aria-label={arialLabel}
50
52
  onClick={handleClick}
51
53
  onCancel={handleCancel}
52
54
  >
53
55
  <div className={css.panel}>
54
56
  <div className={css.header}>
55
- <h2 className={css.title}>{title}</h2>
57
+ {!!title && title}
56
58
  <button
57
59
  type="button"
58
60
  className={css.closeButton}
@@ -0,0 +1,40 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './DropdownMenu.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # DropdownMenu
7
+
8
+ Floating menu with a list of selectable actions, triggered by a button. Supports disabled items and separators.
9
+
10
+ ## When to use
11
+ - Overflow actions in a toolbar or table row
12
+ - Context menus on cards or list items
13
+
14
+ ## When not to use
15
+ - Navigation — use a `<nav>` with links
16
+ - More than ~8 items — consider a `Command` palette or a `Select`
17
+
18
+ ## Examples
19
+
20
+ ### Default
21
+ <Canvas of={Stories.Default} />
22
+
23
+ ### With separator
24
+ <Canvas of={Stories.WithSeparator} />
25
+
26
+ ### With disabled item
27
+ <Canvas of={Stories.WithDisabledItem} />
28
+
29
+ ### Aligned end
30
+ <Canvas of={Stories.AlignedEnd} />
31
+
32
+ ## Props
33
+
34
+ <ArgTypes of={Stories} />
35
+
36
+ ## Accessibility
37
+
38
+ - Implements ARIA menu pattern: `role="menu"`, `role="menuitem"`
39
+ - Arrow keys navigate items; `Enter`/`Space` activate; `Escape` closes
40
+ - Disabled items have `aria-disabled="true"` and are skipped during keyboard navigation
@@ -1,10 +1,10 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { DropdownMenu } from './DropdownMenu';
3
+ import {Button} from "../Button";
3
4
 
4
5
  const meta = {
5
6
  title: 'Interaction/DropdownMenu',
6
7
  component: DropdownMenu,
7
- tags: ['autodocs'],
8
8
  decorators: [
9
9
  Story => (
10
10
  <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'center', padding: '2rem' }}>
@@ -26,14 +26,14 @@ export const Default: Story = {
26
26
  ],
27
27
  },
28
28
  render: args => (
29
- <DropdownMenu {...args} trigger={<button type="button">Actions ▾</button>} />
29
+ <DropdownMenu {...args} trigger={<Button variant="secondary">Actions ▾</Button>} />
30
30
  ),
31
31
  };
32
32
 
33
33
  export const WithDisabledItem: Story = {
34
34
  render: () => (
35
35
  <DropdownMenu
36
- trigger={<button type="button">Options ▾</button>}
36
+ trigger={<Button variant="secondary">Options ▾</Button>}
37
37
  items={[
38
38
  { id: 'view', label: 'View' },
39
39
  { id: 'edit', label: 'Edit' },
@@ -46,7 +46,7 @@ export const WithDisabledItem: Story = {
46
46
  export const WithSeparator: Story = {
47
47
  render: () => (
48
48
  <DropdownMenu
49
- trigger={<button type="button">File ▾</button>}
49
+ trigger={<Button variant="secondary">File ▾</Button>}
50
50
  items={[
51
51
  { id: 'new', label: 'New' },
52
52
  { id: 'open', label: 'Open' },
@@ -60,7 +60,7 @@ export const WithSeparator: Story = {
60
60
  export const AlignedEnd: Story = {
61
61
  render: () => (
62
62
  <DropdownMenu
63
- trigger={<button type="button">⋮</button>}
63
+ trigger={<Button variant="secondary">⋮</Button>}
64
64
  items={[{ id: 'copy', label: 'Copy link' }, { id: 'report', label: 'Report' }]}
65
65
  placement="bottom-end"
66
66
  />
@@ -0,0 +1,34 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Popover.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Popover
7
+
8
+ Floating panel anchored to a trigger element. Unlike `Tooltip`, popovers can contain interactive content (forms, links, buttons).
9
+
10
+ ## When to use
11
+ - Filter panels, date pickers, or mini-forms attached to a trigger
12
+ - Rich contextual details that require user interaction
13
+
14
+ ## When not to use
15
+ - Non-interactive supplementary text — use `Tooltip` instead
16
+ - Full-screen overlays — use `Dialog`
17
+
18
+ ## Examples
19
+
20
+ ### Default
21
+ <Canvas of={Stories.Default} />
22
+
23
+ ### With form
24
+ <Canvas of={Stories.WithForm} />
25
+
26
+ ## Props
27
+
28
+ <ArgTypes of={Stories} />
29
+
30
+ ## Accessibility
31
+
32
+ - Trigger element gets `aria-haspopup="true"` and `aria-expanded` toggled on open/close
33
+ - Popover panel is linked via `aria-controls`
34
+ - Focus moves into the panel on open; `Escape` closes and returns focus to trigger
@@ -1,10 +1,10 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { Popover } from './Popover';
3
+ import { Button } from '../Button/Button';
3
4
 
4
5
  const meta = {
5
6
  title: 'Interaction/Popover',
6
7
  component: Popover,
7
- tags: ['autodocs'],
8
8
  argTypes: {
9
9
  placement: { control: 'radio', options: ['top', 'bottom', 'left', 'right'] },
10
10
  },
@@ -24,7 +24,7 @@ export const Default: Story = {
24
24
  args: { placement: 'bottom', content: <p style={{ margin: 0 }}>This is a popover with rich content.</p> },
25
25
  render: args => (
26
26
  <Popover {...args}>
27
- <button type="button">Open popover</button>
27
+ <Button>Open popover</Button>
28
28
  </Popover>
29
29
  ),
30
30
  };
@@ -41,7 +41,7 @@ export const WithForm: Story = {
41
41
  </div>
42
42
  }
43
43
  >
44
- <button type="button">Filters</button>
44
+ <Button variant="secondary">Filters</Button>
45
45
  </Popover>
46
46
  ),
47
47
  };
@@ -0,0 +1,38 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Rating.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Rating
7
+
8
+ Read-only star rating display. Use to show an existing score; for user input, combine with a form input or interactive stars.
9
+
10
+ ## When to use
11
+ - Displaying product or review scores
12
+ - Showing ratings in cards or lists
13
+
14
+ ## Examples
15
+
16
+ ### Three stars (default)
17
+ <Canvas of={Stories.ThreeStars} />
18
+
19
+ ### Full stars
20
+ <Canvas of={Stories.FullStars} />
21
+
22
+ ### Empty
23
+ <Canvas of={Stories.Empty} />
24
+
25
+ ### Custom max (out of 10)
26
+ <Canvas of={Stories.CustomMax} />
27
+
28
+ ### All ratings
29
+ <Canvas of={Stories.AllRatings} />
30
+
31
+ ## Props
32
+
33
+ <ArgTypes of={Stories} />
34
+
35
+ ## Accessibility
36
+
37
+ - Renders as a `<span>` with `role="img"` and `aria-label="X out of Y stars"`
38
+ - Stars are `aria-hidden` — the label carries the full meaning
@@ -4,7 +4,6 @@ import { Rating } from './Rating';
4
4
  const meta = {
5
5
  title: 'Interaction/Rating',
6
6
  component: Rating,
7
- tags: ['autodocs'],
8
7
  argTypes: {
9
8
  value: { control: { type: 'range', min: 0, max: 5, step: 1 } },
10
9
  max: { control: { type: 'number' } },