@boostdev/design-system-components 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/AGENTS.md +25 -4
  2. package/dist/client.cjs +58 -54
  3. package/dist/client.css +503 -525
  4. package/dist/client.js +58 -54
  5. package/dist/index.cjs +58 -54
  6. package/dist/index.css +503 -525
  7. package/dist/index.js +58 -54
  8. package/dist/web-components/{chunk-6MH5UWUD.js → chunk-2FGATTGT.js} +5 -0
  9. package/dist/web-components/{chunk-5IPHEONG.js → chunk-6I2DBFQ7.js} +1 -0
  10. package/dist/web-components/{chunk-DI46Q2EA.js → chunk-OCODKRVZ.js} +57 -82
  11. package/dist/web-components/index.d.ts +571 -1
  12. package/dist/web-components/index.js +1501 -4
  13. package/dist/web-components/interaction/bds-collapsible.js +1 -1
  14. package/dist/web-components/interaction/bds-drawer.js +1 -1
  15. package/dist/web-components/interaction/form/bds-segmented-control.js +1 -1
  16. package/package.json +1 -1
  17. package/src/components/interaction/Drawer/Drawer.module.css +37 -62
  18. package/src/components/interaction/Drawer/Drawer.tsx +21 -17
  19. package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +8 -4
  20. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +13 -6
  21. package/src/stories/Introduction.mdx +2 -1
  22. package/src/web-components/index.ts +12 -0
  23. package/src/web-components/interaction/BdsAccordion.mdx +80 -28
  24. package/src/web-components/interaction/BdsAccordion.stories.tsx +67 -58
  25. package/src/web-components/interaction/BdsCollapsible.mdx +93 -23
  26. package/src/web-components/interaction/BdsCollapsible.stories.tsx +29 -48
  27. package/src/web-components/interaction/BdsDialog.mdx +88 -27
  28. package/src/web-components/interaction/BdsDialog.stories.tsx +129 -47
  29. package/src/web-components/interaction/BdsDrawer.mdx +85 -27
  30. package/src/web-components/interaction/BdsDrawer.stories.tsx +161 -31
  31. package/src/web-components/interaction/BdsDropdownMenu.mdx +108 -0
  32. package/src/web-components/interaction/BdsDropdownMenu.stories.tsx +91 -0
  33. package/src/web-components/interaction/BdsSkipLink.mdx +72 -16
  34. package/src/web-components/interaction/BdsSkipLink.stories.tsx +47 -34
  35. package/src/web-components/interaction/BdsTabs.mdx +97 -27
  36. package/src/web-components/interaction/BdsTabs.stories.tsx +69 -55
  37. package/src/web-components/interaction/BdsTooltip.mdx +84 -18
  38. package/src/web-components/interaction/BdsTooltip.stories.tsx +49 -30
  39. package/src/web-components/interaction/bds-collapsible.ts +1 -0
  40. package/src/web-components/interaction/bds-drawer.ts +59 -82
  41. package/src/web-components/interaction/bds-dropdown-menu-item.ts +124 -0
  42. package/src/web-components/interaction/bds-dropdown-menu.spec.ts +102 -0
  43. package/src/web-components/interaction/bds-dropdown-menu.ts +200 -0
  44. package/src/web-components/interaction/form/BdsCheckbox.mdx +57 -20
  45. package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +35 -36
  46. package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +70 -0
  47. package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +81 -0
  48. package/src/web-components/interaction/form/BdsCombobox.mdx +52 -33
  49. package/src/web-components/interaction/form/BdsCombobox.stories.tsx +55 -53
  50. package/src/web-components/interaction/form/BdsFileInput.mdx +59 -24
  51. package/src/web-components/interaction/form/BdsFileInput.stories.tsx +38 -53
  52. package/src/web-components/interaction/form/BdsFormInput.mdx +98 -0
  53. package/src/web-components/interaction/form/BdsFormInput.stories.tsx +139 -0
  54. package/src/web-components/interaction/form/BdsNumberInput.mdx +51 -22
  55. package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +24 -46
  56. package/src/web-components/interaction/form/BdsRadio.mdx +53 -22
  57. package/src/web-components/interaction/form/BdsRadio.stories.tsx +19 -51
  58. package/src/web-components/interaction/form/BdsRadioGroup.mdx +73 -0
  59. package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +88 -0
  60. package/src/web-components/interaction/form/BdsSegmentedControl.mdx +76 -23
  61. package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +64 -54
  62. package/src/web-components/interaction/form/BdsSelect.mdx +59 -21
  63. package/src/web-components/interaction/form/BdsSelect.stories.tsx +47 -56
  64. package/src/web-components/interaction/form/BdsSlider.mdx +55 -27
  65. package/src/web-components/interaction/form/BdsSlider.stories.tsx +23 -44
  66. package/src/web-components/interaction/form/BdsSwitch.mdx +53 -20
  67. package/src/web-components/interaction/form/BdsSwitch.stories.tsx +22 -37
  68. package/src/web-components/interaction/form/BdsTextarea.mdx +54 -22
  69. package/src/web-components/interaction/form/BdsTextarea.stories.tsx +35 -44
  70. package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +55 -0
  71. package/src/web-components/interaction/form/bds-checkbox-group.ts +117 -0
  72. package/src/web-components/interaction/form/bds-form-input.spec.ts +83 -0
  73. package/src/web-components/interaction/form/bds-form-input.ts +268 -0
  74. package/src/web-components/interaction/form/bds-radio-group.spec.ts +62 -0
  75. package/src/web-components/interaction/form/bds-radio-group.ts +142 -0
  76. package/src/web-components/interaction/form/bds-segmented-control.ts +5 -0
  77. package/src/web-components/ui/BdsAvatar.mdx +29 -20
  78. package/src/web-components/ui/BdsAvatar.stories.tsx +14 -28
  79. package/src/web-components/ui/BdsBreadcrumb.mdx +22 -19
  80. package/src/web-components/ui/BdsBreadcrumb.stories.tsx +11 -21
  81. package/src/web-components/ui/BdsButtonGroup.mdx +69 -0
  82. package/src/web-components/ui/BdsButtonGroup.stories.tsx +66 -0
  83. package/src/web-components/ui/BdsCalendar.mdx +93 -0
  84. package/src/web-components/ui/BdsCalendar.stories.tsx +63 -0
  85. package/src/web-components/ui/BdsCard.mdx +30 -25
  86. package/src/web-components/ui/BdsCard.stories.tsx +19 -42
  87. package/src/web-components/ui/BdsCarousel.mdx +83 -0
  88. package/src/web-components/ui/BdsCarousel.stories.tsx +96 -0
  89. package/src/web-components/ui/BdsDescriptionList.mdx +13 -16
  90. package/src/web-components/ui/BdsDescriptionList.stories.tsx +34 -49
  91. package/src/web-components/ui/BdsIconWrapper.mdx +20 -20
  92. package/src/web-components/ui/BdsIconWrapper.stories.tsx +46 -41
  93. package/src/web-components/ui/BdsLink.mdx +21 -17
  94. package/src/web-components/ui/BdsLink.stories.tsx +19 -20
  95. package/src/web-components/ui/BdsLoading.mdx +9 -13
  96. package/src/web-components/ui/BdsLoading.stories.tsx +8 -10
  97. package/src/web-components/ui/BdsNotificationBanner.mdx +32 -37
  98. package/src/web-components/ui/BdsNotificationBanner.stories.tsx +38 -47
  99. package/src/web-components/ui/BdsPagination.mdx +74 -0
  100. package/src/web-components/ui/BdsPagination.stories.tsx +36 -0
  101. package/src/web-components/ui/BdsProgress.mdx +24 -17
  102. package/src/web-components/ui/BdsProgress.stories.tsx +55 -29
  103. package/src/web-components/ui/BdsProgressCircle.mdx +24 -18
  104. package/src/web-components/ui/BdsProgressCircle.stories.tsx +17 -29
  105. package/src/web-components/ui/BdsRating.mdx +22 -22
  106. package/src/web-components/ui/BdsRating.stories.tsx +19 -13
  107. package/src/web-components/ui/BdsSectionHeader.mdx +31 -28
  108. package/src/web-components/ui/BdsSectionHeader.stories.tsx +21 -23
  109. package/src/web-components/ui/BdsSeparator.mdx +22 -8
  110. package/src/web-components/ui/BdsSeparator.stories.tsx +27 -19
  111. package/src/web-components/ui/BdsSkeleton.mdx +20 -26
  112. package/src/web-components/ui/BdsSkeleton.stories.tsx +23 -23
  113. package/src/web-components/ui/BdsTable.mdx +81 -0
  114. package/src/web-components/ui/BdsTable.stories.tsx +83 -0
  115. package/src/web-components/ui/BdsTypography.mdx +20 -29
  116. package/src/web-components/ui/BdsTypography.stories.tsx +19 -18
  117. package/src/web-components/ui/bds-button-group.spec.ts +40 -0
  118. package/src/web-components/ui/bds-button-group.ts +78 -0
  119. package/src/web-components/ui/bds-calendar.spec.ts +91 -0
  120. package/src/web-components/ui/bds-calendar.ts +427 -0
  121. package/src/web-components/ui/bds-carousel.spec.ts +64 -0
  122. package/src/web-components/ui/bds-carousel.ts +296 -0
  123. package/src/web-components/ui/bds-pagination.spec.ts +67 -0
  124. package/src/web-components/ui/bds-pagination.ts +197 -0
  125. package/src/web-components/ui/bds-table.spec.ts +45 -0
  126. package/src/web-components/ui/bds-table.ts +96 -0
