@boostdev/design-system-components 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/AGENTS.md +13 -10
  2. package/dist/client.cjs +77 -99
  3. package/dist/client.css +611 -580
  4. package/dist/client.d.cts +13 -16
  5. package/dist/client.d.ts +13 -16
  6. package/dist/client.js +87 -109
  7. package/dist/index.cjs +77 -99
  8. package/dist/index.css +611 -580
  9. package/dist/index.d.cts +13 -16
  10. package/dist/index.d.ts +13 -16
  11. package/dist/index.js +87 -109
  12. package/dist/native/index.cjs +9 -8
  13. package/dist/native/index.d.cts +1 -1
  14. package/dist/native/index.d.ts +1 -1
  15. package/dist/native/index.js +9 -8
  16. package/dist/web-components/{chunk-O4UFI2RX.js → chunk-3REOIRDW.js} +13 -11
  17. package/dist/web-components/{chunk-K5CE6HSB.js → chunk-6MH5UWUD.js} +74 -17
  18. package/dist/web-components/{chunk-VM3YHGQ2.js → chunk-AJSXNDAP.js} +1 -2
  19. package/dist/web-components/{chunk-LSC4N56Q.js → chunk-BX3IPVOB.js} +1 -2
  20. package/dist/web-components/{chunk-CJ5ZOO42.js → chunk-DI46Q2EA.js} +50 -1
  21. package/dist/web-components/{chunk-CAPI25CU.js → chunk-DZRSJGPB.js} +3 -3
  22. package/dist/web-components/{chunk-VSS5ITNE.js → chunk-EBJM3VD4.js} +1 -3
  23. package/dist/web-components/{chunk-O7I63SQX.js → chunk-HHHRF2PS.js} +6 -12
  24. package/dist/web-components/{chunk-OTX4COSV.js → chunk-JUKB3BUP.js} +3 -4
  25. package/dist/web-components/{chunk-IQGLIQSO.js → chunk-N6IMUOY4.js} +2 -5
  26. package/dist/web-components/{chunk-QDRH46TF.js → chunk-QLZ2362S.js} +1 -3
  27. package/dist/web-components/{chunk-IK3PTFZ4.js → chunk-T6AETZRP.js} +1 -3
  28. package/dist/web-components/{chunk-4BX6AWXX.js → chunk-X3FKVHLK.js} +2 -4
  29. package/dist/web-components/{chunk-A67LCT7C.js → chunk-XNA6WTXG.js} +3 -4
  30. package/dist/web-components/{chunk-XIENXR7H.js → chunk-YRXCVKHV.js} +1 -3
  31. package/dist/web-components/{chunk-6T3RKUKG.js → chunk-ZTC6GRP7.js} +1 -2
  32. package/dist/web-components/globals.js +1 -1
  33. package/dist/web-components/index.js +16 -16
  34. package/dist/web-components/interaction/bds-accordion.js +1 -1
  35. package/dist/web-components/interaction/bds-button.d.ts +2 -2
  36. package/dist/web-components/interaction/bds-button.js +1 -1
  37. package/dist/web-components/interaction/bds-dialog.js +1 -1
  38. package/dist/web-components/interaction/bds-drawer.js +1 -1
  39. package/dist/web-components/interaction/form/bds-checkbox.js +1 -1
  40. package/dist/web-components/interaction/form/bds-combobox.js +1 -1
  41. package/dist/web-components/interaction/form/bds-number-input.js +1 -1
  42. package/dist/web-components/interaction/form/bds-radio.js +1 -1
  43. package/dist/web-components/interaction/form/bds-segmented-control.d.ts +20 -3
  44. package/dist/web-components/interaction/form/bds-segmented-control.js +1 -1
  45. package/dist/web-components/interaction/form/bds-select.js +1 -1
  46. package/dist/web-components/interaction/form/bds-switch.js +1 -1
  47. package/dist/web-components/interaction/form/bds-textarea.js +1 -1
  48. package/dist/web-components/ui/bds-alert.js +1 -1
  49. package/dist/web-components/ui/bds-card.js +1 -1
  50. package/dist/web-components/ui/bds-notification-banner.js +1 -1
  51. package/package.json +2 -2
  52. package/src/components/interaction/Button/Button.module.css +12 -11
  53. package/src/components/interaction/Button/Button.native.tsx +10 -9
  54. package/src/components/interaction/Button/Button.tsx +1 -1
  55. package/src/components/interaction/Command/Command.module.css +3 -7
  56. package/src/components/interaction/Dialog/Dialog.module.css +7 -5
  57. package/src/components/interaction/Drawer/Drawer.module.css +49 -1
  58. package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +1 -3
  59. package/src/components/interaction/Popover/Popover.module.css +1 -3
  60. package/src/components/interaction/form/Checkbox/Checkbox.module.css +1 -3
  61. package/src/components/interaction/form/Combobox/Combobox.module.css +2 -6
  62. package/src/components/interaction/form/FormInput/FormInput.module.css +1 -3
  63. package/src/components/interaction/form/NumberInput/NumberInput.module.css +16 -4
  64. package/src/components/interaction/form/Radio/Radio.module.css +6 -16
  65. package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +70 -25
  66. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +55 -33
  67. package/src/components/interaction/form/SegmentedControl/SegmentedControl.spec.tsx +127 -56
  68. package/src/components/interaction/form/SegmentedControl/SegmentedControl.stories.tsx +89 -72
  69. package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +40 -62
  70. package/src/components/interaction/form/SegmentedControl/index.ts +1 -1
  71. package/src/components/interaction/form/Select/Select.module.css +2 -4
  72. package/src/components/interaction/form/Switch/Switch.module.css +5 -7
  73. package/src/components/interaction/form/Switch/Switch.native.spec.tsx +9 -9
  74. package/src/components/interaction/form/Switch/Switch.native.tsx +2 -2
  75. package/src/components/interaction/form/Textarea/Textarea.module.css +1 -3
  76. package/src/components/interaction/form/atoms/InputContainer.stories.tsx +64 -0
  77. package/src/components/interaction/form/atoms/Label.stories.tsx +33 -0
  78. package/src/components/interaction/form/atoms/Message.stories.tsx +33 -0
  79. package/src/components/layout/Card/Card.module.css +1 -3
  80. package/src/components/ui/Accordion/Accordion.module.css +1 -2
  81. package/src/components/ui/Alert/Alert.module.css +1 -2
  82. package/src/components/ui/Calendar/Calendar.module.css +2 -5
  83. package/src/components/ui/Carousel/Carousel.module.css +1 -3
  84. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +1 -2
  85. package/src/components/ui/Pagination/Pagination.module.css +1 -2
  86. package/src/components/ui/SkipLink/SkipLink.module.css +1 -2
  87. package/src/components/ui/Table/Table.module.css +1 -2
  88. package/src/css/bdc.css +16 -11
  89. package/src/index.ts +1 -1
  90. package/src/web-components/globals.ts +3 -3
  91. package/src/web-components/interaction/BdsAccordion.mdx +51 -0
  92. package/src/web-components/interaction/BdsAccordion.stories.tsx +85 -0
  93. package/src/web-components/interaction/BdsCollapsible.mdx +46 -0
  94. package/src/web-components/interaction/BdsCollapsible.stories.tsx +87 -0
  95. package/src/web-components/interaction/BdsDialog.mdx +49 -0
  96. package/src/web-components/interaction/BdsDialog.stories.tsx +75 -0
  97. package/src/web-components/interaction/BdsDrawer.mdx +50 -0
  98. package/src/web-components/interaction/BdsDrawer.stories.tsx +66 -0
  99. package/src/web-components/interaction/BdsSkipLink.mdx +34 -0
  100. package/src/web-components/interaction/BdsSkipLink.stories.tsx +55 -0
  101. package/src/web-components/interaction/BdsTabs.mdx +47 -0
  102. package/src/web-components/interaction/BdsTabs.stories.tsx +80 -0
  103. package/src/web-components/interaction/BdsTooltip.mdx +38 -0
  104. package/src/web-components/interaction/BdsTooltip.stories.tsx +68 -0
  105. package/src/web-components/interaction/bds-accordion.ts +1 -2
  106. package/src/web-components/interaction/bds-button.ts +15 -13
  107. package/src/web-components/interaction/bds-dialog.ts +2 -5
  108. package/src/web-components/interaction/bds-drawer.ts +50 -1
  109. package/src/web-components/interaction/form/BdsCheckbox.mdx +39 -0
  110. package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +73 -0
  111. package/src/web-components/interaction/form/BdsCombobox.mdx +52 -0
  112. package/src/web-components/interaction/form/BdsCombobox.stories.tsx +91 -0
  113. package/src/web-components/interaction/form/BdsFileInput.mdx +44 -0
  114. package/src/web-components/interaction/form/BdsFileInput.stories.tsx +84 -0
  115. package/src/web-components/interaction/form/BdsNumberInput.mdx +41 -0
  116. package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +90 -0
  117. package/src/web-components/interaction/form/BdsRadio.mdx +41 -0
  118. package/src/web-components/interaction/form/BdsRadio.stories.tsx +90 -0
  119. package/src/web-components/interaction/form/BdsSegmentedControl.mdx +42 -0
  120. package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +92 -0
  121. package/src/web-components/interaction/form/BdsSelect.mdx +38 -0
  122. package/src/web-components/interaction/form/BdsSelect.stories.tsx +93 -0
  123. package/src/web-components/interaction/form/BdsSlider.mdx +49 -0
  124. package/src/web-components/interaction/form/BdsSlider.stories.tsx +89 -0
  125. package/src/web-components/interaction/form/BdsSwitch.mdx +39 -0
  126. package/src/web-components/interaction/form/BdsSwitch.stories.tsx +73 -0
  127. package/src/web-components/interaction/form/BdsTextarea.mdx +41 -0
  128. package/src/web-components/interaction/form/BdsTextarea.stories.tsx +79 -0
  129. package/src/web-components/interaction/form/bds-checkbox.ts +1 -3
  130. package/src/web-components/interaction/form/bds-combobox.ts +1 -3
  131. package/src/web-components/interaction/form/bds-number-input.ts +3 -4
  132. package/src/web-components/interaction/form/bds-radio.ts +6 -12
  133. package/src/web-components/interaction/form/bds-segmented-control.ts +91 -20
  134. package/src/web-components/interaction/form/bds-select.ts +2 -4
  135. package/src/web-components/interaction/form/bds-switch.ts +3 -4
  136. package/src/web-components/interaction/form/bds-textarea.ts +1 -3
  137. package/src/web-components/ui/BdsAvatar.mdx +67 -0
  138. package/src/web-components/ui/BdsAvatar.stories.tsx +62 -0
  139. package/src/web-components/ui/BdsBreadcrumb.mdx +63 -0
  140. package/src/web-components/ui/BdsBreadcrumb.stories.tsx +59 -0
  141. package/src/web-components/ui/BdsCard.mdx +83 -0
  142. package/src/web-components/ui/BdsCard.stories.tsx +90 -0
  143. package/src/web-components/ui/BdsDescriptionList.mdx +67 -0
  144. package/src/web-components/ui/BdsDescriptionList.stories.tsx +79 -0
  145. package/src/web-components/ui/BdsIconWrapper.mdx +70 -0
  146. package/src/web-components/ui/BdsIconWrapper.stories.tsx +76 -0
  147. package/src/web-components/ui/BdsLink.mdx +75 -0
  148. package/src/web-components/ui/BdsLink.stories.tsx +64 -0
  149. package/src/web-components/ui/BdsLoading.mdx +62 -0
  150. package/src/web-components/ui/BdsLoading.stories.tsx +37 -0
  151. package/src/web-components/ui/BdsNotificationBanner.mdx +90 -0
  152. package/src/web-components/ui/BdsNotificationBanner.stories.tsx +93 -0
  153. package/src/web-components/ui/BdsProgress.mdx +63 -0
  154. package/src/web-components/ui/BdsProgress.stories.tsx +70 -0
  155. package/src/web-components/ui/BdsProgressCircle.mdx +64 -0
  156. package/src/web-components/ui/BdsProgressCircle.stories.tsx +69 -0
  157. package/src/web-components/ui/BdsRating.mdx +61 -0
  158. package/src/web-components/ui/BdsRating.stories.tsx +39 -0
  159. package/src/web-components/ui/BdsSectionHeader.mdx +69 -0
  160. package/src/web-components/ui/BdsSectionHeader.stories.tsx +56 -0
  161. package/src/web-components/ui/BdsSeparator.mdx +63 -0
  162. package/src/web-components/ui/BdsSeparator.stories.tsx +47 -0
  163. package/src/web-components/ui/BdsSkeleton.mdx +67 -0
  164. package/src/web-components/ui/BdsSkeleton.stories.tsx +50 -0
  165. package/src/web-components/ui/BdsTypography.mdx +83 -0
  166. package/src/web-components/ui/BdsTypography.stories.tsx +51 -0
  167. package/src/web-components/ui/bds-alert.ts +1 -2
  168. package/src/web-components/ui/bds-card.ts +1 -3
  169. package/src/web-components/ui/bds-notification-banner.ts +1 -2
