@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
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import type { CardVariant, CardPadding, CardTextAlign } from './bds-card';
4
+ import '../index';
5
+
6
+ function BdsCard({
7
+ variant,
8
+ padding,
9
+ textAlign,
10
+ clickable,
11
+ ariaLabel,
12
+ onClick,
13
+ children,
14
+ }: {
15
+ variant?: CardVariant;
16
+ padding?: CardPadding;
17
+ textAlign?: CardTextAlign;
18
+ clickable?: boolean;
19
+ ariaLabel?: string;
20
+ onClick?: () => void;
21
+ children?: React.ReactNode;
22
+ }) {
23
+ return React.createElement(
24
+ 'bds-card',
25
+ {
26
+ variant,
27
+ padding,
28
+ 'text-align': textAlign,
29
+ clickable: clickable || undefined,
30
+ 'aria-label': ariaLabel,
31
+ onbdsclick: onClick,
32
+ },
33
+ children,
34
+ );
35
+ }
36
+
37
+ const meta = {
38
+ title: 'Web Components/UI/Card',
39
+ component: BdsCard,
40
+ tags: ['!stable', 'experimental'],
41
+ parameters: { layout: 'centered' },
42
+ argTypes: {
43
+ variant: { control: 'select', options: ['default', 'elevated', 'outlined'] },
44
+ padding: { control: 'select', options: ['none', 'small', 'medium', 'large'] },
45
+ textAlign: { control: 'select', options: ['start', 'center', 'end'] },
46
+ },
47
+ } satisfies Meta<typeof BdsCard>;
48
+
49
+ export default meta;
50
+ type Story = StoryObj<typeof meta>;
51
+
52
+ const cardContent = React.createElement(
53
+ 'div',
54
+ null,
55
+ React.createElement('strong', null, 'Card title'),
56
+ React.createElement('p', { style: { margin: '8px 0 0' } }, 'Card body with some descriptive text.'),
57
+ );
58
+
59
+ export const Default: Story = { args: { variant: 'default', children: cardContent } };
60
+ export const Elevated: Story = { args: { variant: 'elevated', children: cardContent } };
61
+ export const Outlined: Story = { args: { variant: 'outlined', children: cardContent } };
62
+ export const Clickable: Story = { args: { variant: 'default', clickable: true, ariaLabel: 'Open details', children: cardContent } };
63
+
64
+ export const AllVariants: Story = {
65
+ render: () => (
66
+ <div style={{ display: 'flex', gap: '16px', flexWrap: 'wrap' }}>
67
+ {(['default', 'elevated', 'outlined'] as const).map(v => (
68
+ <BdsCard key={v} variant={v} style={{ width: '200px' } as React.CSSProperties}>
69
+ <strong>{v}</strong>
70
+ <p style={{ margin: '8px 0 0' }}>Card variant</p>
71
+ </BdsCard>
72
+ ))}
73
+ </div>
74
+ ),
75
+ };
76
+
77
+ export const Interactive: Story = {
78
+ render: () => {
79
+ const [count, setCount] = React.useState(0);
80
+ return (
81
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '16px' }}>
82
+ <BdsCard variant="elevated" clickable ariaLabel="Click me" onClick={() => setCount(c => c + 1)}>
83
+ <strong>Clickable card</strong>
84
+ <p style={{ margin: '8px 0 0' }}>Click to increment counter</p>
85
+ </BdsCard>
86
+ <p style={{ margin: 0 }}>Clicked {count} times</p>
87
+ </div>
88
+ );
89
+ },
90
+ };
@@ -0,0 +1,67 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsDescriptionList.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # &lt;bds-description-list&gt;
7
+
8
+ Framework-agnostic Description List custom element. Wraps a semantic `<dl>` element for rendering key–value pairs. Consumers provide `<dt>` and `<dd>` elements as slotted children.
9
+
10
+ > **Status: experimental** — API may change before stable release.
11
+
12
+ ## Installation
13
+
14
+ ```js
15
+ import '@boostdev/components/web-components';
16
+ ```
17
+
18
+ ## Examples
19
+
20
+ ### All layouts
21
+ <Canvas of={Stories.AllVariants} />
22
+
23
+ ### Stacked
24
+ <Canvas of={Stories.Stacked} />
25
+
26
+ ### Inline
27
+ <Canvas of={Stories.Inline} />
28
+
29
+ ## Props
30
+
31
+ <ArgTypes of={Stories} />
32
+
33
+ ## Attributes
34
+
35
+ | Attribute | Type | Default | Description |
36
+ |-----------|------|---------|-------------|
37
+ | `layout` | `"stacked" \| "inline"` | `"stacked"` | Arrangement of terms and details |
38
+
39
+ ## Slots
40
+
41
+ | Slot | Description |
42
+ |------|-------------|
43
+ | `(default)` | `<dt>` and `<dd>` element pairs |
44
+
45
+ ## Usage in plain HTML
46
+
47
+ ```html
48
+ <bds-description-list>
49
+ <dt>Name</dt>
50
+ <dd>Jane Doe</dd>
51
+ <dt>Email</dt>
52
+ <dd>jane@example.com</dd>
53
+ </bds-description-list>
54
+
55
+ <bds-description-list layout="inline">
56
+ <dt>Status</dt>
57
+ <dd>Active</dd>
58
+ <dt>Role</dt>
59
+ <dd>Designer</dd>
60
+ </bds-description-list>
61
+ ```
62
+
63
+ ## Accessibility
64
+
65
+ - Renders a semantic `<dl>` element — screen readers announce it as a description list
66
+ - Ensure `<dt>` and `<dd>` elements are properly paired
67
+ - The `::slotted(dt)` and `::slotted(dd)` CSS selectors apply the visual styling
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import '../index';
4
+
5
+ function BdsDescriptionList({
6
+ layout,
7
+ children,
8
+ }: {
9
+ layout?: 'stacked' | 'inline';
10
+ children?: React.ReactNode;
11
+ }) {
12
+ return React.createElement('bds-description-list', { layout }, children);
13
+ }
14
+
15
+ const meta = {
16
+ title: 'Web Components/UI/DescriptionList',
17
+ component: BdsDescriptionList,
18
+ tags: ['!stable', 'experimental'],
19
+ parameters: { layout: 'centered' },
20
+ argTypes: {
21
+ layout: { control: 'select', options: ['stacked', 'inline'] },
22
+ },
23
+ } satisfies Meta<typeof BdsDescriptionList>;
24
+
25
+ export default meta;
26
+ type Story = StoryObj<typeof meta>;
27
+
28
+ const items = [
29
+ { term: 'Name', detail: 'Jane Doe' },
30
+ { term: 'Email', detail: 'jane@example.com' },
31
+ { term: 'Role', detail: 'Designer' },
32
+ ];
33
+
34
+ export const Stacked: Story = {
35
+ render: () => (
36
+ <BdsDescriptionList layout="stacked">
37
+ {items.flatMap(({ term, detail }) => [
38
+ React.createElement('dt', { key: `dt-${term}` }, term),
39
+ React.createElement('dd', { key: `dd-${term}` }, detail),
40
+ ])}
41
+ </BdsDescriptionList>
42
+ ),
43
+ };
44
+
45
+ export const Inline: Story = {
46
+ render: () => (
47
+ <BdsDescriptionList layout="inline">
48
+ {items.flatMap(({ term, detail }) => [
49
+ React.createElement('dt', { key: `dt-${term}` }, term),
50
+ React.createElement('dd', { key: `dd-${term}` }, detail),
51
+ ])}
52
+ </BdsDescriptionList>
53
+ ),
54
+ };
55
+
56
+ export const AllVariants: Story = {
57
+ render: () => (
58
+ <div style={{ display: 'flex', gap: '48px', flexWrap: 'wrap', alignItems: 'flex-start' }}>
59
+ <div>
60
+ <p style={{ margin: '0 0 8px', fontSize: '0.75rem', color: '#888', fontWeight: 600 }}>Stacked</p>
61
+ <BdsDescriptionList layout="stacked">
62
+ {items.flatMap(({ term, detail }) => [
63
+ React.createElement('dt', { key: `dt-${term}` }, term),
64
+ React.createElement('dd', { key: `dd-${term}` }, detail),
65
+ ])}
66
+ </BdsDescriptionList>
67
+ </div>
68
+ <div>
69
+ <p style={{ margin: '0 0 8px', fontSize: '0.75rem', color: '#888', fontWeight: 600 }}>Inline</p>
70
+ <BdsDescriptionList layout="inline">
71
+ {items.flatMap(({ term, detail }) => [
72
+ React.createElement('dt', { key: `dt-${term}` }, term),
73
+ React.createElement('dd', { key: `dd-${term}` }, detail),
74
+ ])}
75
+ </BdsDescriptionList>
76
+ </div>
77
+ </div>
78
+ ),
79
+ };
@@ -0,0 +1,70 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsIconWrapper.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # &lt;bds-icon-wrapper&gt;
7
+
8
+ Framework-agnostic Icon Wrapper custom element. Displays an icon inside a circular container with background and foreground theming.
9
+
10
+ > **Status: experimental** — API may change before stable release.
11
+
12
+ ## Installation
13
+
14
+ ```js
15
+ import '@boostdev/components/web-components';
16
+ ```
17
+
18
+ ## Examples
19
+
20
+ ### All variants
21
+ <Canvas of={Stories.AllVariants} />
22
+
23
+ ### With star icon
24
+ <Canvas of={Stories.WithStar} />
25
+
26
+ ## Props
27
+
28
+ <ArgTypes of={Stories} />
29
+
30
+ ## Attributes
31
+
32
+ | Attribute | Type | Default | Description |
33
+ |-----------|------|---------|-------------|
34
+ | `aria-hidden` | string | — | Set to `"true"` for purely decorative icons |
35
+
36
+ ## Slots
37
+
38
+ | Slot | Description |
39
+ |------|-------------|
40
+ | `(default)` | Icon content (SVG or `<img>`) |
41
+
42
+ ## CSS Custom Properties
43
+
44
+ | Property | Description |
45
+ |----------|-------------|
46
+ | `--icon-wrapper-color` | Circle background colour |
47
+ | `--bds-on-icon-wrapper-color` | Icon fill/stroke colour |
48
+
49
+ ## Usage in plain HTML
50
+
51
+ ```html
52
+ <!-- Decorative icon -->
53
+ <bds-icon-wrapper aria-hidden="true">
54
+ <svg viewBox="0 0 24 24" fill="currentColor">
55
+ <path d="M12 17.27L18.18 21..."/>
56
+ </svg>
57
+ </bds-icon-wrapper>
58
+
59
+ <!-- Meaningful icon — add aria-label to the wrapper -->
60
+ <bds-icon-wrapper aria-label="Favourite">
61
+ <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
62
+ <path d="M12 21.593c..."/>
63
+ </svg>
64
+ </bds-icon-wrapper>
65
+ ```
66
+
67
+ ## Accessibility
68
+
69
+ - When the icon is decorative, set `aria-hidden="true"` on the host
70
+ - When the icon conveys meaning, set `aria-label` on the host and `aria-hidden="true"` on the SVG itself
@@ -0,0 +1,76 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import '../index';
4
+
5
+ const StarIcon = () =>
6
+ React.createElement(
7
+ 'svg',
8
+ { viewBox: '0 0 24 24', fill: 'currentColor', width: '1.25em', height: '1.25em', 'aria-hidden': 'true' },
9
+ React.createElement('path', {
10
+ d: 'M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z',
11
+ }),
12
+ );
13
+
14
+ const CheckIcon = () =>
15
+ React.createElement(
16
+ 'svg',
17
+ { viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2.5', width: '1.25em', height: '1.25em', 'aria-hidden': 'true' },
18
+ React.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', d: 'M5 13l4 4L19 7' }),
19
+ );
20
+
21
+ const HeartIcon = () =>
22
+ React.createElement(
23
+ 'svg',
24
+ { viewBox: '0 0 24 24', fill: 'currentColor', width: '1.25em', height: '1.25em', 'aria-hidden': 'true' },
25
+ React.createElement('path', {
26
+ d: 'M12 21.593c-.425-.052-8.092-5.65-8.092-10.82C3.908 6.64 7.084 4 10.408 4c1.75 0 3.192.806 4.175 2.016A5.355 5.355 0 0 1 18.175 4c3.324 0 6.5 2.64 6.5 6.773 0 5.17-7.667 10.768-8.092 10.82z',
27
+ }),
28
+ );
29
+
30
+ function BdsIconWrapper({ children }: { children?: React.ReactNode }) {
31
+ return React.createElement('bds-icon-wrapper', null, children);
32
+ }
33
+
34
+ const meta = {
35
+ title: 'Web Components/UI/IconWrapper',
36
+ component: BdsIconWrapper,
37
+ tags: ['!stable', 'experimental'],
38
+ parameters: { layout: 'centered' },
39
+ } satisfies Meta<typeof BdsIconWrapper>;
40
+
41
+ export default meta;
42
+ type Story = StoryObj<typeof meta>;
43
+
44
+ export const WithStar: Story = {
45
+ render: () => (
46
+ <BdsIconWrapper>
47
+ <StarIcon />
48
+ </BdsIconWrapper>
49
+ ),
50
+ };
51
+
52
+ export const WithCheck: Story = {
53
+ render: () => (
54
+ <BdsIconWrapper>
55
+ <CheckIcon />
56
+ </BdsIconWrapper>
57
+ ),
58
+ };
59
+
60
+ export const WithHeart: Story = {
61
+ render: () => (
62
+ <BdsIconWrapper>
63
+ <HeartIcon />
64
+ </BdsIconWrapper>
65
+ ),
66
+ };
67
+
68
+ export const AllVariants: Story = {
69
+ render: () => (
70
+ <div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}>
71
+ <BdsIconWrapper><StarIcon /></BdsIconWrapper>
72
+ <BdsIconWrapper><CheckIcon /></BdsIconWrapper>
73
+ <BdsIconWrapper><HeartIcon /></BdsIconWrapper>
74
+ </div>
75
+ ),
76
+ };
@@ -0,0 +1,75 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsLink.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # &lt;bds-link&gt;
7
+
8
+ Framework-agnostic Link custom element. Renders a semantic `<a>` element with three visual styles and built-in external link handling.
9
+
10
+ > **Status: experimental** — API may change before stable release.
11
+
12
+ ## Installation
13
+
14
+ ```js
15
+ import '@boostdev/components/web-components';
16
+ ```
17
+
18
+ ## Examples
19
+
20
+ ### All variants
21
+ <Canvas of={Stories.AllVariants} />
22
+
23
+ ### Default
24
+ <Canvas of={Stories.Default} />
25
+
26
+ ### Standalone
27
+ <Canvas of={Stories.Standalone} />
28
+
29
+ ### External
30
+ <Canvas of={Stories.External} />
31
+
32
+ ## Props
33
+
34
+ <ArgTypes of={Stories} />
35
+
36
+ ## Attributes
37
+
38
+ | Attribute | Type | Default | Description |
39
+ |-----------|------|---------|-------------|
40
+ | `href` | string | — | Link destination |
41
+ | `target` | string | — | Native anchor target |
42
+ | `variant` | `"default" \| "subtle" \| "standalone"` | `"default"` | Visual style |
43
+ | `external` | boolean | `false` | Adds `rel="noreferrer noopener"`, opens in new tab, appends screen-reader text |
44
+ | `external-label` | string | `"(opens in new tab)"` | Visually-hidden text appended for external links |
45
+
46
+ ## Slots
47
+
48
+ | Slot | Description |
49
+ |------|-------------|
50
+ | `(default)` | Link text content |
51
+
52
+ ## CSS Custom Properties
53
+
54
+ | Property | Description |
55
+ |----------|-------------|
56
+ | `--link_color` | Link text colour |
57
+ | `--link_color-hover` | Link colour on hover |
58
+
59
+ ## Usage in plain HTML
60
+
61
+ ```html
62
+ <bds-link href="/docs">Read the docs</bds-link>
63
+
64
+ <bds-link href="/about" variant="subtle">About us</bds-link>
65
+
66
+ <bds-link href="/get-started" variant="standalone">Get started →</bds-link>
67
+
68
+ <bds-link href="https://example.com" external>Visit site</bds-link>
69
+ ```
70
+
71
+ ## Accessibility
72
+
73
+ - Renders a native `<a>` — fully keyboard accessible
74
+ - `external` adds a visually-hidden `(opens in new tab)` label for screen reader users and sets `rel="noreferrer noopener"` for security
75
+ - Always provide meaningful link text — avoid "click here" or "read more"
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import type { LinkVariant } from './bds-link';
4
+ import '../index';
5
+
6
+ function BdsLink({
7
+ href,
8
+ variant,
9
+ external,
10
+ externalLabel,
11
+ children,
12
+ }: {
13
+ href?: string;
14
+ variant?: LinkVariant;
15
+ external?: boolean;
16
+ externalLabel?: string;
17
+ children?: React.ReactNode;
18
+ }) {
19
+ return React.createElement(
20
+ 'bds-link',
21
+ {
22
+ href: href ?? '#',
23
+ variant,
24
+ external: external || undefined,
25
+ 'external-label': externalLabel,
26
+ },
27
+ children,
28
+ );
29
+ }
30
+
31
+ const meta = {
32
+ title: 'Web Components/UI/Link',
33
+ component: BdsLink,
34
+ tags: ['!stable', 'experimental'],
35
+ parameters: { layout: 'centered' },
36
+ argTypes: {
37
+ variant: { control: 'select', options: ['default', 'subtle', 'standalone'] },
38
+ },
39
+ } satisfies Meta<typeof BdsLink>;
40
+
41
+ export default meta;
42
+ type Story = StoryObj<typeof meta>;
43
+
44
+ export const Default: Story = { args: { children: 'Default link', variant: 'default' } };
45
+ export const Subtle: Story = { args: { children: 'Subtle link', variant: 'subtle' } };
46
+ export const Standalone: Story = { args: { children: 'Standalone link →', variant: 'standalone' } };
47
+ export const External: Story = { args: { children: 'External link', variant: 'default', external: true } };
48
+
49
+ export const AllVariants: Story = {
50
+ render: () => (
51
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
52
+ {(['default', 'subtle', 'standalone'] as const).map(v => (
53
+ <div key={v} style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
54
+ <BdsLink variant={v}>{v.charAt(0).toUpperCase() + v.slice(1)} link</BdsLink>
55
+ <span style={{ fontSize: '0.75rem', color: '#888' }}>{v}</span>
56
+ </div>
57
+ ))}
58
+ <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
59
+ <BdsLink external>External link</BdsLink>
60
+ <span style={{ fontSize: '0.75rem', color: '#888' }}>external</span>
61
+ </div>
62
+ </div>
63
+ ),
64
+ };
@@ -0,0 +1,62 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsLoading.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # &lt;bds-loading&gt;
7
+
8
+ Framework-agnostic Loading spinner custom element. Use to indicate an ongoing asynchronous operation.
9
+
10
+ > **Status: experimental** — API may change before stable release.
11
+
12
+ ## Installation
13
+
14
+ ```js
15
+ import '@boostdev/components/web-components';
16
+ ```
17
+
18
+ ## Examples
19
+
20
+ ### All sizes
21
+ <Canvas of={Stories.AllVariants} />
22
+
23
+ ### Small
24
+ <Canvas of={Stories.Small} />
25
+
26
+ ### Medium
27
+ <Canvas of={Stories.Medium} />
28
+
29
+ ### Large
30
+ <Canvas of={Stories.Large} />
31
+
32
+ ## Props
33
+
34
+ <ArgTypes of={Stories} />
35
+
36
+ ## Attributes
37
+
38
+ | Attribute | Type | Default | Description |
39
+ |-----------|------|---------|-------------|
40
+ | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Spinner diameter |
41
+
42
+ ## CSS Custom Properties
43
+
44
+ The spinner uses design tokens directly:
45
+
46
+ | Token | Description |
47
+ |-------|-------------|
48
+ | `--bds-color_bg` | Track colour |
49
+ | `--bds-color_interactive` | Active arc colour |
50
+
51
+ ## Usage in plain HTML
52
+
53
+ ```html
54
+ <bds-loading></bds-loading>
55
+ <bds-loading size="small"></bds-loading>
56
+ <bds-loading size="large"></bds-loading>
57
+ ```
58
+
59
+ ## Accessibility
60
+
61
+ - Renders a `role="status"` element with `aria-label="Loading"` — screen readers announce the loading state
62
+ - Respects `prefers-reduced-motion`: the spin animation stops for users who prefer reduced motion
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import '../index';
4
+
5
+ function BdsLoading({ size }: { size?: 'small' | 'medium' | 'large' }) {
6
+ return React.createElement('bds-loading', { size });
7
+ }
8
+
9
+ const meta = {
10
+ title: 'Web Components/UI/Loading',
11
+ component: BdsLoading,
12
+ tags: ['!stable', 'experimental'],
13
+ parameters: { layout: 'centered' },
14
+ argTypes: {
15
+ size: { control: 'select', options: ['small', 'medium', 'large'] },
16
+ },
17
+ } satisfies Meta<typeof BdsLoading>;
18
+
19
+ export default meta;
20
+ type Story = StoryObj<typeof meta>;
21
+
22
+ export const Small: Story = { args: { size: 'small' } };
23
+ export const Medium: Story = { args: { size: 'medium' } };
24
+ export const Large: Story = { args: { size: 'large' } };
25
+
26
+ export const AllVariants: Story = {
27
+ render: () => (
28
+ <div style={{ display: 'flex', alignItems: 'center', gap: '32px' }}>
29
+ {(['small', 'medium', 'large'] as const).map(s => (
30
+ <div key={s} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px' }}>
31
+ <BdsLoading size={s} />
32
+ <span style={{ fontSize: '0.75rem', color: '#888' }}>{s}</span>
33
+ </div>
34
+ ))}
35
+ </div>
36
+ ),
37
+ };