@@ -0,0 +1,83 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsCarousel.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # &lt;bds-carousel&gt;
7
+
8
+ Framework-agnostic Carousel custom element. A scroll-snap based slider where each direct child becomes a slide. Supports dot indicators, looping, and custom previous/next buttons via named slots.
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
+ ## When to use
19
+
20
+ - Cycling through a set of related content cards, images, or feature highlights
21
+ - Use `indicators` to help users understand how many slides exist and which one is active
22
+ - Use `loop` for continuous navigation without boundaries
23
+
24
+ ## Examples
25
+
26
+ ### Default
27
+ <Canvas of={Stories.Default} />
28
+
29
+ ### With indicators
30
+ <Canvas of={Stories.WithIndicators} />
31
+
32
+ ### Loop
33
+ <Canvas of={Stories.Loop} />
34
+
35
+ ## Props
36
+
37
+ <ArgTypes of={Stories} />
38
+
39
+ ## Attributes
40
+
41
+ | Attribute | Type | Default | Description |
42
+ |-----------|------|---------|-------------|
43
+ | `current-index` | number | `0` | Active slide index (0-based) |
44
+ | `indicators` | boolean | `false` | Shows dot indicator buttons below the slides |
45
+ | `loop` | boolean | `false` | Wraps from last slide to first and vice versa |
46
+
47
+ ## Slots
48
+
49
+ | Slot | Description |
50
+ |------|-------------|
51
+ | `(default)` | Slide content — each direct child is one slide |
52
+ | `prev` | Custom previous button (replaces the built-in button) |
53
+ | `next` | Custom next button (replaces the built-in button) |
54
+
55
+ ## Events
56
+
57
+ | Event | Detail | Description |
58
+ |-------|--------|-------------|
59
+ | `bds-slide-change` | `{ index: number }` | Fired when the active slide changes |
60
+
61
+ ## Usage in plain HTML
62
+
63
+ ```html
64
+ <bds-carousel indicators loop>
65
+ <div>Slide 1</div>
66
+ <div>Slide 2</div>
67
+ <div>Slide 3</div>
68
+ </bds-carousel>
69
+
70
+ <script>
71
+ document.querySelector('bds-carousel')
72
+ .addEventListener('bds-slide-change', e => {
73
+ console.log('Active slide:', e.detail.index);
74
+ });
75
+ </script>
76
+ ```
77
+
78
+ ## Accessibility
79
+
80
+ - The slide track has `role="region"` with `aria-label="Carousel"`
81
+ - Dot indicators carry `role="tab"` and `aria-selected`; the indicator list has `role="tablist"`
82
+ - Navigation buttons have descriptive `aria-label` values and are disabled at boundaries (unless `loop` is set)
83
+ - Smooth scroll is suppressed when `prefers-reduced-motion: reduce` is set
@@ -0,0 +1,96 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import '../index'; // auto-registers all custom elements
4
+
5
+ function BdsCarousel({
6
+ currentIndex = 0,
7
+ indicators,
8
+ loop,
9
+ children,
10
+ }: {
11
+ currentIndex?: number;
12
+ indicators?: boolean;
13
+ loop?: boolean;
14
+ children?: React.ReactNode;
15
+ }) {
16
+ return React.createElement(
17
+ 'bds-carousel',
18
+ {
19
+ 'current-index': currentIndex,
20
+ indicators: indicators || undefined,
21
+ loop: loop || undefined,
22
+ },
23
+ children,
24
+ );
25
+ }
26
+
27
+ const slides = ['Slide 1', 'Slide 2', 'Slide 3'].map(label =>
28
+ React.createElement(
29
+ 'div',
30
+ {
31
+ key: label,
32
+ style: {
33
+ background: 'var(--bds-color_bg--subtle, #f3f4f6)',
34
+ display: 'flex',
35
+ alignItems: 'center',
36
+ justifyContent: 'center',
37
+ height: 160,
38
+ fontSize: '1.25rem',
39
+ fontWeight: 600,
40
+ borderRadius: 8,
41
+ },
42
+ },
43
+ label,
44
+ ),
45
+ );
46
+
47
+ const meta = {
48
+ title: 'Web Components/UI/Carousel',
49
+ component: BdsCarousel,
50
+ tags: ['!stable', 'experimental'],
51
+ parameters: { layout: 'padded' },
52
+ argTypes: {
53
+ currentIndex: { control: 'number' },
54
+ indicators: { control: 'boolean' },
55
+ loop: { control: 'boolean' },
56
+ },
57
+ } satisfies Meta<typeof BdsCarousel>;
58
+
59
+ export default meta;
60
+ type Story = StoryObj<typeof meta>;
61
+
62
+ export const Default: Story = {
63
+ render: () => (
64
+ <div style={{ width: 400 }}>
65
+ {React.createElement(
66
+ 'bds-carousel',
67
+ {},
68
+ ...slides,
69
+ )}
70
+ </div>
71
+ ),
72
+ };
73
+
74
+ export const WithIndicators: Story = {
75
+ render: () => (
76
+ <div style={{ width: 400 }}>
77
+ {React.createElement(
78
+ 'bds-carousel',
79
+ { indicators: true },
80
+ ...slides,
81
+ )}
82
+ </div>
83
+ ),
84
+ };
85
+
86
+ export const Loop: Story = {
87
+ render: () => (
88
+ <div style={{ width: 400 }}>
89
+ {React.createElement(
90
+ 'bds-carousel',
91
+ { indicators: true, loop: true },
92
+ ...slides,
93
+ )}
94
+ </div>
95
+ ),
96
+ };
@@ -5,7 +5,7 @@ import * as Stories from './BdsDescriptionList.stories';
5
5
 