@@ -39,12 +39,10 @@
39
39
  --bdc_color: var(--bds-color_on-bg);
40
40
 
41
41
  border: none;
42
- outline: var(--bdc-outline_width) solid var(--bdc_color);
43
- outline-offset: var(--bdc-outline_offset);
42
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--bds-shadow_s);
44
43
  cursor: pointer;
45
44
  flex-shrink: 0;
46
45
  transition: var(--bds-animation_transition);
47
- box-shadow: var(--bds-shadow_s);
48
46
  }
49
47
 
50
48
  .navBtn svg {
@@ -57,8 +57,7 @@
57
57
  transition: var(--bds-animation_transition);
58
58
  --bdc_color: currentcolor;
59
59
 
60
- outline: var(--bdc-outline_width) solid var(--bdc_color);
61
- outline-offset: var(--bdc-outline_offset);
60
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color);
62
61
  }
63
62
 
64
63
  .dismiss svg {
@@ -27,8 +27,7 @@
27
27
  --bdc_color: var(--btn_border, var(--bds-color_bg--subtle));
28
28
 
29
29
  border: none;
30
- outline: var(--bdc-outline_width) solid var(--bdc_color);
31
- outline-offset: var(--bdc-outline_offset);
30
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color);
32
31
  color: var(--btn_color, var(--bds-color_on-bg));
