@boostdev/design-system-components 1.2.1 → 1.2.3

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 (74) hide show
  1. package/AGENTS.md +26 -0
  2. package/README.md +36 -19
  3. package/dist/client.cjs +141 -120
  4. package/dist/client.css +506 -503
  5. package/dist/client.d.cts +71 -96
  6. package/dist/client.d.ts +71 -96
  7. package/dist/client.js +141 -120
  8. package/dist/index.cjs +141 -120
  9. package/dist/index.css +506 -503
  10. package/dist/index.d.cts +71 -96
  11. package/dist/index.d.ts +71 -96
  12. package/dist/index.js +141 -120
  13. package/package.json +3 -3
  14. package/src/components/interaction/Button/Button.module.css +1 -4
  15. package/src/components/interaction/Command/Command.tsx +4 -3
  16. package/src/components/interaction/Dialog/Dialog.module.css +1 -1
  17. package/src/components/interaction/Dialog/Dialog.tsx +4 -5
  18. package/src/components/interaction/Drawer/Drawer.module.css +1 -1
  19. package/src/components/interaction/Drawer/Drawer.spec.tsx +3 -3
  20. package/src/components/interaction/Drawer/Drawer.tsx +4 -7
  21. package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +4 -0
  22. package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +4 -4
  23. package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +4 -2
  24. package/src/components/interaction/Popover/Popover.tsx +4 -3
  25. package/src/components/interaction/Rating/Rating.tsx +4 -2
  26. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +4 -6
  27. package/src/components/interaction/form/Combobox/Combobox.module.css +1 -1
  28. package/src/components/interaction/form/Combobox/Combobox.tsx +4 -2
  29. package/src/components/interaction/form/FileInput/FileInput.tsx +4 -3
  30. package/src/components/interaction/form/FormInput/FormInput.module.css +1 -1
  31. package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -1
  32. package/src/components/interaction/form/NumberInput/NumberInput.tsx +4 -3
  33. package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +4 -6
  34. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +5 -5
  35. package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +4 -6
  36. package/src/components/interaction/form/Select/Select.module.css +1 -1
  37. package/src/components/interaction/form/Slider/Slider.module.css +4 -4
  38. package/src/components/interaction/form/Switch/Switch.module.css +3 -3
  39. package/src/components/interaction/form/atoms/InputContainer.tsx +2 -2
  40. package/src/components/layout/ButtonGroup/ButtonGroup.tsx +4 -6
  41. package/src/components/layout/Card/Card.tsx +4 -10
  42. package/src/components/layout/IconWrapper/IconWrapper.module.css +1 -1
  43. package/src/components/layout/IconWrapper/IconWrapper.tsx +4 -5
  44. package/src/components/layout/SectionHeader/SectionHeader.tsx +5 -4
  45. package/src/components/ui/Accordion/Accordion.tsx +4 -3
  46. package/src/components/ui/Alert/Alert.module.css +1 -1
  47. package/src/components/ui/Alert/Alert.tsx +4 -3
  48. package/src/components/ui/Avatar/Avatar.module.css +1 -1
  49. package/src/components/ui/Avatar/Avatar.tsx +5 -3
  50. package/src/components/ui/Badge/Badge.module.css +1 -1
  51. package/src/components/ui/Badge/Badge.tsx +4 -5
  52. package/src/components/ui/Breadcrumb/Breadcrumb.tsx +4 -3
  53. package/src/components/ui/Calendar/Calendar.module.css +2 -2
  54. package/src/components/ui/Calendar/Calendar.tsx +4 -4
  55. package/src/components/ui/Carousel/Carousel.module.css +1 -1
  56. package/src/components/ui/Carousel/Carousel.tsx +4 -4
  57. package/src/components/ui/DescriptionList/DescriptionList.tsx +4 -4
  58. package/src/components/ui/Loading/Loading.module.css +1 -1
  59. package/src/components/ui/Loading/Loading.tsx +4 -3
  60. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +1 -1
  61. package/src/components/ui/NotificationBanner/NotificationBanner.tsx +4 -2
  62. package/src/components/ui/Pagination/Pagination.module.css +5 -1
  63. package/src/components/ui/Pagination/Pagination.tsx +4 -2
  64. package/src/components/ui/Progress/Progress.module.css +2 -2
  65. package/src/components/ui/Progress/Progress.tsx +4 -2
  66. package/src/components/ui/ProgressCircle/ProgressCircle.tsx +4 -1
  67. package/src/components/ui/Separator/Separator.tsx +5 -3
  68. package/src/components/ui/Skeleton/Skeleton.tsx +4 -3
  69. package/src/components/ui/SkipLink/SkipLink.tsx +4 -5
  70. package/src/components/ui/Tabs/Tabs.tsx +4 -4
  71. package/src/components/ui/Tooltip/Tooltip.module.css +1 -1
  72. package/src/components/ui/Tooltip/Tooltip.tsx +4 -2
  73. package/src/components/ui/Typography/Typography.tsx +4 -5
  74. package/src/stories/DesignSystem/Grid.mdx +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boostdev/design-system-components",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "BoostDev React component library: accessible, token-driven components built on @boostdev/design-system-foundation",