6
6
  # &lt;bds-description-list&gt;
7
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.
8
+ Framework-agnostic Description List custom element. Wraps slotted `<dt>`/`<dd>` pairs in a styled `<dl>` with stacked or inline layout.
9
9
 
10
10
  > **Status: experimental** — API may change before stable release.
11
11
 
@@ -15,10 +15,15 @@ Framework-agnostic Description List custom element. Wraps a semantic `<dl>` elem
15
15
  import '@boostdev/components/web-components';
16
16
  ```
17
17
 
18
+ ## When to use
19
+
20
+ - Displaying key–value data such as user profiles, settings summaries, or metadata panels
21
+ - Use `layout="inline"` to place term and value side-by-side on wider screens
22
+
18
23
  ## Examples
19
24
 
20
- ### All layouts
21
- <Canvas of={Stories.AllVariants} />
25
+ ### Default (stacked)
26
+ <Canvas of={Stories.Default} />
22
27
 
23
28
  ### Stacked
24
29
  <Canvas of={Stories.Stacked} />
@@ -34,34 +39,26 @@ import '@boostdev/components/web-components';
34
39
 
35
40
  | Attribute | Type | Default | Description |
36
41
  |-----------|------|---------|-------------|
37
- | `layout` | `"stacked" \| "inline"` | `"stacked"` | Arrangement of terms and details |
42
+ | `layout` | `"stacked" \| "inline"` | `"stacked"` | Display mode stacked places term above value; inline places them side-by-side |
38
43
 
39
44
  ## Slots
40
45
 
41
46
  | Slot | Description |
42
47
  |------|-------------|
43
- | `(default)` | `<dt>` and `<dd>` element pairs |
48
+ | `(default)` | `<dt>` and `<dd>` pairs |
44
49
 
45
50
  ## Usage in plain HTML
46
51
 
47
52
  ```html