33
32
  font-family: var(--bds-font_family--body);
34
33
  font-size: var(--bds-font_size--body);
@@ -15,8 +15,7 @@
15
15
  --bdc_color: var(--color_focus, var(--bds-color_interactive));
16
16
 
17
17
  border: var(--skip-link_border, none);
18
- outline: var(--bdc-outline_width) solid var(--bdc_color);
19
- outline-offset: var(--bdc-outline_offset);
18
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color);
20
19
  transform: translateY(-200%);
21
20
  transition: transform var(--bds-animation_transition-duration) var(--bds-animation_easing);
22
21
  }
@@ -6,9 +6,8 @@
6
6
  --bdc_color: var(--bds-color_bg--subtle);
7
7
  --bdc-outline_radius: var(--bds-border_radius--s);
8
8
 
9
- outline: var(--bdc-outline_width) solid var(--bdc_color);
10
- outline-offset: var(--bdc-outline_offset);
11
9
  border-radius: var(--table_radius, var(--bdc-outline_radius));
10
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color);
12
11
  }
13
12
 
14
13
  .table {
package/src/css/bdc.css CHANGED
@@ -38,11 +38,12 @@
38
38
  initial-value: 0;
39
39
  }
40
40
 
41
- /* '*' syntax cannot be interpolated but gives a guaranteed none default */
41
+ /* '*' syntax cannot be interpolated; use a transparent shadow (not 'none') so it
42
+ can safely appear as the trailing item in a comma-separated box-shadow list. */
42
43
  @property --bdc-outline_shadow {
43
44
  syntax: '*';
44
45
  inherits: false;
45
- initial-value: none;
46
+ initial-value: 0 0 0 0 transparent;
46
47
  }