5
5
  "keywords": [
6
6
  "React",
@@ -271,7 +271,7 @@
271
271
  "typecheck": "tsc --noEmit",
272
272
  "lint": "eslint src",
273
273
  "lint:css": "stylelint \"src/**/*.css\"",
274
- "storybook": "storybook dev -p 6006 --no-manager-cache",
274
+ "storybook": "storybook dev -p 6006",
275
275
  "build-storybook": "storybook build",
276
276
  "test": "vitest run",
277
277
  "test:watch": "vitest",
@@ -279,7 +279,7 @@
279
279
  "changelog:unreleased": "pnpm exec git-cliff --unreleased"
280
280
  },
281
281
  "devDependencies": {
282
- "@boostdev/design-system-foundation": "^1.0.4",
282
+ "@boostdev/design-system-foundation": "^1.0.5",
283
283
  "@modelcontextprotocol/sdk": "^1.27.0",
284
284
  "@storybook/addon-docs": "8.6.14",
285
285
  "@storybook/addon-essentials": "^8.6.14",
@@ -36,7 +36,7 @@
36
36
  padding-inline: var(--bds-space_l);
37
37
  display: inline-flex;
38
38
  gap: var(--bds-space_xs);
39
- border-radius: var(--button_border_radius--s, var(--bds-border_radius--s));
39
+ border-radius: var(--button_radius, var(--bds-border_radius--full));
40
40
  align-items: center;
41
41
  cursor: pointer;
42
42
  scroll-padding-block-end: var(--bds-space_l);
@@ -76,7 +76,6 @@
76
76
  .button.--size_small {
77
77
  --button_height: 2.25em;
78
78
  --button_font-size: var(--bds-font_size--body--s);
79
- --button_border_radius--s: var(--bds-border_radius--xs);
80
79
 
81
80
  padding-inline: var(--bds-space_m);
82
81
  }
@@ -89,7 +88,6 @@
89
88
 
90
89
  .button.--size_large {
91
90
  --button_font-size: var(--bds-font_size--heading-3);
92
- --button_border_radius--s: var(--bds-border_radius--m);
93
91
 
94
92
  padding-inline: var(--bds-space_xl);
95
93
  }
@@ -137,7 +135,6 @@
137
135
  .button:focus-visible {
138
136
  outline: var(--bds-outline_default);
139
137
  outline-offset: var(--bds-outline_offset);
140
- border-radius: var(--bds-border_radius--s);
141
138
  }
142
139
 
143
140
  /* Default hover: outline appears, bg becomes page surface */
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useRef, useId, KeyboardEvent, useMemo } from 'react';
1
+ import { HTMLAttributes, useState, useEffect, useRef, useId, KeyboardEvent, useMemo } from 'react';
2
2
  import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
3
3
 
4
4
  // Install once at module load — no-op if native support is present.
@@ -17,8 +17,7 @@ export interface CommandItem {
17
17
  onSelect: () => void;
18
18
  }
19
19
 