48
- <bds-description-list>
53
+ <bds-description-list layout="inline">
49
54
  <dt>Name</dt>
50
55
  <dd>Jane Doe</dd>
51
56
  <dt>Email</dt>
52
57
  <dd>jane@example.com</dd>
53
58
  </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
59
  ```
62
60
 
63
61
  ## Accessibility
64
62
 
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
63
+ - Renders a native `<dl>` element — screen readers announce it as a description list
64
+ - Ensure every `<dt>` has a corresponding `<dd>`
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
- import '../index';
3
+ import '../index'; // auto-registers all custom elements
4
4
 
5
5
  function BdsDescriptionList({
6
- layout,
6
+ layout = 'stacked',
7
7
  children,
8
8
  }: {
9
9
  layout?: 'stacked' | 'inline';
@@ -18,62 +18,47 @@ const meta = {
18
18
  tags: ['!stable', 'experimental'],
19
19
  parameters: { layout: 'centered' },
20
20
  argTypes: {
21
- layout: { control: 'select', options: ['stacked', 'inline'] },
21
+ layout: { control: 'radio', options: ['stacked', 'inline'] },
22
22
  },
23
23
  } satisfies Meta<typeof BdsDescriptionList>;
24
24
 
25
25
  export default meta;
26
26
  type Story = StoryObj<typeof meta>;
27
27
 
28
- const items = [
29
- { term: 'Name', detail: 'Jane Doe' },
30
- { term: 'Email', detail: 'jane@example.com' },
31
- { term: 'Role', detail: 'Designer' },
32
- ];
28
+ export const Default: Story = {
29
+ render: () =>
30
+ React.createElement(
31
+ 'bds-description-list',
32
+ { layout: 'stacked' },
33
+ React.createElement('dt', null, 'Name'),
34
+ React.createElement('dd', null, 'Jane Doe'),
35
+ React.createElement('dt', null, 'Email'),
36
+ React.createElement('dd', null, 'jane@example.com'),
37
+ React.createElement('dt', null, 'Role'),
38
+ React.createElement('dd', null, 'Administrator'),
39
+ ),
40
+ };
33
41
 
34
42
  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
+ render: () =>
44
+ React.createElement(
45
+ 'bds-description-list',
46
+ { layout: 'stacked' },
47
+ React.createElement('dt', null, 'Framework'),
48
+ React.createElement('dd', null, 'Lit'),
49
+ React.createElement('dt', null, 'Language'),
50
+ React.createElement('dd', null, 'TypeScript'),
51
+ ),
43
52
  };
44
53
 
45
54
  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
- ),
55
+ render: () =>
56
+ React.createElement(
57
+ 'bds-description-list',
58
+ { layout: 'inline' },
59
+ React.createElement('dt', null, 'Status'),
60
+ React.createElement('dd', null, 'Active'),
61
+ React.createElement('dt', null, 'Plan'),
62
+ React.createElement('dd', null, 'Professional'),
63
+ ),
79
64
  };
@@ -5,7 +5,7 @@ import * as Stories from './BdsIconWrapper.stories';
5
5
 
6
6
  # &lt;bds-icon-wrapper&gt;
7
7
 
8
- Framework-agnostic Icon Wrapper custom element. Displays an icon inside a circular container with background and foreground theming.
8
+ Framework-agnostic Icon Wrapper custom element. Displays a slotted icon inside a circular container with design-system background and foreground colour theming.
9
9
 
10
10
  > **Status: experimental** — API may change before stable release.
11
11
 
@@ -15,13 +15,21 @@ Framework-agnostic Icon Wrapper custom element. Displays an icon inside a circul
15
15
  import '@boostdev/components/web-components';
16
16
  ```