47
48
 
48
49
  @layer component {
@@ -56,19 +57,23 @@
56
57
  --bdc_color--error: var(--bds-color_error);
57
58
 
58
59
  /* Active / selected state colours — override these to theme all filled form controls globally */
59
- --bdc-color_active: var(--bds-color_green);
60
- --bdc-color_on-active: var(--bds-color_on-green);
61
- --bdc-color_active--subtle: var(--bds-color_green--subtle);
62
- --bdc-color_on-active--subtle: var(--bds-color_on-green--subtle);
63
- --bdc-color_active--strong: var(--bds-color_green--strong);
64
- --bdc-color_on-active--strong: var(--bds-color_on-green--strong);
60
+ --bdc-color_active: var(--bds-color_interactive);
61
+ --bdc-color_on-active: var(--bds-color_on-interactive);
62
+ --bdc-color_active--subtle: var(--bds-color_blue--subtle);
63
+ --bdc-color_on-active--subtle: var(--bds-color_on-blue--subtle);
64
+ --bdc-color_active--strong: var(--bds-color_interactive_on-bg);
65
+ --bdc-color_on-active--strong: var(--bds-color_on-blue--strong);
65
66
 
66
67
  /* Outline geometry */
67
68
  --bdc-outline_width: 1px;
68
- --bdc-outline_offset: -1px; /* inset appears inside the element like a traditional border */
69
+ --bdc-outline_offset: -1px; /* available for consumer use; bdc borders now use inset box-shadow */
69
70
  --bdc-outline_radius: var(--bds-border_radius--xs);
70
71
 
71
- /* Shadow: opt-in, none by default */
72
- --bdc-outline_shadow: none;
72
+ /* Shadow: opt-in; transparent (not 'none') so it's valid in a comma-separated box-shadow list */
73
+ --bdc-outline_shadow: 0 0 0 0 transparent;
74
+
75
+ /* Focus-visible ring — solid interactive colour, replaces foundation's dotted grey default */
76
+ --bds-outline_default: 2px solid var(--bds-color_interactive);
77
+ --bds-outline_offset: 2px;
73
78
  }
74
79
  }
package/src/index.ts CHANGED
@@ -56,7 +56,7 @@ export { Radio } from './components/interaction/form/Radio';
56
56
  export { RadioGroup } from './components/interaction/form/RadioGroup';
57
57
  export type { RadioGroupProps } from './components/interaction/form/RadioGroup';
58
58
  export { SegmentedControl } from './components/interaction/form/SegmentedControl';
59
- export type { SegmentedControlProps, SegmentedControlOption } from './components/interaction/form/SegmentedControl';
59
+ export type { SegmentedControlProps } from './components/interaction/form/SegmentedControl';
60
60
  export { Select } from './components/interaction/form/Select';
61
61
  export type { SelectOption } from './components/interaction/form/Select';
62
62
  export { Slider } from './components/interaction/form/Slider';
@@ -33,7 +33,7 @@ const bdcCss = `
33
33
  @property --bdc-outline_shadow {
34
34
  syntax: '*';
35
35
  inherits: false;
36
- initial-value: none;
36
+ initial-value: 0 0 0 0 transparent;
37
37
  }
38
38
 
39
39
  @layer component {
@@ -45,9 +45,9 @@ const bdcCss = `
45
45
  --bdc_color--error: var(--bds-color_error);
46
46
 
47
47
  --bdc-outline_width: 1px;
48
- --bdc-outline_offset: -1px;
48
+ --bdc-outline_offset: -1px; /* available for consumer use; bdc borders now use inset box-shadow */
49
49
  --bdc-outline_radius: var(--bds-border_radius--xs);
50
- --bdc-outline_shadow: none;
50
+ --bdc-outline_shadow: 0 0 0 0 transparent;
51
51
  }
52
52
  }
