@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
@@ -1,9 +1,20 @@
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
- function BdsSkipLink({ href, children }: { href?: string; children?: React.ReactNode }) {
6
- return React.createElement('bds-skip-link', { href }, children);
5
+ // Thin React wrapper so Storybook controls work with the custom element's props
6
+ function BdsSkipLink({
7
+ href = '#main',
8
+ children,
9
+ }: {
10
+ href?: string;
11
+ children?: React.ReactNode;
12
+ }) {
13
+ return React.createElement(
14
+ 'bds-skip-link',
15
+ { href },
16
+ children,
17
+ );
7
18
  }
8
19
 
9
20
  const meta = {
@@ -11,45 +22,47 @@ const meta = {
11
22
  component: BdsSkipLink,
12
23
  tags: ['!stable', 'experimental'],
13
24
  parameters: { layout: 'padded' },
25
+ argTypes: {
26
+ href: { control: 'text' },
27
+ },
14
28
  } satisfies Meta<typeof BdsSkipLink>;
15
29
 
16
30
  export default meta;
17
31
  type Story = StoryObj<typeof meta>;
18
32
 
19
33
  export const Default: Story = {
20
- render: () => (
21
- <div style={{ position: 'relative', minHeight: '80px', border: '1px dashed #ccc', padding: '8px' }}>
22
- <p style={{ margin: '0 0 8px', fontSize: '0.75rem', color: '#888' }}>
23
- Tab into this area to reveal the skip link
24
- </p>
25
- <BdsSkipLink href="#main">Skip to main content</BdsSkipLink>
26
- <p style={{ margin: '8px 0 0', fontSize: '0.875rem' }}>Press Tab to focus the skip link above</p>
27
- </div>
28
- ),
29
- };
30
-
31
- export const CustomTarget: Story = {
32
- render: () => (
33
- <div style={{ position: 'relative', minHeight: '80px' }}>
34
- <BdsSkipLink href="#main-content">Skip to main content</BdsSkipLink>
35
- </div>
36
- ),
37
- };
38
-
39
- export const CustomLabel: Story = {
40
- render: () => (
41
- <div style={{ position: 'relative', minHeight: '80px' }}>
42
- <BdsSkipLink href="#content">Jump to content</BdsSkipLink>
43
- </div>
44
- ),
34
+ args: { href: '#main' },
35
+ // Hint to the viewer that the link is visually hidden until focused
36
+ decorators: [
37
+ (Story) => (
38
+ <div>
39
+ <p style={{ marginBottom: '0.5rem', fontSize: '0.875rem', color: 'var(--bds-color_on-bg, #333)' }}>
40
+ Tab into the preview to see the skip link appear.
41
+ </p>
42
+ <Story />
43
+ <main id="main" style={{ marginTop: '1rem', padding: '1rem', border: '1px dashed currentcolor' }}>
44
+ Main content area
45
+ </main>
46
+ </div>
47
+ ),
48
+ ],
45
49
  };
46
50
 
47
- export const AllVariants: Story = {
48
- render: () => (
49
- <div style={{ position: 'relative', minHeight: '120px', display: 'flex', flexDirection: 'column', gap: '8px' }}>
50
- <p style={{ margin: 0, fontSize: '0.75rem', color: '#888' }}>Tab to reveal — visually hidden until focused</p>
51
- <BdsSkipLink href="#main">Skip to main content</BdsSkipLink>
52
- <BdsSkipLink href="#nav">Skip to navigation</BdsSkipLink>
51
+ export const WithCustomLabel: Story = {
52
+ args: { href: '#content' },
53
+ render: (args) => (
54
+ <div>
55
+ <p style={{ marginBottom: '0.5rem', fontSize: '0.875rem', color: 'var(--bds-color_on-bg, #333)' }}>
56
+ Tab into the preview to see the skip link appear.
57
+ </p>
58
+ {React.createElement(
59
+ 'bds-skip-link',
60
+ { href: args.href },
61
+ 'Skip to main content',
62
+ )}
63
+ <main id="content" style={{ marginTop: '1rem', padding: '1rem', border: '1px dashed currentcolor' }}>
64
+ Main content area
65
+ </main>
53
66
  </div>
54
67
  ),
55
68
  };
@@ -3,45 +3,115 @@ import * as Stories from './BdsTabs.stories';
3
3
 
4
4
  <Meta of={Stories} />
5
5
 
6
- # Tabs
6
+ # &lt;bds-tabs&gt;
7
7
 
8
- `<bds-tabs>` provides a tabbed interface. Place `<bds-tab>` elements in the `tab` slot and matching `<bds-tab-panel>` elements in the `panel` slot. Tabs and panels are associated by order.
8
+ Framework-agnostic Tabs custom element. Coordinates `<bds-tab>` and `<bds-tab-panel>` children by DOM order, with full keyboard navigation.
9
9
 
10
- ## Usage
10
+ > **Status: experimental** — API may change before stable release.
11
+
12
+ ## Installation
13
+
14
+ ```js
15
+ import '@boostdev/components/web-components';
16
+ ```
17
+
18
+ Or in HTML without a bundler:
11
19
 
12
20
  ```html
13
- <bds-tabs>
14
- <bds-tab slot="tab">Overview</bds-tab>
15
- <bds-tab slot="tab">Details</bds-tab>
16
- <bds-tab-panel slot="panel">
17
- <p>Overview content.</p>
18
- </bds-tab-panel>
19
- <bds-tab-panel slot="panel">
20
- <p>Details content.</p>
21
- </bds-tab-panel>
22
- </bds-tabs>
21
+ <script type="module">
22
+ import '@boostdev/components/web-components';
23
+ </script>
23
24
  ```
24
25
 
25
- ## Attributes `<bds-tab>`
26
+ ## When to use
26
27
 
27
- | Attribute | Type | Default | Description |
28
- |------------|-----------|---------|---------------------------|
29
- | `disabled` | `boolean` | `false` | Prevents selecting this tab |
28
+ - Organising related content into sections when only one section is shown at a time
29
+ - Switching between views at the same level of hierarchy
30
30
 
31
- ## Keyboard Navigation
31
+ ## When not to use
32
32
 
33
- | Key | Action |
34
- |-------------|-------------------------|
35
- | `ArrowLeft` | Move to previous tab |
36
- | `ArrowRight`| Move to next tab |
37
- | `Home` | Move to first tab |
38
- | `End` | Move to last tab |
33
+ - For navigation between pages — use `<a>` links or a router
34
+ - When all sections should be visible at once — use an accordion or separate sections
39
35
 
40
- ## Stories
36
+ ## Examples
41
37
 
38
+ ### Default
42
39
  <Canvas of={Stories.Default} />
43
- <Canvas of={Stories.AllVariants} />
44
40
 
45
- ## Controls
41
+ ### With disabled tab
42
+ <Canvas of={Stories.WithDisabledTab} />
43
+
44
+ ### Many tabs
45
+ <Canvas of={Stories.ManyTabs} />
46
+
47
+ ## Props
46
48
 
47
49
  <ArgTypes of={Stories} />
50
+
51
+ ## Attributes — `<bds-tabs>`
52
+
53
+ `<bds-tabs>` has no configurable attributes. Tab state and active index are managed internally.
54
+
55
+ ## Attributes — `<bds-tab>`
56
+
57
+ | Attribute | Type | Default | Description |
58
+ |-----------|------|---------|-------------|
59
+ | `disabled` | boolean | `false` | Prevents the tab from being selected; tab is skipped during keyboard navigation |
60
+ | `selected` | boolean | `false` | Set by the parent `<bds-tabs>`; do not set manually |
61
+
62
+ ## Slots
63
+
64
+ ### `<bds-tabs>`
65
+
66
+ | Slot | Description |
67
+ |------|-------------|
68
+ | `tab` | `<bds-tab>` elements — rendered in the tab list |
69
+ | `panel` | `<bds-tab-panel>` elements — rendered below the tab list |
70
+
71
+ ### `<bds-tab>`
72
+
73
+ | Slot | Description |
74
+ |------|-------------|
75
+ | `(default)` | Tab label text |
76
+
77
+ ### `<bds-tab-panel>`
78
+
79
+ | Slot | Description |
80
+ |------|-------------|
81
+ | `(default)` | Panel content displayed when the corresponding tab is selected |
82
+
83
+ ## Events
84
+
85
+ | Event | Fired by | Bubbles | Description |
86
+ |-------|----------|---------|-------------|
87
+ | `bds-tab-select` | `<bds-tab>` | Yes | Fired when a tab is clicked; handled internally by `<bds-tabs>` |
88
+
89
+ ## Usage in plain HTML
90
+
91
+ Tabs and panels are matched by DOM order (first tab → first panel, etc.):
92
+
93
+ ```html
94
+ <bds-tabs>
95
+ <bds-tab slot="tab">Overview</bds-tab>
96
+ <bds-tab slot="tab">Details</bds-tab>
97
+ <bds-tab slot="tab" disabled>Disabled</bds-tab>
98
+
99
+ <bds-tab-panel slot="panel">
100
+ <p>Overview content</p>
101
+ </bds-tab-panel>
102
+ <bds-tab-panel slot="panel">
103
+ <p>Details content</p>
104
+ </bds-tab-panel>
105
+ <bds-tab-panel slot="panel">
106
+ <p>Disabled tab content (unreachable)</p>
107
+ </bds-tab-panel>
108
+ </bds-tabs>
109
+ ```
110
+
111
+ ## Accessibility
112
+
113
+ - The tab list container has `role="tablist"`
114
+ - Each `<bds-tab>` renders a `<button role="tab">` with `aria-selected` and the correct `tabindex` (0 for active, -1 for others)
115
+ - Each `<bds-tab-panel>` receives `hidden` when not active
116
+ - Keyboard navigation: ArrowRight / ArrowLeft cycles through enabled tabs; Home / End jumps to first / last enabled tab
117
+ - Disabled tabs are excluded from keyboard navigation
@@ -1,80 +1,94 @@
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
- function TabsDemo({ withDisabled = false }: { withDisabled?: boolean }) {
5
+ // Thin React wrapper so Storybook controls work with the custom element's props
6
+ function BdsTabs({
7
+ tabs = ['Overview', 'Details', 'Settings'],
8
+ panels = ['Overview content.', 'Details content.', 'Settings content.'],
9
+ }: {
10
+ tabs?: string[];
11
+ panels?: string[];
12
+ }) {
6
13
  return React.createElement(
7
14
  'bds-tabs',
8
15
  null,
9
- React.createElement('bds-tab', { slot: 'tab' }, 'Overview'),
10
- React.createElement('bds-tab', { slot: 'tab' }, 'Details'),
11
- withDisabled
12
- ? React.createElement('bds-tab', { slot: 'tab', disabled: true }, 'Disabled')
13
- : React.createElement('bds-tab', { slot: 'tab' }, 'Settings'),
14
- React.createElement(
15
- 'bds-tab-panel',
16
- { slot: 'panel' },
17
- React.createElement('p', { style: { margin: 0 } }, 'Overview content — general information about this item.'),
18
- ),
19
- React.createElement(
20
- 'bds-tab-panel',
21
- { slot: 'panel' },
22
- React.createElement('p', { style: { margin: 0 } }, 'Details content — technical specifications and metadata.'),
16
+ ...tabs.map(label =>
17
+ React.createElement('bds-tab', { key: label, slot: 'tab' }, label),
23
18
  ),
24
- React.createElement(
25
- 'bds-tab-panel',
26
- { slot: 'panel' },
27
- withDisabled
28
- ? React.createElement('p', { style: { margin: 0 } }, 'Disabled tab content.')
29
- : React.createElement('p', { style: { margin: 0 } }, 'Settings content — configuration options.'),
19
+ ...panels.map((content, i) =>
20
+ React.createElement(
21
+ 'bds-tab-panel',
22
+ { key: i, slot: 'panel' },
23
+ React.createElement('p', { style: { margin: 0 } }, content),
24
+ ),
30
25
  ),
31
26
  );
32
27
  }
33
28
 
34
- // Thin wrapper so Storybook controls work
35
- function BdsTabs({ withDisabled }: { withDisabled?: boolean }) {
36
- return React.createElement(TabsDemo, { withDisabled });
37
- }
38
-
39
29
  const meta = {
40
30
  title: 'Web Components/Interaction/Tabs',
41
31
  component: BdsTabs,
42
32
  tags: ['!stable', 'experimental'],
43
33
  parameters: { layout: 'padded' },
44
- argTypes: {
45
- withDisabled: { control: 'boolean' },
46
- },
47
34
  } satisfies Meta<typeof BdsTabs>;
48
35
 
49
36
  export default meta;
50
37
  type Story = StoryObj<typeof meta>;
51
38
 
52
- export const Default: Story = { args: { withDisabled: false } };
53
- export const WithDisabledTab: Story = { args: { withDisabled: true } };
39
+ export const Default: Story = {
40
+ args: {
41
+ tabs: ['Overview', 'Details', 'Settings'],
42
+ panels: [
43
+ 'Overview content. Use tabs to divide related content into sections.',
44
+ 'Details content. Each tab maps to a panel by DOM order.',
45
+ 'Settings content. Navigate with ArrowLeft / ArrowRight keys.',
46
+ ],
47
+ },
48
+ };
54
49
 
55
- export const AllVariants: Story = {
56
- render: () => (
57
- <div style={{ display: 'flex', flexDirection: 'column', gap: '32px' }}>
58
- <div>
59
- <p style={{ margin: '0 0 8px', fontSize: '0.75rem', color: '#888', fontWeight: 600 }}>Standard tabs</p>
60
- <TabsDemo />
61
- </div>
62
- <div>
63
- <p style={{ margin: '0 0 8px', fontSize: '0.75rem', color: '#888', fontWeight: 600 }}>With disabled tab</p>
64
- <TabsDemo withDisabled />
65
- </div>
66
- </div>
67
- ),
50
+ export const WithDisabledTab: Story = {
51
+ render: () =>
52
+ React.createElement(
53
+ 'bds-tabs',
54
+ null,
55
+ React.createElement('bds-tab', { slot: 'tab' }, 'Active'),
56
+ React.createElement('bds-tab', { slot: 'tab', disabled: '' }, 'Disabled'),
57
+ React.createElement('bds-tab', { slot: 'tab' }, 'Another'),
58
+ React.createElement(
59
+ 'bds-tab-panel',
60
+ { slot: 'panel' },
61
+ React.createElement('p', { style: { margin: 0 } }, 'Content for the first tab.'),
62
+ ),
63
+ React.createElement(
64
+ 'bds-tab-panel',
65
+ { slot: 'panel' },
66
+ React.createElement('p', { style: { margin: 0 } }, 'This panel is paired with the disabled tab.'),
67
+ ),
68
+ React.createElement(
69
+ 'bds-tab-panel',
70
+ { slot: 'panel' },
71
+ React.createElement('p', { style: { margin: 0 } }, 'Content for the third tab.'),
72
+ ),
73
+ ),
68
74
  };
69
75
 
70
- export const Interactive: Story = {
71
- name: 'Interactive (keyboard nav)',
72
- render: () => (
73
- <div>
74
- <p style={{ margin: '0 0 12px', fontSize: '0.875rem', color: '#666' }}>
75
- Use ArrowLeft / ArrowRight to navigate between tabs. Home / End jump to first / last.
76
- </p>
77
- <TabsDemo />
78
- </div>
79
- ),
76
+ export const ManyTabs: Story = {
77
+ render: () => {
78
+ const labels = ['Dashboard', 'Reports', 'Users', 'Billing', 'Integrations'];
79
+ return React.createElement(
80
+ 'bds-tabs',
81
+ null,
82
+ ...labels.map(label =>
83
+ React.createElement('bds-tab', { key: label, slot: 'tab' }, label),
84
+ ),
85
+ ...labels.map((label, i) =>
86
+ React.createElement(
87
+ 'bds-tab-panel',
88
+ { key: i, slot: 'panel' },
89
+ React.createElement('p', { style: { margin: 0 } }, `${label} content area.`),
90
+ ),
91
+ ),
92
+ );
93
+ },
80
94
  };
@@ -3,36 +3,102 @@ import * as Stories from './BdsTooltip.stories';
3
3
 
4
4
  <Meta of={Stories} />
5
5
 
6
- # Tooltip
6
+ # &lt;bds-tooltip&gt;
7
7
 
8
- `<bds-tooltip>` wraps any trigger element and shows a floating tooltip on hover and focus. The tooltip is dismissed on mouse-out and blur.
8
+ Framework-agnostic Tooltip custom element. Displays a small informational label on hover and keyboard focus using CSS-only visibility no JavaScript positioning required.
9
9
 
10
- ## Usage
10
+ > **Status: experimental** — API may change before stable release.
11
+
12
+ ## Installation
13
+
14
+ ```js
15
+ import '@boostdev/components/web-components';
16
+ ```
17
+
18
+ Or in HTML without a bundler:
11
19
 
12
20
  ```html
13
- <bds-tooltip content="Save changes" placement="top">
14
- <button type="button">Save</button>
15
- </bds-tooltip>
21
+ <script type="module">
22
+ import '@boostdev/components/web-components';
23
+ </script>
16
24
  ```
17
25
 
18
- ## Attributes
26
+ ## When to use
19
27
 
20
- | Attribute | Type | Default | Description |
21
- |-------------|-----------------------------------------|----------|----------------------------------|
22
- | `content` | `string` | `""` | Tooltip text |
23
- | `placement` | `"top" \| "bottom" \| "left" \| "right"` | `"top"` | Preferred tooltip placement |
28
+ - Labelling icon-only buttons that have no visible text
29
+ - Providing supplementary information for a UI element without cluttering the layout
30
+ - Short, non-interactive hints (a single sentence or less)
24
31
 
25
- ## Slots
32
+ ## When not to use
26
33
 
27
- | Slot | Description |
28
- |-----------|------------------------------------|
29
- | (default) | The trigger element for the tooltip |
34
+ - For interactive content (links, buttons inside the tooltip) — use `<bds-popover>` instead
35
+ - For error messages or validation feedback — use an inline message
36
+ - For content longer than a short phrase use `<bds-popover>` or a disclosure pattern
30
37
 
31
- ## Stories
38
+ ## Examples
32
39
 
33
- <Canvas of={Stories.Top} />
40
+ ### Default
41
+ <Canvas of={Stories.Default} />
42
+
43
+ ### On a button
44
+ <Canvas of={Stories.OnButton} />
45
+
46
+ ### Bottom placement
47
+ <Canvas of={Stories.Bottom} />
48
+
49
+ ### Left placement
50
+ <Canvas of={Stories.Left} />
51
+
52
+ ### Right placement
53
+ <Canvas of={Stories.Right} />
54
+
55
+ ### All placements
34
56
  <Canvas of={Stories.AllPlacements} />
35
57
 
36
- ## Controls
58
+ ## Props
37
59
 
38
60
  <ArgTypes of={Stories} />
61
+
62
+ ## Attributes
63
+
64
+ | Attribute | Type | Default | Description |
65
+ |-----------|------|---------|-------------|
66
+ | `content` | string | `""` | Text to display inside the tooltip |
67
+ | `placement` | `"top" \| "bottom" \| "left" \| "right"` | `"top"` | Where the tooltip appears relative to the trigger |
68
+
69
+ ## Slots
70
+
71
+ | Slot | Description |
72
+ |------|-------------|
73
+ | `(default)` | The trigger element (button, icon, etc.) that the tooltip is anchored to |
74
+
75
+ ## CSS Custom Properties
76
+
77
+ | Property | Description |
78
+ |----------|-------------|
79
+ | `--tooltip_bg` | Tooltip background colour (default: `var(--bds-color_on-bg)`) |
80
+ | `--tooltip_text` | Tooltip text colour (default: `var(--bds-color_bg)`) |
81
+
82
+ ## Usage in plain HTML
83
+
84
+ ```html
85
+ <bds-tooltip content="Save your changes" placement="top">
86
+ <button type="button">Save</button>
87
+ </bds-tooltip>
88
+
89
+ <!-- Icon-only button with tooltip label -->
90
+ <bds-tooltip content="Delete item" placement="bottom">
91
+ <button type="button" aria-label="Delete item">
92
+ <!-- icon SVG -->
93
+ </button>
94
+ </bds-tooltip>
95
+ ```
96
+
97
+ ## Accessibility
98
+
99
+ - The tooltip panel has `role="tooltip"`
100
+ - Visibility is controlled by `:hover` and `:focus-within` — no JavaScript required
101
+ - The tooltip is rendered adjacent to the trigger inside the same shadow root, so screen readers can read it via proximity
102
+ - Note: `aria-describedby` cannot cross shadow DOM boundaries; if your project requires explicit ARIA association, use `<bds-popover>` instead
103
+ - `pointer-events: none` on the tooltip prevents it from interfering with mouse events
104
+ - Fade transition respects `prefers-reduced-motion`
@@ -1,11 +1,12 @@
1
1
  import React from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
  import type { TooltipPlacement } from './bds-tooltip';
4
- import '../index';
4
+ import '../index'; // auto-registers all custom elements
5
5
 
6
+ // Thin React wrapper so Storybook controls work with the custom element's props
6
7
  function BdsTooltip({
7
- content,
8
- placement,
8
+ content = 'Tooltip text',
9
+ placement = 'top',
9
10
  children,
10
11
  }: {
11
12
  content?: string;
@@ -25,6 +26,7 @@ const meta = {
25
26
  tags: ['!stable', 'experimental'],
26
27
  parameters: { layout: 'centered' },
27
28
  argTypes: {
29
+ content: { control: 'text' },
28
30
  placement: { control: 'select', options: ['top', 'bottom', 'left', 'right'] },
29
31
  },
30
32
  } satisfies Meta<typeof BdsTooltip>;
@@ -32,37 +34,54 @@ const meta = {
32
34
  export default meta;
33
35
  type Story = StoryObj<typeof meta>;
34
36
 
35
- export const Top: Story = { args: { content: 'Tooltip on top', placement: 'top' } };
36
- export const Bottom: Story = { args: { content: 'Tooltip on bottom', placement: 'bottom' } };
37
- export const Left: Story = { args: { content: 'Tooltip on left', placement: 'left' } };
38
- export const Right: Story = { args: { content: 'Tooltip on right', placement: 'right' } };
37
+ export const Default: Story = {
38
+ args: { content: 'Tooltip text', placement: 'top' },
39
+ };
39
40
 
40
- export const AllPlacements: Story = {
41
- render: () => (
42
- <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, auto)', gap: '64px', padding: '48px' }}>
43
- {(['top', 'bottom', 'left', 'right'] as const).map(p => (
44
- <div key={p} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
45
- <BdsTooltip content={`Placed ${p}`} placement={p}>
46
- <button type="button">{p}</button>
47
- </BdsTooltip>
48
- </div>
49
- ))}
50
- </div>
51
- ),
41
+ export const OnButton: Story = {
42
+ args: { content: 'Save your changes', placement: 'top' },
43
+ render: (args) =>
44
+ React.createElement(
45
+ 'bds-tooltip',
46
+ { content: args.content, placement: args.placement },
47
+ React.createElement(
48
+ 'button',
49
+ { type: 'button', style: { padding: '0.5rem 1rem', cursor: 'pointer' } },
50
+ 'Save',
51
+ ),
52
+ ),
53
+ };
54
+
55
+ export const Bottom: Story = {
56
+ args: { content: 'Appears below', placement: 'bottom' },
57
+ };
58
+
59
+ export const Left: Story = {
60
+ args: { content: 'Appears to the left', placement: 'left' },
52
61
  };
53
62
 
54
- export const AllVariants: Story = {
63
+ export const Right: Story = {
64
+ args: { content: 'Appears to the right', placement: 'right' },
65
+ };
66
+
67
+ export const AllPlacements: Story = {
55
68
  render: () => (
56
- <div style={{ display: 'flex', gap: '32px', flexWrap: 'wrap', padding: '32px' }}>
57
- <BdsTooltip content="A helpful tip" placement="top">
58
- <button type="button">Info button</button>
59
- </BdsTooltip>
60
- <BdsTooltip content="Delete this item" placement="bottom">
61
- <button type="button">Delete</button>
62
- </BdsTooltip>
63
- <BdsTooltip content="Opens in new tab" placement="right">
64
- <button type="button">External link</button>
65
- </BdsTooltip>
69
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, auto)', gap: '4rem', padding: '3rem' }}>
70
+ {(['top', 'bottom', 'left', 'right'] as const).map(placement =>
71
+ React.createElement(
72
+ 'div',
73
+ { key: placement, style: { display: 'flex', justifyContent: 'center', alignItems: 'center' } },
74
+ React.createElement(
75
+ 'bds-tooltip',
76
+ { content: `Placement: ${placement}`, placement },
77
+ React.createElement(
78
+ 'button',
79
+ { type: 'button', style: { padding: '0.5rem 1rem', cursor: 'pointer' } },
80
+ placement,
81
+ ),
82
+ ),
83
+ ),
84
+ )}
66
85
  </div>
67
86
  ),
68
87
  };
@@ -91,6 +91,7 @@ export class BdsCollapsible extends LitElement {
91
91
 
92
92
  .content {
93
93
  padding: var(--bds-space_m);
94
+ background-color: var(--collapsible_content-bg, var(--bds-color_bg));
94
95
  color: var(--collapsible_on-color, var(--bds-color_on-bg));
95
96
  font-size: var(--bds-font_size--body);
96
97
  line-height: var(--bds-font_line-height--body);