17
17
 
18
+ ## When to use
19
+
20
+ - To visually highlight a feature icon in a card, empty state, or marketing section
21
+ - Use `aria-hidden="true"` when the icon is purely decorative and adjacent text provides the label
22
+
18
23
  ## Examples
19
24
 
20
- ### All variants
21
- <Canvas of={Stories.AllVariants} />
25
+ ### Default
26
+ <Canvas of={Stories.Default} />
27
+
28
+ ### With label
29
+ <Canvas of={Stories.WithLabel} />
22
30
 
23
- ### With star icon
24
- <Canvas of={Stories.WithStar} />
31
+ ### Multiple
32
+ <Canvas of={Stories.Multiple} />
25
33
 
26
34
  ## Props
27
35
 
@@ -31,7 +39,7 @@ import '@boostdev/components/web-components';
31
39
 
32
40
  | Attribute | Type | Default | Description |
33
41
  |-----------|------|---------|-------------|
34
- | `aria-hidden` | string | — | Set to `"true"` for purely decorative icons |
42
+ | `aria-hidden` | string | — | Set to `"true"` for decorative icons |
35
43
 
36
44
  ## Slots
37
45
 
@@ -43,28 +51,20 @@ import '@boostdev/components/web-components';
43
51
 
44
52
  | Property | Description |