53
53
  `;
@@ -0,0 +1,51 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsAccordion.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Accordion
7
+
8
+ `<bds-accordion>` is a container for one or more `<bds-accordion-item>` elements. Each item shows a header and reveals its body on click. By default only one item can be open at a time; set `allow-multiple` to allow several.
9
+
10
+ ## Usage
11
+
12
+ ```html
13
+ <bds-accordion>
14
+ <bds-accordion-item>
15
+ <span slot="header">Section 1</span>
16
+ <p>Content for section 1.</p>
17
+ </bds-accordion-item>
18
+ <bds-accordion-item>
19
+ <span slot="header">Section 2</span>
20
+ <p>Content for section 2.</p>
21
+ </bds-accordion-item>
22
+ </bds-accordion>
23
+ ```
24
+
25
+ ## Attributes — `<bds-accordion>`
26
+
27
+ | Attribute | Type | Default | Description |
28
+ |------------------|-----------|---------|-----------------------------------------|
29
+ | `allow-multiple` | `boolean` | `false` | Allow more than one item open at a time |
30
+
31
+ ## Attributes — `<bds-accordion-item>`
32
+
33
+ | Attribute | Type | Default | Description |
34
+ |------------|-----------|---------|-----------------------------------|
35
+ | `disabled` | `boolean` | `false` | Prevents this item from toggling |
36
+
37
+ ## Slots — `<bds-accordion-item>`
38
+
39
+ | Slot | Description |
40
+ |-----------|--------------------------|
41
+ | `header` | The clickable header text |
42
+ | (default) | The collapsible body content |
43
+
44
+ ## Stories
45
+
46
+ <Canvas of={Stories.Default} />
47
+ <Canvas of={Stories.AllVariants} />
48
+
49
+ ## Controls
50
+
51
+ <ArgTypes of={Stories} />
@@ -0,0 +1,85 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import '../index';
4
+
5
+ function BdsAccordion({ allowMultiple }: { allowMultiple?: boolean }) {
6
+ return React.createElement(
7
+ 'bds-accordion',
8
+ { 'allow-multiple': allowMultiple || undefined },
9
+ React.createElement(
10
+ 'bds-accordion-item',
11
+ null,
12
+ React.createElement('span', { slot: 'header' }, 'What is a web component?'),
13
+ React.createElement('p', { style: { margin: 0 } }, 'A web component is a reusable custom element built with native web platform APIs — no framework required.'),
14
+ ),
15
+ React.createElement(
16
+ 'bds-accordion-item',
17
+ null,
18
+ React.createElement('span', { slot: 'header' }, 'How do I use it?'),
19
+ React.createElement('p', { style: { margin: 0 } }, 'Import the package and add the custom element tag to your HTML. Works in any framework.'),
20
+ ),
21
+ React.createElement(
22
+ 'bds-accordion-item',
23
+ null,
24
+ React.createElement('span', { slot: 'header' }, 'Is it accessible?'),
25
+ React.createElement('p', { style: { margin: 0 } }, 'Yes. Each item uses proper ARIA attributes: aria-expanded, aria-controls, and aria-labelledby.'),
26
+ ),
27
+ );
28
+ }
29
+
30
+ const meta = {
31
+ title: 'Web Components/Interaction/Accordion',
32
+ component: BdsAccordion,
33
+ tags: ['!stable', 'experimental'],
34
+ parameters: { layout: 'padded' },
35
+ argTypes: {
36
+ allowMultiple: { control: 'boolean' },
37
+ },
38
+ } satisfies Meta<typeof BdsAccordion>;
39
+
40
+ export default meta;
41
+ type Story = StoryObj<typeof meta>;
42
+
43
+ export const Default: Story = { args: { allowMultiple: false } };
44
+ export const AllowMultiple: Story = { args: { allowMultiple: true } };
45
+
46
+ export const WithDisabledItem: Story = {
47
+ render: () =>
48
+ React.createElement(
49
+ 'bds-accordion',
50
+ null,
51
+ React.createElement(
52
+ 'bds-accordion-item',
53
+ null,
54
+ React.createElement('span', { slot: 'header' }, 'Available item'),
55
+ React.createElement('p', { style: { margin: 0 } }, 'This item can be opened and closed.'),
56
+ ),
57
+ React.createElement(
58
+ 'bds-accordion-item',
59
+ { disabled: true },
60
+ React.createElement('span', { slot: 'header' }, 'Disabled item'),
61
+ React.createElement('p', { style: { margin: 0 } }, 'This item cannot be toggled.'),
62
+ ),
63
+ React.createElement(
64
+ 'bds-accordion-item',
65
+ null,
66
+ React.createElement('span', { slot: 'header' }, 'Another available item'),
67
+ React.createElement('p', { style: { margin: 0 } }, 'This item can also be opened.'),
68
+ ),
69
+ ),
70
+ };
71
+
72
+ export const AllVariants: Story = {
73
+ render: () => (
74
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '24px' }}>
75
+ <div>
76
+ <p style={{ margin: '0 0 8px', fontSize: '0.75rem', color: '#888', fontWeight: 600 }}>Single open (default)</p>
77
+ <BdsAccordion />
78
+ </div>
79
+ <div>
80
+ <p style={{ margin: '0 0 8px', fontSize: '0.75rem', color: '#888', fontWeight: 600 }}>Multiple open</p>
81
+ <BdsAccordion allowMultiple />
82
+ </div>
83
+ </div>
84
+ ),
85
+ };
@@ -0,0 +1,46 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsCollapsible.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Collapsible
7
+
8
+ `<bds-collapsible>` reveals or hides content on demand. Built on native `<details>/<summary>` semantics for built-in accessibility. Set the same `name` on multiple collapsibles to link them — opening one will close the others.
9
+
10
+ ## Usage
11
+
12
+ ```html
13
+ <bds-collapsible>
14
+ <span slot="summary">Show more</span>
15
+ <p>Hidden content revealed on click.</p>
16
+ </bds-collapsible>
17
+ ```
18
+
19
+ ## Attributes
20
+
21
+ | Attribute | Type | Default | Description |
22
+ |-----------|-----------|-------------|----------------------------------------------|
23
+ | `open` | `boolean` | `false` | Controls the open/closed state |
24
+ | `name` | `string` | `undefined` | Links collapsibles into a mutually-exclusive group |
25
+
26
+ ## Slots
27
+
28
+ | Slot | Description |
29
+ |-----------|----------------------------------|
30
+ | `summary` | The always-visible clickable label |
31
+ | (default) | The collapsible body content |
32
+
33
+ ## Events
34
+
35
+ | Event | Detail | Description |
36
+ |--------------|---------------------------------|--------------------------------|
37
+ | `bds-toggle` | `{ open: boolean }` | Fired when open state changes |
38
+
39
+ ## Stories
40
+
41
+ <Canvas of={Stories.Default} />
42
+ <Canvas of={Stories.Grouped} />
43
+
44
+ ## Controls
45
+
46
+ <ArgTypes of={Stories} />
@@ -0,0 +1,87 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import '../index';
4
+
5
+ function BdsCollapsible({
6
+ open,
7
+ name,
8
+ summary,
9
+ children,
10
+ onToggle,
11
+ }: {
12
+ open?: boolean;
13
+ name?: string;
14
+ summary?: string;
15
+ children?: React.ReactNode;
16
+ onToggle?: (e: Event) => void;
17
+ }) {
18
+ return React.createElement(
19
+ 'bds-collapsible',
20
+ { open: open || undefined, name, onbdstoggle: onToggle },
21
+ React.createElement('span', { slot: 'summary' }, summary ?? 'Toggle section'),
22
+ children ?? React.createElement('p', { style: { margin: 0 } }, 'Collapsible content goes here.'),
23
+ );
24
+ }
25
+
26
+ const meta = {
27
+ title: 'Web Components/Interaction/Collapsible',
28
+ component: BdsCollapsible,
29
+ tags: ['!stable', 'experimental'],
30
+ parameters: { layout: 'padded' },
31
+ argTypes: {
32
+ open: { control: 'boolean' },
33
+ },
34
+ } satisfies Meta<typeof BdsCollapsible>;
35
+
36
+ export default meta;
37
+ type Story = StoryObj<typeof meta>;
38
+
39
+ export const Default: Story = { args: { summary: 'Show more details' } };
40
+ export const OpenByDefault: Story = { args: { summary: 'Open by default', open: true } };
41
+
42
+ export const Grouped: Story = {
43
+ render: () => (
44
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '8px', maxWidth: '480px' }}>
45
+ <BdsCollapsible name="faq" summary="What is this component?">
46
+ <p style={{ margin: 0 }}>A Collapsible reveals or hides content on demand.</p>
47
+ </BdsCollapsible>
48
+ <BdsCollapsible name="faq" summary="How does grouping work?">
49
+ <p style={{ margin: 0 }}>Setting the same `name` on multiple collapsibles links them — opening one closes the others.</p>
50
+ </BdsCollapsible>
51
+ <BdsCollapsible name="faq" summary="Is it accessible?">
52
+ <p style={{ margin: 0 }}>Yes — built on native `&lt;details&gt;/&lt;summary&gt;` semantics.</p>
53
+ </BdsCollapsible>
54
+ </div>
55
+ ),
56
+ };
57
+
58
+ export const AllVariants: Story = {
59
+ render: () => (
60
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '12px', maxWidth: '480px' }}>
61
+ <BdsCollapsible summary="Closed by default">
62
+ <p style={{ margin: 0 }}>This starts closed.</p>
63
+ </BdsCollapsible>
64
+ <BdsCollapsible summary="Open by default" open>
65
+ <p style={{ margin: 0 }}>This starts open.</p>
66
+ </BdsCollapsible>
67
+ </div>
68
+ ),
69
+ };
70
+
71
+ export const Interactive: Story = {
72
+ render: () => {
73
+ const [isOpen, setIsOpen] = React.useState(false);
74
+ return (
75
+ <div style={{ maxWidth: '480px' }}>
76
+ <p style={{ margin: '0 0 8px', fontSize: '0.875rem' }}>State: {isOpen ? 'open' : 'closed'}</p>
77
+ <BdsCollapsible
78
+ summary="Controlled collapsible"
79
+ open={isOpen}
80
+ onToggle={(e: Event) => setIsOpen((e as CustomEvent<{ open: boolean }>).detail.open)}
81
+ >
82
+ <p style={{ margin: 0 }}>Controlled via React state. The bds-toggle event updates the parent.</p>
83
+ </BdsCollapsible>
84
+ </div>
85
+ );
86
+ },
87
+ };
@@ -0,0 +1,49 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsDialog.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Dialog
7
+
8
+ `<bds-dialog>` is a modal dialog built on the native `<dialog>` element. It traps focus while open, dismisses on Escape and backdrop click, and restores focus to the trigger element on close. Supports the native Invoker Commands API.
9
+
10
+ ## Usage
11
+
12
+ ```html
13
+ <bds-dialog id="my-dialog">
14
+ <h2>Dialog title</h2>
15
+ <p>Dialog body content.</p>
16
+ <button type="button">Close</button>
17
+ </bds-dialog>
18
+
19
+ <!-- Declarative open via Invoker API -->
20
+ <button type="button" commandfor="my-dialog" command="show-modal">Open</button>
21
+ ```
22
+
23
+ ## Attributes
24
+
25
+ | Attribute | Type | Default | Description |
26
+ |-----------|-----------|---------|-----------------------------|
27
+ | `open` | `boolean` | `false` | Controls the open state |
28
+
29
+ ## Events
30
+
31
+ | Event | Description |
32
+ |-------------|---------------------------------------|
33
+ | `bds-open` | Fired when the dialog opens |
34
+ | `bds-close` | Fired when the dialog closes |
35
+
36
+ ## Slots
37
+
38
+ | Slot | Description |
39
+ |-----------|-------------------------------------|
40
+ | (default) | Dialog content (title, body, actions) |
41
+
42
+ ## Stories
43
+
44
+ <Canvas of={Stories.Default} />
45
+ <Canvas of={Stories.InvokerAPI} />
46
+
47
+ ## Controls
48
+
49
+ <ArgTypes of={Stories} />
@@ -0,0 +1,75 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import '../index';
4
+
5
+ function DialogDemo({ title = 'Dialog title', withForm = false }: { title?: string; withForm?: boolean }) {
6
+ const [open, setOpen] = React.useState(false);
7
+ return (
8
+ <div>
9
+ <button type="button" onClick={() => setOpen(true)}>Open dialog</button>
10
+ {React.createElement(
11
+ 'bds-dialog',
12
+ { open: open || undefined, onbdsclose: () => setOpen(false) },
13
+ React.createElement('h2', { style: { margin: '0 0 12px' } }, title),
14
+ withForm
15
+ ? React.createElement(
16
+ 'form',
17
+ { style: { display: 'flex', flexDirection: 'column', gap: '12px' } },
18
+ React.createElement('label', null,
19
+ 'Name',
20
+ React.createElement('input', { type: 'text', placeholder: 'Your name', style: { display: 'block', width: '100%', marginTop: '4px' } }),
21
+ ),
22
+ React.createElement('label', null,
23
+ 'Email',
24
+ React.createElement('input', { type: 'email', placeholder: 'you@example.com', style: { display: 'block', width: '100%', marginTop: '4px' } }),
25
+ ),
26
+ )
27
+ : React.createElement('p', { style: { margin: '0 0 12px' } }, 'This is the dialog body. Press Escape or click the × button to close.'),
28
+ React.createElement(
29
+ 'div',
30
+ { style: { display: 'flex', gap: '8px', justifyContent: 'flex-end', marginTop: '12px' } },
31
+ React.createElement('button', { type: 'button', onClick: () => setOpen(false) }, 'Cancel'),
32
+ React.createElement('button', { type: 'button', onClick: () => setOpen(false) }, 'Confirm'),
33
+ ),
34
+ )}
35
+ </div>
36
+ );
37
+ }
38
+
39
+ function BdsDialogStory({ withForm }: { withForm?: boolean }) {
40
+ return React.createElement(DialogDemo, { withForm });
41
+ }
42
+
43
+ const meta = {
44
+ title: 'Web Components/Interaction/Dialog',
45
+ component: BdsDialogStory,
46
+ tags: ['!stable', 'experimental'],
47
+ parameters: { layout: 'centered' },
48
+ } satisfies Meta<typeof BdsDialogStory>;
49
+
50
+ export default meta;
51
+ type Story = StoryObj<typeof meta>;
52
+
53
+ export const Default: Story = { args: { withForm: false } };
54
+ export const WithForm: Story = { args: { withForm: true } };
55
+
56
+ export const InvokerAPI: Story = {
57
+ render: () => (
58
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '16px' }}>
59
+ <p style={{ margin: 0, fontSize: '0.875rem', color: '#666' }}>
60
+ Uses native Invoker Commands API — no JavaScript needed.
61
+ </p>
62
+ {React.createElement('button', { type: 'button', commandfor: 'invoker-dialog', command: 'show-modal' }, 'Open via commandfor')}
63
+ {React.createElement(
64
+ 'bds-dialog',
65
+ { id: 'invoker-dialog' },
66
+ React.createElement('h2', { style: { margin: '0 0 12px' } }, 'Opened via Invoker API'),
67
+ React.createElement('p', { style: { margin: 0 } }, 'This dialog was opened declaratively using commandfor and command attributes.'),
68
+ )}
69
+ </div>
70
+ ),
71
+ };
72
+
73
+ export const AllVariants: Story = {
74
+ render: () => React.createElement(DialogDemo, { title: 'Confirmation' }),
75
+ };
@@ -0,0 +1,50 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsDrawer.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Drawer
7
+
8
+ `<bds-drawer>` is a slide-in side panel built on the native `<dialog>` element. It uses the same modal infrastructure as `<bds-dialog>` but positions itself at the edge of the viewport. Supports the native Invoker Commands API.
9
+
10
+ ## Usage
11
+
12
+ ```html
13
+ <bds-drawer id="settings-drawer" side="right">
14
+ <span slot="title">Settings</span>
15
+ <p>Drawer body content.</p>
16
+ </bds-drawer>
17
+
18
+ <!-- Declarative open via Invoker API -->
19
+ <button type="button" commandfor="settings-drawer" command="show-modal">Open settings</button>
20
+ ```
21
+
22
+ ## Attributes
23
+
24
+ | Attribute | Type | Default | Description |
25
+ |-----------|-------------------------|-----------|-------------------------------|
26
+ | `open` | `boolean` | `false` | Controls the open state |
27
+ | `side` | `"left" \| "right"` | `"right"` | Which side the drawer slides in from |
28
+
29
+ ## Slots
30
+
31
+ | Slot | Description |
32
+ |-----------|----------------------------------|
33
+ | `title` | Drawer title text in the header |
34
+ | (default) | Drawer body content |
35
+
36
+ ## Events
37
+
38
+ | Event | Description |
39
+ |-------------|---------------------------------|
40
+ | `bds-open` | Fired when the drawer opens |
41
+ | `bds-close` | Fired when the drawer closes |
42
+
43
+ ## Stories
44
+
45
+ <Canvas of={Stories.Right} />
46
+ <Canvas of={Stories.InvokerAPI} />
47
+
48
+ ## Controls
49
+
50
+ <ArgTypes of={Stories} />
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import type { DrawerSide } from './bds-drawer';
4
+ import '../index';
5
+
6
+ function DrawerDemo({ side = 'right' }: { side?: DrawerSide }) {
7
+ const [open, setOpen] = React.useState(false);
8
+ return (
9
+ <div>
10
+ <button type="button" onClick={() => setOpen(true)}>Open drawer</button>
11
+ {React.createElement(
12
+ 'bds-drawer',
13
+ { open: open || undefined, side, onbdsclose: () => setOpen(false) },
14
+ React.createElement('span', { slot: 'title' }, side === 'left' ? 'Left Drawer' : 'Settings'),
15
+ React.createElement('p', { style: { margin: '0 0 12px' } }, 'Drawer content goes here. Press Escape or click × to close.'),
16
+ React.createElement('button', { type: 'button', onClick: () => setOpen(false) }, 'Close'),
17
+ )}
18
+ </div>
19
+ );
20
+ }
21
+
22
+ function BdsDrawer({ side }: { side?: DrawerSide }) {
23
+ return React.createElement(DrawerDemo, { side });
24
+ }
25
+
26
+ const meta = {
27
+ title: 'Web Components/Interaction/Drawer',
28
+ component: BdsDrawer,
29
+ tags: ['!stable', 'experimental'],
30
+ parameters: { layout: 'centered' },
31
+ argTypes: {
32
+ side: { control: 'select', options: ['left', 'right'] },
33
+ },
34
+ } satisfies Meta<typeof BdsDrawer>;
35
+
36
+ export default meta;
37
+ type Story = StoryObj<typeof meta>;
38
+
39
+ export const Right: Story = { args: { side: 'right' } };
40
+ export const Left: Story = { args: { side: 'left' } };
41
+
42
+ export const AllVariants: Story = {
43
+ render: () => (
44
+ <div style={{ display: 'flex', gap: '12px' }}>
45
+ <DrawerDemo side="right" />
46
+ <DrawerDemo side="left" />
47
+ </div>
48
+ ),
49
+ };
50
+
51
+ export const InvokerAPI: Story = {
52
+ render: () => (
53
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '16px' }}>
54
+ <p style={{ margin: 0, fontSize: '0.875rem', color: '#666' }}>
55
+ Uses native Invoker Commands API — no JavaScript needed.
56
+ </p>
57
+ {React.createElement('button', { type: 'button', commandfor: 'invoker-drawer', command: 'show-modal' }, 'Open via commandfor')}
58
+ {React.createElement(
59
+ 'bds-drawer',
60
+ { id: 'invoker-drawer', side: 'right' },
61
+ React.createElement('span', { slot: 'title' }, 'Opened via Invoker API'),
62
+ React.createElement('p', { style: { margin: 0 } }, 'This drawer was opened declaratively using commandfor and command attributes.'),
63
+ )}
64
+ </div>
65
+ ),
66
+ };