20
- interface CommandProps extends WithClassName {
21
- id?: string;
20
+ interface CommandProps extends WithClassName, HTMLAttributes<HTMLDialogElement> {
22
21
  isOpen: boolean;
23
22
  onOpen?: () => void;
24
23
  onClose: () => void;
@@ -34,6 +33,7 @@ export function Command({
34
33
  items,
35
34
  placeholder = 'Search commands…',
36
35
  className,
36
+ ...rest
37
37
  }: Readonly<CommandProps>) {
38
38
  const [query, setQuery] = useState('');
39
39
  const [activeIndex, setActiveIndex] = useState(0);
@@ -139,6 +139,7 @@ export function Command({
139
139
 
140
140
  return (
141
141
  <dialog
142
+ {...rest}
142
143
  ref={dialogRef}
143
144
  id={dialogId}
144
145
  className={cn(css.dialog, className)}
@@ -33,7 +33,7 @@
33
33
  display: flex;
34
34
  align-items: center;
35
35
  justify-content: center;
36
- border-radius: 50%;
36
+ border-radius: var(--dialog_close-radius, var(--bds-border_radius--circle));
37
37
  background-color: var(--bds-color_bg);
38
38
  color: currentcolor;
39
39
  border: var(--dialog_close-border, none);
@@ -1,4 +1,4 @@
1
- import { ReactNode, useEffect, useId, useRef } from 'react';
1
+ import { HTMLAttributes, useEffect, useId, useRef } from 'react';
2
2
  import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
3
3
 
4
4
  // Install once at module load — no-op if native support is present.
@@ -9,15 +9,13 @@ import css from './Dialog.module.css';
9
9
  import { cn } from '@boostdev/design-system-foundation';
10
10
  import type { WithClassName } from '../../../types';
11
11
 
12
- interface DialogProps extends WithClassName {
13
- children: ReactNode;
14
- id?: string;
12
+ interface DialogProps extends WithClassName, HTMLAttributes<HTMLDialogElement> {
15
13
  isOpen?: boolean;
16
14
  onOpen?: () => void;
17
15
  onClose?: () => void;
18
16
  }
19
17
 
20
- export function Dialog({ children, id: idProp, isOpen = false, className, onOpen, onClose }: DialogProps) {
18
+ export function Dialog({ children, id: idProp, isOpen = false, className, onOpen, onClose, ...rest }: DialogProps) {
21
19
  const generatedId = useId();
22
20
  const id = idProp ?? generatedId;
23
21
 
@@ -92,6 +90,7 @@ export function Dialog({ children, id: idProp, isOpen = false, className, onOpen
92
90
 
93
91
  return (
94
92
  <dialog
93
+ {...rest}
95
94
  ref={dialogRef}
96
95
  id={id}
97
96
  className={cn(className, css.dialog)}
@@ -78,7 +78,7 @@
78
78
  justify-content: center;
79
79
  inline-size: 2rem;
80
80
  block-size: 2rem;
81
- border-radius: 50%;
81
+ border-radius: var(--drawer_close-radius, var(--bds-border_radius--circle));
82
82
  cursor: pointer;
83
83
  transition: var(--bds-animation_transition);
84
84
  }
@@ -85,13 +85,13 @@ describe('Drawer', () => {
85
85
  expect(dialog).toHaveAttribute('aria-modal', 'true');
86
86
  });
87
87
 
88
- it('renders aria-label on the dialog when ariaLabel prop is provided', () => {
89
- render(<Drawer isOpen title="Settings" ariaLabel="Settings panel" onClose={() => {}}>Content</Drawer>);
88
+ it('renders aria-label on the dialog when aria-label prop is provided', () => {
89
+ render(<Drawer isOpen title="Settings" aria-label="Settings panel" onClose={() => {}}>Content</Drawer>);
90
90
  const dialog = document.querySelector('dialog');
91
91
  expect(dialog).toHaveAttribute('aria-label', 'Settings panel');
92
92
  });
93
93
 
94
- it('does not render aria-label when ariaLabel prop is not provided', () => {
94
+ it('does not render aria-label when aria-label prop is not provided', () => {
95
95
  render(<Drawer isOpen title="Settings" onClose={() => {}}>Content</Drawer>);
96
96
  const dialog = document.querySelector('dialog');
97
97
  expect(dialog).not.toHaveAttribute('aria-label');
@@ -1,4 +1,4 @@
1
- import { ReactNode, useEffect, useId, useRef } from 'react';
1
+ import { HTMLAttributes, ReactNode, useEffect, useId, useRef } from 'react';
2
2
  import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
3
3
 
4
4
  // Install once at module load — no-op if native support is present.
@@ -8,15 +8,12 @@ import css from './Drawer.module.css';
8
8
  import { cn } from '@boostdev/design-system-foundation';
9
9
  import type { WithClassName } from '../../../types';
10
10
 
11
- interface DrawerProps extends WithClassName {
12
- id?: string;
11
+ interface DrawerProps extends WithClassName, Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
13
12
  isOpen: boolean;
14
13
  onOpen?: () => void;
15
14
  onClose: () => void;
16
15
  title: ReactNode;
17
- children: ReactNode;
18
16
  side?: 'left' | 'right';
19
- ariaLabel?: string;
20
17
  }
21
18
 
22
19
  export function Drawer({
@@ -27,8 +24,8 @@ export function Drawer({
27
24
  title,
28
25
  children,
29
26
  side = 'right',
30
- ariaLabel,
31
27
  className,
28
+ ...rest
32
29
  }: Readonly<DrawerProps>) {
33
30
  const generatedId = useId();
34
31
  const id = idProp ?? generatedId;
@@ -92,10 +89,10 @@ export function Drawer({
92
89
 
93
90
  return (
94
91
  <dialog
92
+ {...rest}
95
93
  ref={dialogRef}
96
94
  id={id}
97
95
  className={cn(css.drawer, css[`--side_${side}`], className)}
98
- aria-label={ariaLabel}
99
96
  aria-modal="true"
100
97
  onClick={handleClick}
101
98
  onCancel={handleCancel}
@@ -22,6 +22,10 @@
22
22
  margin: 0;
23
23
  }
24
24
 
25
+ .menu li {
26
+ list-style: none;
27
+ }
28
+
25
29
  .--placement_bottom-start { inset-inline-start: 0; }
26
30
  .--placement_bottom-end { inset-inline-end: 0; }
27
31
 
@@ -26,14 +26,14 @@ export const Default: Story = {
26
26
  ],
27
27
  },
28
28
  render: args => (
29
- <DropdownMenu {...args} trigger={<Button variant="ghost">Actions ▾</Button>} />
29
+ <DropdownMenu {...args} trigger={<Button variant="outline">Actions ▾</Button>} />
30
30
  ),
31
31
  };
32
32
 
33
33
  export const WithDisabledItem: Story = {
34
34
  render: () => (
35
35
  <DropdownMenu
36
- trigger={<Button variant="ghost">Options ▾</Button>}
36
+ trigger={<Button variant="outline">Options ▾</Button>}
37
37
  items={[
38
38
  { id: 'view', label: 'View' },
39
39
  { id: 'edit', label: 'Edit' },
@@ -46,7 +46,7 @@ export const WithDisabledItem: Story = {
46
46
  export const WithSeparator: Story = {
47
47
  render: () => (
48
48
  <DropdownMenu
49
- trigger={<Button variant="ghost">File ▾</Button>}
49
+ trigger={<Button variant="outline">File ▾</Button>}
50
50
  items={[
51
51
  { id: 'new', label: 'New' },
52
52
  { id: 'open', label: 'Open' },
@@ -60,7 +60,7 @@ export const WithSeparator: Story = {
60
60
  export const AlignedEnd: Story = {
61
61
  render: () => (
62
62
  <DropdownMenu
63
- trigger={<Button variant="ghost">⋮</Button>}
63
+ trigger={<Button variant="outline">⋮</Button>}
64
64
  items={[{ id: 'copy', label: 'Copy link' }, { id: 'report', label: 'Report' }]}
65
65
  placement="bottom-end"
66
66
  />
@@ -1,4 +1,5 @@
1
1
  import {
2
+ HTMLAttributes,
2
3
  KeyboardEvent,
3
4
  ReactElement,
4
5
  cloneElement,
@@ -21,7 +22,7 @@ export interface DropdownMenuItem {
21
22
  separator?: boolean;
22
23
  }
23
24
 
24
- interface DropdownMenuProps extends WithClassName {
25
+ interface DropdownMenuProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
25
26
  trigger: ReactElement;
26
27
  items: DropdownMenuItem[];
27
28
  placement?: 'bottom-start' | 'bottom-end';
@@ -32,6 +33,7 @@ export function DropdownMenu({
32
33
  items,
33
34
  placement = 'bottom-start',
34
35
  className,
36
+ ...rest
35
37
  }: Readonly<DropdownMenuProps>) {
36
38
  const [isOpen, setIsOpen] = useState(false);
37
39
  const containerRef = useRef<HTMLDivElement>(null);
@@ -105,7 +107,7 @@ export function DropdownMenu({
105
107
  : trigger;
106
108
 
107
109
  return (
108
- <div ref={containerRef} className={cn(css.wrapper, className)}>
110
+ <div {...rest} ref={containerRef} className={cn(css.wrapper, className)}>
109
111
  {triggerEl}
110
112
  {isOpen && (
111
113
  <ul
@@ -1,4 +1,5 @@
1
1
  import {
2
+ HTMLAttributes,
2
3
  ReactElement,
3
4
  ReactNode,
4
5
  cloneElement,
@@ -25,11 +26,10 @@ if (
25
26
  import('@oddbird/css-anchor-positioning').catch(() => {});
26
27
  }
27
28
 
28
- interface PopoverProps extends WithClassName {
29
+ interface PopoverProps extends WithClassName, Omit<HTMLAttributes<HTMLSpanElement>, 'content'> {
29
30
  children: ReactElement;
30
31
  content: ReactNode;
31
32
  placement?: 'top' | 'bottom' | 'left' | 'right';
32
- 'aria-label'?: string;
33
33
  }
34
34
 
35
35
  export function Popover({
@@ -38,6 +38,7 @@ export function Popover({
38
38
  placement = 'bottom',
39
39
  className,
40
40
  'aria-label': ariaLabel,
41
+ ...rest
41
42
  }: Readonly<PopoverProps>) {
42
43
  const [isOpen, setIsOpen] = useState(false);
43
44
  const containerRef = useRef<HTMLSpanElement>(null);
@@ -102,7 +103,7 @@ export function Popover({
102
103
  : children;
103
104
 
104
105
  return (
105
- <span ref={containerRef} className={cn(css.wrapper, className)}>
106
+ <span {...rest} ref={containerRef} className={cn(css.wrapper, className)}>
106
107
  {trigger}
107
108
  <div
108
109
  ref={panelRef}
@@ -1,15 +1,17 @@
1
+ import { HTMLAttributes } from 'react';
1
2
  import css from './Rating.module.css';
2
3
  import { cn } from '@boostdev/design-system-foundation';
3
4
  import type { WithClassName } from '../../../types';
4
5
 
5
- interface RatingProps extends WithClassName {
6
+ interface RatingProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
6
7
  value: number;
7
8
  max?: number;
8
9
  }
9
10
 
10
- export function Rating({ value, max = 5, className }: RatingProps) {
11
+ export function Rating({ value, max = 5, className, ...rest }: RatingProps) {
11
12
  return (
12
13
  <div
14
+ {...rest}
13
15
  className={cn(css.rating, className)}
14
16
  role="img"
15
17
  aria-label={`${value} out of ${max} stars`}
@@ -1,16 +1,14 @@
1
- import { ReactNode, useId } from 'react';
1
+ import { FieldsetHTMLAttributes, useId } from 'react';
2
2
  import css from './CheckboxGroup.module.css';
3
3
  import { Message } from '../atoms/Message';
4
4
  import { cn } from '@boostdev/design-system-foundation';
5
5
  import type { WithClassName } from '../../../../types';
6
6
 
7
- export interface CheckboxGroupProps extends WithClassName {
7
+ export interface CheckboxGroupProps extends WithClassName, FieldsetHTMLAttributes<HTMLFieldSetElement> {
8
8
  legend: string;
9
- children: ReactNode;
10
9
  error?: string;
11
10
  hint?: string;
12
11
  required?: boolean;
13
- disabled?: boolean;
14
12
  }
15
13
 
16
14
  export function CheckboxGroup({
@@ -19,8 +17,8 @@ export function CheckboxGroup({
19
17
  error,
20
18
  hint,
21
19
  required,
22
- disabled,
23
20
  className,
21
+ ...rest
24
22
  }: Readonly<CheckboxGroupProps>) {
25
23
  const id = useId();
26
24
  const hintId = id + 'hint';
@@ -29,8 +27,8 @@ export function CheckboxGroup({
29
27
 
30
28
  return (
31
29
  <fieldset
30
+ {...rest}
32
31
  className={cn(css.group, className)}
33
- disabled={disabled}
34
32
  aria-required={required || undefined}
35
33
  aria-describedby={describedBy}
36
34
  >
@@ -19,7 +19,7 @@
19
19
  padding-inline-end: var(--bds-space_xxl);
20
20
  border: var(--combobox_border, none);
21
21
  --bdc_color: currentcolor;
22
- --bdc-outline_radius: var(--bds-border_radius--xs);
22
+ --bdc-outline_radius: var(--bds-border_radius--full);
23
23
 
24
24
  border-radius: var(--combobox_radius, var(--bdc-outline_radius));
25
25
  box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--combobox_shadow, var(--bdc-outline_shadow));
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  ChangeEvent,
3
+ HTMLAttributes,
3
4
  KeyboardEvent,
4
5
  ReactNode,
5
6
  useEffect,
@@ -21,7 +22,7 @@ export interface ComboboxOption {
21
22
  disabled?: boolean;
22
23
  }
23
24
 
24
- interface ComboboxProps extends WithClassName {
25
+ interface ComboboxProps extends WithClassName, Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
25
26
  label: ReactNode;
26
27
  name: string;
27
28
  options: ComboboxOption[];
@@ -44,6 +45,7 @@ export function Combobox({
44
45
  error,
45
46
  hint,
46
47
  className,
48
+ ...rest
47
49
  }: Readonly<ComboboxProps>) {
48
50
  const id = name + useId();
49
51
  const listboxId = id + 'listbox';
@@ -122,7 +124,7 @@ export function Combobox({
122
124
  };
123
125
 
124
126
  return (
125
- <InputContainer className={cn(css.formGroup, className)}>
127
+ <InputContainer {...rest} className={cn(css.formGroup, className)}>
126
128
  <Label id={id} label={label} />
127
129
  <div ref={containerRef} className={css.inputWrapper}>
128
130
  <input
@@ -1,11 +1,11 @@
1
- import { useId, useRef, useState, DragEvent, ChangeEvent } from 'react';
1
+ import { HTMLAttributes, useId, useRef, useState, DragEvent, ChangeEvent } from 'react';
2
2
  import css from './FileInput.module.css';
3
3
  import { Message } from '../atoms/Message';
4
4
  import { InputContainer } from '../atoms/InputContainer';
5
5
  import { cn } from '@boostdev/design-system-foundation';
6
6
  import type { WithClassName } from '../../../../types';
7
7
 
8
- interface FileInputProps extends WithClassName {
8
+ interface FileInputProps extends WithClassName, Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
9
9
  label: string;
10
10
  name: string;
11
11
  accept?: string;
@@ -28,6 +28,7 @@ export function FileInput({
28
28
  hint,
29
29
  onChange,
30
30
  className,
31
+ ...rest
31
32
  }: Readonly<FileInputProps>) {
32
33
  const uid = name + useId();
33
34
  const hintId = uid + 'hint';
@@ -73,7 +74,7 @@ export function FileInput({
73
74
  const handleDragLeave = () => setIsDragging(false);
74
75
 
75
76
  return (
76
- <InputContainer className={cn(css.formGroup, className)}>
77
+ <InputContainer {...rest} className={cn(css.formGroup, className)}>
77
78
  <label
78
79
  htmlFor={uid}
79
80
  className={cn(css.dropZone, isDragging && css.isDragging, error && css.hasError, disabled && css.isDisabled)}
@@ -12,7 +12,7 @@
12
12
  padding: var(--bds-space_s);
13
13
  border: var(--input_border, none);
14
14
  --bdc_color: currentcolor;
15
- --bdc-outline_radius: var(--bds-border_radius--xs);
15
+ --bdc-outline_radius: var(--bds-border_radius--full);
16
16
 
17
17
  border-radius: var(--input_radius, var(--bdc-outline_radius));
18
18
  box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--input_shadow, var(--bdc-outline_shadow));
@@ -9,7 +9,7 @@
9
9
  align-items: stretch;
10
10
  border: var(--numberInput_border, none);
11
11
  --bdc_color: currentcolor;
12
- --bdc-outline_radius: var(--bds-border_radius--xs);
12
+ --bdc-outline_radius: var(--bds-border_radius--full);
13
13
 
14
14
  --_radius: var(--numberInput_radius, var(--bdc-outline_radius));
15
15
 
@@ -1,4 +1,4 @@
1
- import {useId, useRef, useState} from 'react';
1
+ import {HTMLAttributes, useId, useRef, useState} from 'react';
2
2
  import css from './NumberInput.module.css';
3
3
  import {Label} from '../atoms/Label';
4
4
  import {Message} from '../atoms/Message';
@@ -6,7 +6,7 @@ import {InputContainer} from '../atoms/InputContainer';
6
6
  import {cn} from '@boostdev/design-system-foundation';
7
7
  import type { WithClassName } from '../../../../types';
8
8
 
9
- interface NumberInputProps extends WithClassName {
9
+ interface NumberInputProps extends WithClassName, Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
10
10
  label: string;
11
11
  name: string;
12
12
  value?: number;
@@ -33,6 +33,7 @@ export function NumberInput({
33
33
  hint,
34
34
  onChange,
35
35
  className,
36
+ ...rest
36
37
  }: Readonly<NumberInputProps>) {
37
38
  const uid = name + useId();
38
39
  const hintId = uid + 'hint';
@@ -62,7 +63,7 @@ export function NumberInput({
62
63
  };
63
64
 
64
65
  return (
65
- <InputContainer className={cn(css.formGroup, className)}>
66
+ <InputContainer {...rest} className={cn(css.formGroup, className)}>
66
67
  <Label id={uid} label={label} />
67
68
  <div className={css.inputRow}>
68
69
  <button
@@ -1,16 +1,14 @@
1
- import { ReactNode, useId } from 'react';
1
+ import { FieldsetHTMLAttributes, useId } from 'react';
2
2
  import css from './RadioGroup.module.css';
3
3
  import { Message } from '../atoms/Message';
4
4
  import { cn } from '@boostdev/design-system-foundation';
5
5
  import type { WithClassName } from '../../../../types';
6
6
 
7
- export interface RadioGroupProps extends WithClassName {
7
+ export interface RadioGroupProps extends WithClassName, FieldsetHTMLAttributes<HTMLFieldSetElement> {
8
8
  legend: string;
9
- children: ReactNode;
10
9
  error?: string;
11
10
  hint?: string;
12
11
  required?: boolean;
13
- disabled?: boolean;
14
12
  }
15
13
 
16
14
  export function RadioGroup({
@@ -19,8 +17,8 @@ export function RadioGroup({
19
17
  error,
20
18
  hint,
21
19
  required,
22
- disabled,
23
20
  className,
21
+ ...rest
24
22
  }: Readonly<RadioGroupProps>) {
25
23
  const id = useId();
26
24
  const hintId = id + 'hint';
@@ -29,8 +27,8 @@ export function RadioGroup({
29
27
 
30
28
  return (
31
29
  <fieldset
30
+ {...rest}
32
31
  className={cn(css.group, className)}
33
- disabled={disabled}
34
32
  aria-required={required || undefined}
35
33
  aria-describedby={describedBy}
36
34
  >
@@ -6,7 +6,7 @@
6
6
  grid-auto-columns: 1fr;
7
7
  padding: var(--bds-space_xxxs);
8
8
  background-color: var(--control_track-bg, var(--bds-color_bg--subtle));
9
- border-radius: var(--control_radius, var(--bds-border_radius--m));
9
+ border-radius: var(--control_radius, var(--bds-border_radius--full));
10
10
  gap: 0;
11
11
  }
12
12
 
@@ -16,7 +16,7 @@
16
16
  inset-block: var(--bds-space_xxxs);
17
17
  inset-inline-start: var(--bds-space_xxxs);
18
18
  inline-size: calc((100% - 2 * var(--bds-space_xxxs)) / var(--control_count, 1));
19
- border-radius: var(--control_thumb-radius, var(--bds-border_radius--s));
19
+ border-radius: var(--control_thumb-radius, var(--bds-border_radius--full));
20
20
  background-color: var(--control_thumb-bg, var(--bds-color_interactive));
21
21
  box-shadow: var(--bds-shadow_s);
22
22
  pointer-events: none;
@@ -30,7 +30,7 @@
30
30
  inset-block: var(--bds-space_xxxs);
31
31
  inset-inline-start: var(--bds-space_xxxs);
32
32
  inline-size: calc((100% - 2 * var(--bds-space_xxxs)) / var(--control_count, 1));
33
- border-radius: var(--control_thumb-radius, var(--bds-border_radius--s));
33
+ border-radius: var(--control_thumb-radius, var(--bds-border_radius--full));
34
34
  pointer-events: none;
35
35
  z-index: 2;
36
36
  display: none;
@@ -46,7 +46,7 @@
46
46
  align-items: center;
47
47
  justify-content: center;
48
48
  cursor: pointer;
49
- border-radius: var(--control_thumb-radius, var(--bds-border_radius--s));
49
+ border-radius: var(--control_thumb-radius, var(--bds-border_radius--full));
50
50
  padding: var(--bds-space_xs) var(--bds-space_m);
51
51
  font-size: var(--bds-font_size--body);
52
52
  font-family: var(--bds-font_family--body);
@@ -66,7 +66,7 @@
66
66
  .item:focus-visible {
67
67
  outline: var(--bds-outline_default);
68
68
  outline-offset: calc(var(--bds-outline_offset) * -1);
69
- border-radius: var(--control_thumb-radius, var(--bds-border_radius--s));
69
+ border-radius: var(--control_thumb-radius, var(--bds-border_radius--full));
70
70
  }
71
71
 
72
72
  .item.--active {
@@ -1,10 +1,9 @@
1
- import { Children, ReactElement, ReactNode, cloneElement, isValidElement } from 'react';
1
+ import { Children, HTMLAttributes, ReactElement, cloneElement, isValidElement } from 'react';
2
2
  import css from './SegmentedControl.module.css';
3
3
  import { cn } from '@boostdev/design-system-foundation';
4
4
  import type { WithClassName } from '../../../../types';
5
5
 
6
- export interface SegmentedControlProps extends WithClassName {
7
- children: ReactNode;
6
+ export interface SegmentedControlProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
8
7
  /**
9
8
  * Zero-based index of the active item.
10
9
  * When omitted, auto-detected from the first child that has
@@ -15,7 +14,6 @@ export interface SegmentedControlProps extends WithClassName {
15
14
  size?: 'small' | 'medium' | 'large';
16
15
  /** "outline" (default) — sliding inset border; "filled" — sliding filled thumb */
17
16
  variant?: 'outline' | 'filled';
18
- 'aria-label'?: string;
19
17
  }
20
18
 
21
19
  export function SegmentedControl({
@@ -25,7 +23,7 @@ export function SegmentedControl({
25
23
  size = 'medium',
26
24
  variant = 'outline',
27
25
  className,
28
- 'aria-label': ariaLabel,
26
+ ...rest
29
27
  }: Readonly<SegmentedControlProps>) {
30
28
  const validChildren = Children.toArray(children).filter(isValidElement) as ReactElement<Record<string, unknown>>[];
31
29
 
@@ -41,8 +39,8 @@ export function SegmentedControl({
41
39
 
42
40
  return (
43
41
  <div
42
+ {...rest}
44
43
  role="group"
45
- aria-label={ariaLabel}
46
44
  className={cn(css.control, css[`--size_${size}`], css[`--variant_${variant}`], className)}
47
45
  style={{
48
46
  '--control_count': validChildren.length,
@@ -19,7 +19,7 @@
19
19
  padding-inline-end: var(--bds-space_xxl);
20
20
  border: var(--select_border, none);
21
21
  --bdc_color: currentcolor;
22
- --bdc-outline_radius: var(--bds-border_radius--xs);
22
+ --bdc-outline_radius: var(--bds-border_radius--full);
23
23
 
24
24
  border-radius: var(--select_radius, var(--bdc-outline_radius));
25
25
  box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--select_shadow, var(--bdc-outline_shadow));