45
53
  |----------|-------------|
46
- | `--icon-wrapper-color` | Circle background colour |
47
- | `--bds-on-icon-wrapper-color` | Icon fill/stroke colour |
54
+ | `--icon-wrapper-color` | Background fill of the circular container |
55
+ | `--bds-on-icon-wrapper-color` | Icon/foreground colour inside the wrapper |
48
56
 
49
57
  ## Usage in plain HTML
50
58
 
51
59
  ```html
52
- <!-- Decorative icon -->
53
60
  <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..."/>
61
+ <svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
62
+ <path d="M12 2l3.09 6.26L22 9.27l-5 4.87L18.18 21 12 17.77 5.82 21 7 14.14 2 9.27l6.91-1.01z"/>
63
63
  </svg>
64
64
  </bds-icon-wrapper>
65
65
  ```
66
66
 
67
67
  ## Accessibility
68
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
69
+ - Use `aria-hidden="true"` when the icon is decorative and the surrounding content provides context
70
+ - When the icon is standalone and meaningful, omit `aria-hidden` and provide a label via adjacent text or `aria-label` on the wrapper
@@ -1,34 +1,25 @@
1
1
  import React from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
- import '../index';
3
+ import '../index'; // auto-registers all custom elements
4
4
 
5
+ // Simple star SVG for demonstration
5
6
  const StarIcon = () =>
6
7
  React.createElement(
7
8
  'svg',
8
- { viewBox: '0 0 24 24', fill: 'currentColor', width: '1.25em', height: '1.25em', 'aria-hidden': 'true' },
9
+ { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 24 24', fill: 'currentColor', width: 24, height: 24 },
9
10
  React.createElement('path', {
10
11
  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
  }),
12
13
  );
13
14
 
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);
15
+ function BdsIconWrapper({
16
+ ariaHidden,
17
+ children,
18
+ }: {
19
+ ariaHidden?: string;
20
+ children?: React.ReactNode;
21
+ }) {
22
+ return React.createElement('bds-icon-wrapper', { 'aria-hidden': ariaHidden }, children);
32
23
  }
33
24
 
