@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
@@ -5,7 +5,7 @@ import * as Stories from './BdsBreadcrumb.stories';
5
5
 
6
6
  # <bds-breadcrumb>
7
7
 
8
- Framework-agnostic Breadcrumb navigation custom element. Renders an ordered list of links showing the user's location in the site hierarchy.
8
+ Framework-agnostic Breadcrumb custom element. Renders a horizontal navigation trail from a JSON array of `{ label, href? }` items. The last item is treated as the current page and rendered without a link.
9
9
 
10
10
  > **Status: experimental** — API may change before stable release.
11
11
 
@@ -15,16 +15,21 @@ Framework-agnostic Breadcrumb navigation custom element. Renders an ordered list
15
15
  import '@boostdev/components/web-components';
16
16
  ```
17
17
 
18
+ ## When to use
19
+
20
+ - On pages more than one level deep in a hierarchy to help users understand their location
21
+ - Always include a link back to the root/home as the first item
22
+
18
23
  ## Examples
19
24
 
20
- ### Default (3 levels)
25
+ ### Default
21
26
  <Canvas of={Stories.Default} />
22
27
 
23
- ### Deep nested
24
- <Canvas of={Stories.DeepNested} />
28
+ ### Short (two levels)
29
+ <Canvas of={Stories.Short} />
25
30
 
26
- ### All variants
27
- <Canvas of={Stories.AllVariants} />
31
+ ### Long (five levels)
32
+ <Canvas of={Stories.Long} />
28
33
 
29
34
  ## Props
30
35
 
@@ -34,30 +39,28 @@ import '@boostdev/components/web-components';
34
39
 
35
40
  | Attribute | Type | Default | Description |
36
41
  |-----------|------|---------|-------------|
37
- | `items` | JSON string | `"[]"` | Array of `{ label: string, href?: string }` objects. The last item is the current page (no link). |
42
+ | `items` | `Array<{ label: string; href?: string }>` | `[]` | Breadcrumb items as a JSON-serialisable array. The last item is always the current page. |
38
43
 
39
44
  ## Usage in plain HTML
40
45
 
41
46
  ```html
42
- <bds-breadcrumb items='[
43
- {"label":"Home","href":"/"},
44
- {"label":"Products","href":"/products"},
45
- {"label":"Detail"}
46
- ]'></bds-breadcrumb>
47
+ <bds-breadcrumb
48
+ items='[{"label":"Home","href":"/"},{"label":"Products","href":"/products"},{"label":"Item"}]'>
49
+ </bds-breadcrumb>
47
50
  ```
48
51
 
49
- In JavaScript:
52
+ Or set the property in JavaScript:
53
+
50
54
  ```js