34
25
  const meta = {
@@ -36,41 +27,55 @@ const meta = {
36
27
  component: BdsIconWrapper,
37
28
  tags: ['!stable', 'experimental'],
38
29
  parameters: { layout: 'centered' },
30
+ argTypes: {
31
+ ariaHidden: { control: 'text' },
32
+ },
39
33
  } satisfies Meta<typeof BdsIconWrapper>;
40
34
 
41
35
  export default meta;
42
36
  type Story = StoryObj<typeof meta>;
43
37
 
44
- export const WithStar: Story = {
45
- render: () => (
46
- <BdsIconWrapper>
47
- <StarIcon />
48
- </BdsIconWrapper>
49
- ),
38
+ export const Default: Story = {
39
+ render: () =>
40
+ React.createElement(
41
+ 'bds-icon-wrapper',
42
+ { 'aria-hidden': 'true' },
43
+ React.createElement(StarIcon),
44
+ ),
50
45
  };
51
46
 
52
- export const WithCheck: Story = {
53
- render: () => (
54
- <BdsIconWrapper>
55
- <CheckIcon />
56
- </BdsIconWrapper>
57
- ),
47
+ export const Decorative: Story = {
48
+ render: () =>
49
+ React.createElement(
50
+ 'bds-icon-wrapper',
51
+ { 'aria-hidden': 'true' },
52
+ React.createElement(StarIcon),
53
+ ),
58
54
  };
59
55
 
60
- export const WithHeart: Story = {
56
+ export const WithLabel: Story = {
61
57
  render: () => (
62
- <BdsIconWrapper>
63
- <HeartIcon />
64
- </BdsIconWrapper>
58
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
59
+ {React.createElement(
60
+ 'bds-icon-wrapper',
61
+ {},
62
+ React.createElement(StarIcon),
63
+ )}
64
+ <span>Favourites</span>
65
+ </div>
65
66
  ),
66
67
  };
67
68
 
68
- export const AllVariants: Story = {
69
+ export const Multiple: Story = {
69
70
  render: () => (
70
- <div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}>
71
- <BdsIconWrapper><StarIcon /></BdsIconWrapper>
72
- <BdsIconWrapper><CheckIcon /></BdsIconWrapper>
73
- <BdsIconWrapper><HeartIcon /></BdsIconWrapper>
71
+ <div style={{ display: 'flex', gap: 16 }}>
72
+ {[1, 2, 3].map(i =>
73
+ React.createElement(
74
+ 'bds-icon-wrapper',
75
+ { key: i, 'aria-hidden': 'true' },
76
+ React.createElement(StarIcon),
77
+ ),
78
+ )}
74
79
  </div>
75
80
  ),
76
81
  };
@@ -5,7 +5,7 @@ import * as Stories from './BdsLink.stories';
5
5
 
6
6
  # &lt;bds-link&gt;
7
7
 
8
- Framework-agnostic Link custom element. Renders a semantic `<a>` element with three visual styles and built-in external link handling.
8
+ Framework-agnostic Link custom element. Renders a styled `<a>` tag with three visual variants, automatic external link handling, and design-token colours.
9
9
 
10
10
  > **Status: experimental** — API may change before stable release.
11
11
 
@@ -15,6 +15,12 @@ Framework-agnostic Link custom element. Renders a semantic `<a>` element with th
15
15
  import '@boostdev/components/web-components';
16
16
  ```
17
17
 
18
+ ## When to use
19
+
20
+ - Inline hyperlinks within body text (default or subtle)
21
+ - Standalone call-to-action links that should look more prominent (standalone)
22
+ - External links that automatically get `rel="noreferrer noopener"` and a screen reader suffix
23
+
18
24
  ## Examples
19
25
 
20
26
  ### All variants
@@ -23,6 +29,9 @@ import '@boostdev/components/web-components';
23
29
  ### Default
24
30
  <Canvas of={Stories.Default} />
25
31
 
32
+ ### Subtle
33
+ <Canvas of={Stories.Subtle} />
34
+
26
35
  ### Standalone
27
36
  <Canvas of={Stories.Standalone} />
28
37
 
@@ -37,39 +46,34 @@ import '@boostdev/components/web-components';
37
46
 
38
47
  | Attribute | Type | Default | Description |
39
48
  |-----------|------|---------|-------------|
40
- | `href` | string | — | Link destination |
41
- | `target` | string | — | Native anchor target |
49
+ | `href` | string | — | Link destination URL |
50
+ | `target` | string | — | Standard `<a>` target attribute |
42
51
  | `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 |
52
+ | `external` | boolean | `false` | Adds `rel="noreferrer noopener"`, forces `target="_blank"`, and appends a visually-hidden screen reader label |
53
+ | `external-label` | string | `"(opens in new tab)"` | Screen reader text appended when `external` is set |
45
54
 
46
55
  ## Slots
47
56
 
48
57
  | Slot | Description |
49
58
  |------|-------------|
50
- | `(default)` | Link text content |
59
+ | `(default)` | Link text or content |
51
60
 
52
61
  ## CSS Custom Properties
53
62
 
54
63
  | Property | Description |
55
64
  |----------|-------------|
56
- | `--link_color` | Link text colour |
57
- | `--link_color-hover` | Link colour on hover |
65
+ | `--link_color` | Link colour |
66
+ | `--link_color-hover` | Link hover colour |
58
67
 
59
68
  ## Usage in plain HTML
60
69
 
61
70
  ```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
71
  <bds-link href="https://example.com" external>Visit site</bds-link>
72
+ <p>Read our <bds-link href="/privacy">privacy policy</bds-link>.</p>
73
+ <bds-link href="/docs" variant="standalone">View documentation</bds-link>
69
74
  ```
70
75
 
71
76
  ## Accessibility
72
77
 
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"
78
+ - When `external` is set, a visually-hidden `<span>` with configurable text is appended for screen readers
79
+ - Ensure `href` always points to a valid destination