51
- const crumb = document.querySelector('bds-breadcrumb');
52
- crumb.items = [
55
+ document.querySelector('bds-breadcrumb').items = [
53
56
  { label: 'Home', href: '/' },
54
57
  { label: 'Products', href: '/products' },
55
- { label: 'Detail' },
58
+ { label: 'Item' },
56
59
  ];
57
60
  ```
58
61
 
59
62
  ## Accessibility
60
63
 
61
- - Renders as `<nav aria-label="Breadcrumb">` with an `<ol>` list
62
- - The last item gets `aria-current="page"` — it is not a link
63
- - Separators (`›`) are hidden from screen readers with `aria-hidden="true"`
64
+ - Wrapped in `<nav aria-label="Breadcrumb">` with an `<ol>` list structure
65
+ - The current page item carries `aria-current="page"`
66
+ - Separator characters are `aria-hidden="true"` to avoid screen reader noise
@@ -1,17 +1,17 @@
1
1
  import React from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
  import type { BreadcrumbItem } from './bds-breadcrumb';
4
- import '../index';
4
+ import '../index'; // auto-registers all custom elements
5
5
 
6
6
  function BdsBreadcrumb({ items = [] }: { items?: BreadcrumbItem[] }) {
7
- return React.createElement('bds-breadcrumb', { items: JSON.stringify(items) });
7
+ return React.createElement('bds-breadcrumb', { items });
8
8
  }
9
9
 
10
10
  const meta = {
11
11
  title: 'Web Components/UI/Breadcrumb',
12
12
  component: BdsBreadcrumb,
13
13
  tags: ['!stable', 'experimental'],
14
- parameters: { layout: 'padded' },
14
+ parameters: { layout: 'centered' },
15
15
  } satisfies Meta<typeof BdsBreadcrumb>;
16
16
 
17
17
  export default meta;
@@ -22,38 +22,28 @@ export const Default: Story = {
22
22
  items: [
23
23
  { label: 'Home', href: '/' },
24
24
  { label: 'Products', href: '/products' },
25
- { label: 'Current page' },
25
+ { label: 'Item' },
26
26
  ],
27
27
  },
28
28
  };
29
29
 
30
- export const TwoLevels: Story = {
30
+ export const Short: Story = {
31
31
  args: {
32
32
  items: [
33
33
  { label: 'Home', href: '/' },
34
- { label: 'Current page' },
34
+ { label: 'Current Page' },
35
35
  ],
36
36
  },
37
37
  };
38
38
 
39
- export const DeepNested: Story = {
39
+ export const Long: Story = {
40
40
  args: {
41
41
  items: [
42
42
  { label: 'Home', href: '/' },
43
- { label: 'Category', href: '/category' },
44
- { label: 'Subcategory', href: '/category/sub' },
45
- { label: 'Section', href: '/category/sub/section' },
46
- { label: 'Current page' },
43
+ { label: 'Catalogue', href: '/catalogue' },
44
+ { label: 'Electronics', href: '/catalogue/electronics' },
45
+ { label: 'Phones', href: '/catalogue/electronics/phones' },
46
+ { label: 'Smartphone X' },
47
47
  ],
48
48
  },
49
49
  };
50
-
51
- export const AllVariants: Story = {
52
- render: () => (
53
- <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
54
- <BdsBreadcrumb items={[{ label: 'Home', href: '/' }, { label: 'Current' }]} />
55
- <BdsBreadcrumb items={[{ label: 'Home', href: '/' }, { label: 'Products', href: '/products' }, { label: 'Detail' }]} />
56
- <BdsBreadcrumb items={[{ label: 'Home', href: '/' }, { label: 'A', href: '/a' }, { label: 'B', href: '/a/b' }, { label: 'C', href: '/a/b/c' }, { label: 'Current' }]} />
57
- </div>
58
- ),
59
- };
@@ -0,0 +1,69 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsButtonGroup.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # &lt;bds-button-group&gt;
7
+
8
+ Framework-agnostic Button Group custom element. Groups related `<bds-button>` elements in a flex container, collapsing adjacent border radii so they read as a single unit.
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
+ - Toggle groups (Day / Week / Month), segmented actions, or sets of closely related operations
21
+ - Use `orientation="vertical"` for stacked navigation menus
22
+
23
+ ## Examples
24
+
25
+ ### Default (horizontal)
26
+ <Canvas of={Stories.Default} />
27
+
28
+ ### Horizontal
29
+ <Canvas of={Stories.Horizontal} />
30
+
31
+ ### Vertical
32
+ <Canvas of={Stories.Vertical} />
33
+
34
+ ## Props
35
+
36
+ <ArgTypes of={Stories} />
37
+
38
+ ## Attributes
39
+
40
+ | Attribute | Type | Default | Description |
41
+ |-----------|------|---------|-------------|
42
+ | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Flex direction of the group |
43
+ | `aria-label` | string | — | Accessible label for the group container |
44
+
45
+ ## Slots
46
+
47
+ | Slot | Description |
48
+ |------|-------------|
49
+ | `(default)` | `<bds-button>` elements |
50
+
51
+ ## Usage in plain HTML
52
+
53
+ ```html
54
+ <bds-button-group aria-label="View options">
55
+ <bds-button>Day</bds-button>
56
+ <bds-button>Week</bds-button>
57
+ <bds-button>Month</bds-button>
58
+ </bds-button-group>
59
+
60
+ <bds-button-group orientation="vertical" aria-label="Navigation">
61
+ <bds-button>Profile</bds-button>
62
+ <bds-button>Settings</bds-button>
63
+ </bds-button-group>
64
+ ```
65
+
66
+ ## Accessibility
67
+
68
+ - Renders a `<div role="group">` with the provided `aria-label`
69
+ - Always set `aria-label` to describe the purpose of the group
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import type { ButtonGroupOrientation } from './bds-button-group';
4
+ import '../index'; // auto-registers all custom elements
5
+
6
+ function BdsButtonGroup({
7
+ orientation = 'horizontal',
8
+ ariaLabel,
9
+ children,
10
+ }: {
11
+ orientation?: ButtonGroupOrientation;
12
+ ariaLabel?: string;
13
+ children?: React.ReactNode;
14
+ }) {
15
+ return React.createElement(
16
+ 'bds-button-group',
17
+ { orientation, 'aria-label': ariaLabel },
18
+ children,
19
+ );
20
+ }
21
+
22
+ const meta = {
23
+ title: 'Web Components/UI/ButtonGroup',
24
+ component: BdsButtonGroup,
25
+ tags: ['!stable', 'experimental'],
26
+ parameters: { layout: 'centered' },
27
+ argTypes: {
28
+ orientation: { control: 'radio', options: ['horizontal', 'vertical'] },
29
+ ariaLabel: { control: 'text' },
30
+ },
31
+ } satisfies Meta<typeof BdsButtonGroup>;
32
+
33
+ export default meta;
34
+ type Story = StoryObj<typeof meta>;
35
+
36
+ export const Default: Story = {
37
+ render: () =>
38
+ React.createElement(
39
+ 'bds-button-group',
40
+ { 'aria-label': 'View options' },
41
+ React.createElement('bds-button', null, 'Day'),
42
+ React.createElement('bds-button', null, 'Week'),
43
+ React.createElement('bds-button', null, 'Month'),
44
+ ),
45
+ };
46
+
47
+ export const Horizontal: Story = {
48
+ render: () =>
49
+ React.createElement(
50
+ 'bds-button-group',
51
+ { orientation: 'horizontal', 'aria-label': 'Actions' },
52
+ React.createElement('bds-button', null, 'Save'),
53
+ React.createElement('bds-button', { variant: 'ghost' }, 'Cancel'),
54
+ ),
55
+ };
56
+
57
+ export const Vertical: Story = {
58
+ render: () =>
59
+ React.createElement(
60
+ 'bds-button-group',
61
+ { orientation: 'vertical', 'aria-label': 'Navigation' },
62
+ React.createElement('bds-button', null, 'Profile'),
63
+ React.createElement('bds-button', null, 'Settings'),
64
+ React.createElement('bds-button', null, 'Logout'),
65
+ ),
66
+ };
@@ -0,0 +1,93 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsCalendar.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # &lt;bds-calendar&gt;
7
+
8
+ Framework-agnostic Calendar date picker custom element. Renders a keyboard-navigable month grid for selecting a date. Participates in HTML forms via the Form-Associated Custom Elements API.
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
+ - Date selection fields in forms where a visual calendar grid aids comprehension
21
+ - Pair with `min`/`max` to restrict selectable dates to a valid range
22
+
23
+ ## Examples
24
+
25
+ ### Default
26
+ <Canvas of={Stories.Default} />
27
+
28
+ ### With preselected value
29
+ <Canvas of={Stories.WithValue} />
30
+
31
+ ### With min/max range
32
+ <Canvas of={Stories.WithMinMax} />
33
+
34
+ ### Disabled
35
+ <Canvas of={Stories.Disabled} />
36
+
37
+ ### Inside a form
38
+ <Canvas of={Stories.FormField} />
39
+
40
+ ## Props
41
+
42
+ <ArgTypes of={Stories} />
43
+
44
+ ## Attributes
45
+
46
+ | Attribute | Type | Default | Description |
47
+ |-----------|------|---------|-------------|
48
+ | `name` | string | — | Form field name — required to participate in form submission |
49
+ | `value` | string | — | Selected date in ISO 8601 format (`YYYY-MM-DD`) |
50
+ | `min` | string | — | Minimum selectable date (ISO 8601) |
51
+ | `max` | string | — | Maximum selectable date (ISO 8601) |
52
+ | `disabled` | boolean | `false` | Disables all interaction |
53
+
54
+ ## Events
55
+
56
+ | Event | Detail | Description |
57
+ |-------|--------|-------------|
58
+ | `bds-date-select` | `{ date: string }` | Fired when the user selects a date; `date` is in `YYYY-MM-DD` format |
59
+
60
+ ## Usage in plain HTML
61
+
62
+ ```html
63
+ <bds-calendar name="birthday" value="2026-04-15"></bds-calendar>
64
+
65
+ <!-- With constraints -->
66
+ <bds-calendar name="departure" min="2026-04-01" max="2026-12-31"></bds-calendar>
67
+
68
+ <script>
69
+ document.querySelector('bds-calendar')
70
+ .addEventListener('bds-date-select', e => {
71
+ console.log('Selected date:', e.detail.date);
72
+ });
73
+ </script>
74
+ ```
75
+
76
+ ## Keyboard Navigation
77
+
78
+ | Key | Action |
79
+ |-----|--------|
80
+ | Arrow keys | Move focus between days |
81
+ | Home | Move to start of week |
82
+ | End | Move to end of week |
83
+ | Page Up | Navigate to previous month |
84
+ | Page Down | Navigate to next month |
85
+ | Enter / Space | Select focused date |
86
+
87
+ ## Accessibility
88
+
89
+ - Uses the ARIA grid pattern with `role="grid"` and `role="gridcell"` on day buttons
90
+ - The month/year label uses `aria-live="polite"` for screen reader announcements on navigation
91
+ - Each day carries `aria-selected` and `aria-current="date"` (today)
92
+ - Disabled days carry `aria-disabled="true"` and are not focusable
93
+ - Respects the `disabled` attribute at the host level
@@ -0,0 +1,63 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import '../index'; // auto-registers all custom elements
4
+
5
+ function BdsCalendar({
6
+ name,
7
+ value,
8
+ min,
9
+ max,
10
+ disabled,
11
+ }: {
12
+ name?: string;
13
+ value?: string;
14
+ min?: string;
15
+ max?: string;
16
+ disabled?: boolean;
17
+ }) {
18
+ return React.createElement('bds-calendar', {
19
+ name,
20
+ value,
21
+ min,
22
+ max,
23
+ disabled: disabled || undefined,
24
+ });
25
+ }
26
+
27
+ const meta = {
28
+ title: 'Web Components/UI/Calendar',
29
+ component: BdsCalendar,
30
+ tags: ['!stable', 'experimental'],
31
+ parameters: { layout: 'centered' },
32
+ argTypes: {
33
+ value: { control: 'text' },
34
+ min: { control: 'text' },
35
+ max: { control: 'text' },
36
+ disabled: { control: 'boolean' },
37
+ },
38
+ } satisfies Meta<typeof BdsCalendar>;
39
+
40
+ export default meta;
41
+ type Story = StoryObj<typeof meta>;
42
+
43
+ export const Default: Story = { args: {} };
44
+ export const WithValue: Story = { args: { value: '2026-04-15' } };
45
+ export const WithMinMax: Story = {
46
+ args: { value: '2026-04-10', min: '2026-04-05', max: '2026-04-25' },
47
+ };
48
+ export const Disabled: Story = { args: { value: '2026-04-10', disabled: true } };
49
+ export const FormField: Story = {
50
+ render: () => (
51
+ <form
52
+ onSubmit={(e) => {
53
+ e.preventDefault();
54
+ const data = new FormData(e.currentTarget);
55
+ alert(`Selected: ${data.get('birthday') ?? 'none'}`);
56
+ }}
57
+ style={{ display: 'flex', flexDirection: 'column', gap: 16, alignItems: 'flex-start' }}
58
+ >
59
+ {React.createElement('bds-calendar', { name: 'birthday' })}
60
+ <button type="submit">Submit</button>
61
+ </form>
62
+ ),
63
+ };
@@ -5,7 +5,7 @@ import * as Stories from './BdsCard.stories';
5
5
 
6
6
  # &lt;bds-card&gt;
7
7
 
8
- Framework-agnostic Card layout element. Groups related content in a contained surface with optional elevation or outline. Can be made clickable.
8
+ Framework-agnostic Card layout custom element. A versatile container with three visual variants, configurable padding, text alignment, and an optional clickable mode.
9
9
 
10
10
  > **Status: experimental** — API may change before stable release.
11
11
 
@@ -15,13 +15,27 @@ Framework-agnostic Card layout element. Groups related content in a contained su
15
15
  import '@boostdev/components/web-components';
16
16
  ```
17
17
 
18
+ ## When to use
19
+
20
+ - Grouping related content visually (product listings, dashboard widgets, feature summaries)
21
+ - Use `clickable` to turn the entire card into an interactive button (e.g. navigation tiles)
22
+
18
23
  ## Examples
19
24
 
20
25
  ### All variants
21
26
  <Canvas of={Stories.AllVariants} />
22
27
 
28
+ ### Default
29
+ <Canvas of={Stories.Default} />
30
+
31
+ ### Elevated
32
+ <Canvas of={Stories.Elevated} />
33
+
34
+ ### Outlined
35
+ <Canvas of={Stories.Outlined} />
36
+
23
37
  ### Clickable
24
- <Canvas of={Stories.Interactive} />
38
+ <Canvas of={Stories.Clickable} />
25
39
 
26
40
  ## Props
27
41
 
@@ -31,11 +45,11 @@ import '@boostdev/components/web-components';
31
45
 
32
46
  | Attribute | Type | Default | Description |
33
47
  |-----------|------|---------|-------------|
34
- | `variant` | `"default" \| "elevated" \| "outlined"` | `"default"` | Visual style |
35
- | `padding` | `"none" \| "small" \| "medium" \| "large"` | `"medium"` | Internal spacing |
36
- | `text-align` | `"start" \| "center" \| "end"` | `"start"` | Content alignment |
37
- | `clickable` | boolean | `false` | Renders as `<button>`, adds hover/active effects |
38
- | `aria-label` | string | — | Accessible label (required when `clickable` and the card content is not descriptive enough) |
48
+ | `variant` | `"default" \| "elevated" \| "outlined"` | `"default"` | Shadow and border style |
49
+ | `padding` | `"none" \| "small" \| "medium" \| "large"` | `"medium"` | Inner padding |
50
+ | `text-align` | `"start" \| "center" \| "end"` | `"start"` | Text alignment |
51
+ | `clickable` | boolean | `false` | Renders the card as a `<button>` with hover/active effects |
52
+ | `aria-label` | string | — | Accessible label (recommended when `clickable` is set) |
39
53
 
40
54
  ## Slots
41
55
 
@@ -45,39 +59,30 @@ import '@boostdev/components/web-components';
45
59
 
46
60
  ## Events
47
61
 
48
- | Event | Detail | Bubbles | Composed | Description |
49
- |-------|--------|---------|----------|-------------|
50
- | `bds-click` | — | Yes | Yes | Fired when a clickable card is activated |
62
+ | Event | Detail | Description |
63
+ |-------|--------|-------------|
64
+ | `bds-click` | — | Fired when the card button is clicked (only when `clickable` is set) |
51
65
 
52
66
  ## CSS Custom Properties
53
67
 
54
68
  | Property | Description |
55
69
  |----------|-------------|
56
70
  | `--card_color` | Card background colour |
57
- | `--card_on-color` | Card text colour |
71
+ | `--card_on-color` | Card text/foreground colour |
58
72
 
59
73
  ## Usage in plain HTML
60
74
 
61
75
  ```html
62
76
  <bds-card variant="elevated" padding="large">
63
- <h3>Card title</h3>
64
- <p>Card body text.</p>
77
+ <p>Card content</p>
65
78
  </bds-card>
66
79
 
67
- <bds-card clickable aria-label="View product details">
68
- <strong>Clickable card</strong>
80
+ <bds-card clickable aria-label="Open details">
81
+ <p>Clickable card</p>
69
82
  </bds-card>
70
83
  ```
71
84
 
72
- Listen for click:
73
- ```js
74
- document.querySelector('bds-card[clickable]').addEventListener('bds-click', () => {
75
- console.log('card clicked');
76
- });
77
- ```
78
-
79
85
  ## Accessibility
80
86
 
81
- - Non-clickable cards are `<div>` elements they have no interactive role
82
- - Clickable cards render as `<button>` with keyboard and pointer support
83
- - Always provide `aria-label` on clickable cards when the text content alone is not descriptive
87
+ - Static cards render as `<div>`; clickable cards render as `<button type="button">`
88
+ - Always provide `aria-label` on clickable cards to give a meaningful accessible name
@@ -1,15 +1,14 @@
1
1
  import React from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
  import type { CardVariant, CardPadding, CardTextAlign } from './bds-card';
4
- import '../index';
4
+ import '../index'; // auto-registers all custom elements
5
5
 
6
6
  function BdsCard({
7
- variant,
8
- padding,
9
- textAlign,
7
+ variant = 'default',
8
+ padding = 'medium',
9
+ textAlign = 'start',
10
10
  clickable,
11
11
  ariaLabel,
12
- onClick,
13
12
  children,
14
13
  }: {
15
14
  variant?: CardVariant;
@@ -17,7 +16,6 @@ function BdsCard({
17
16
  textAlign?: CardTextAlign;
18
17
  clickable?: boolean;
19
18
  ariaLabel?: string;
20
- onClick?: () => void;
21
19
  children?: React.ReactNode;
22
20
  }) {
23
21
  return React.createElement(
@@ -28,7 +26,6 @@ function BdsCard({
28
26
  'text-align': textAlign,
29
27
  clickable: clickable || undefined,
30
28
  'aria-label': ariaLabel,
31
- onbdsclick: onClick,
32
29
  },
33
30
  children,
34
31
  );
@@ -40,51 +37,31 @@ const meta = {
40
37
  tags: ['!stable', 'experimental'],
41
38
  parameters: { layout: 'centered' },
42
39
  argTypes: {
43
- variant: { control: 'select', options: ['default', 'elevated', 'outlined'] },
44
- padding: { control: 'select', options: ['none', 'small', 'medium', 'large'] },
40
+ variant: { control: 'select', options: ['default', 'elevated', 'outlined'] },
41
+ padding: { control: 'select', options: ['none', 'small', 'medium', 'large'] },
45
42
  textAlign: { control: 'select', options: ['start', 'center', 'end'] },
43
+ clickable: { control: 'boolean' },
46
44
  },
47
45
  } satisfies Meta<typeof BdsCard>;
48
46
 
49
47
  export default meta;
50
48
  type Story = StoryObj<typeof meta>;
51
49
 
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 } };
50
+ export const Default: Story = { args: { variant: 'default', padding: 'medium', children: 'Card content goes here.' } };
51
+ export const Elevated: Story = { args: { variant: 'elevated', padding: 'medium', children: 'Elevated card.' } };
52
+ export const Outlined: Story = { args: { variant: 'outlined', padding: 'medium', children: 'Outlined card.' } };
53
+ export const Clickable: Story = {
54
+ args: { variant: 'default', padding: 'medium', clickable: true, ariaLabel: 'Open details', children: 'Clickable card hover me.' },
55
+ };
63
56
 
64
57
  export const AllVariants: Story = {
65
58
  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
- ))}
59
+ <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', maxWidth: 600 }}>
60
+ {(['default', 'elevated', 'outlined'] as const).map(v =>
61
+ React.createElement('bds-card', { key: v, variant: v, padding: 'medium', style: 'width: 160px' },
62
+ React.createElement('p', { style: 'margin: 0' }, v),
63
+ ),
64
+ )}
73
65
  </div>
74
66
  ),
75
67
  };
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
- };