@boostdev/design-system-components 1.1.0 → 1.1.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 (72) hide show
  1. package/AGENTS.md +46 -5
  2. package/dist/client.cjs +206 -113
  3. package/dist/client.css +664 -624
  4. package/dist/client.d.cts +9 -3
  5. package/dist/client.d.ts +9 -3
  6. package/dist/client.js +241 -149
  7. package/dist/index.cjs +206 -113
  8. package/dist/index.css +664 -624
  9. package/dist/index.d.cts +9 -3
  10. package/dist/index.d.ts +9 -3
  11. package/dist/index.js +241 -149
  12. package/dist/web-components.d.ts +1 -0
  13. package/dist/web-components.js +10 -0
  14. package/package.json +1 -1
  15. package/src/components/interaction/Button/Button.module.css +1 -1
  16. package/src/components/interaction/Button/Button.native.mdx +72 -0
  17. package/src/components/interaction/Command/Command.module.css +7 -7
  18. package/src/components/interaction/Command/Command.spec.tsx +37 -0
  19. package/src/components/interaction/Command/Command.tsx +37 -2
  20. package/src/components/interaction/Dialog/Dialog.module.css +10 -13
  21. package/src/components/interaction/Dialog/Dialog.spec.tsx +53 -1
  22. package/src/components/interaction/Dialog/Dialog.stories.tsx +17 -0
  23. package/src/components/interaction/Dialog/Dialog.tsx +50 -16
  24. package/src/components/interaction/Drawer/Drawer.module.css +11 -11
  25. package/src/components/interaction/Drawer/Drawer.spec.tsx +48 -1
  26. package/src/components/interaction/Drawer/Drawer.tsx +38 -4
  27. package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +6 -6
  28. package/src/components/interaction/Popover/Popover.module.css +74 -15
  29. package/src/components/interaction/Popover/Popover.spec.tsx +94 -59
  30. package/src/components/interaction/Popover/Popover.tsx +44 -60
  31. package/src/components/interaction/Toast/Toast.module.css +7 -7
  32. package/src/components/interaction/form/Checkbox/Checkbox.module.css +6 -6
  33. package/src/components/interaction/form/Checkbox/Checkbox.native.mdx +74 -0
  34. package/src/components/interaction/form/Combobox/Combobox.module.css +7 -8
  35. package/src/components/interaction/form/FileInput/FileInput.module.css +4 -4
  36. package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -1
  37. package/src/components/interaction/form/Radio/Radio.module.css +6 -6
  38. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +5 -6
  39. package/src/components/interaction/form/Select/Select.module.css +3 -3
  40. package/src/components/interaction/form/Slider/Slider.module.css +6 -6
  41. package/src/components/interaction/form/Switch/Switch.module.css +8 -8
  42. package/src/components/interaction/form/Textarea/Textarea.module.css +1 -1
  43. package/src/components/layout/IconWrapper/IconWrapper.module.css +2 -2
  44. package/src/components/ui/Accordion/Accordion.module.css +5 -5
  45. package/src/components/ui/Alert/Alert.native.mdx +62 -0
  46. package/src/components/ui/Avatar/Avatar.native.mdx +50 -0
  47. package/src/components/ui/Badge/Badge.native.mdx +54 -0
  48. package/src/components/ui/Calendar/Calendar.module.css +7 -7
  49. package/src/components/ui/Carousel/Carousel.module.css +5 -5
  50. package/src/components/ui/Collapsible/Collapsible.module.css +4 -4
  51. package/src/components/ui/Link/Link.module.css +2 -2
  52. package/src/components/ui/Loading/Loading.module.css +7 -7
  53. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +3 -3
  54. package/src/components/ui/Pagination/Pagination.module.css +6 -6
  55. package/src/components/ui/Progress/Progress.module.css +5 -5
  56. package/src/components/ui/Separator/Separator.module.css +3 -3
  57. package/src/components/ui/Skeleton/Skeleton.module.css +1 -1
  58. package/src/components/ui/Table/Table.module.css +9 -9
  59. package/src/components/ui/Tabs/Tabs.module.css +3 -3
  60. package/src/components/ui/Tooltip/Tooltip.module.css +8 -8
  61. package/src/components/ui/Typography/Typography.native.mdx +56 -0
  62. package/src/polyfill-invoker-commands.ts +68 -0
  63. package/src/react-augment.d.ts +19 -0
  64. package/src/stories/ReactNative.mdx +121 -0
  65. package/src/web-components/interaction/BdsButton.mdx +106 -0
  66. package/src/web-components/interaction/BdsPopover.mdx +120 -0
  67. package/src/web-components/interaction/BdsToastProvider.mdx +94 -0
  68. package/src/web-components/interaction/bds-popover.spec.ts +49 -1
  69. package/src/web-components/interaction/bds-popover.ts +12 -0
  70. package/src/web-components/test/helpers.ts +8 -0
  71. package/src/web-components/ui/BdsAlert.mdx +90 -0
  72. package/src/web-components/ui/BdsBadge.mdx +74 -0
@@ -3,20 +3,21 @@ import {
3
3
  ReactNode,
4
4
  cloneElement,
5
5
  isValidElement,
6
- useCallback,
7
6
  useEffect,
8
7
  useId,
9
8
  useRef,
10
9
  useState,
11
10
  } from 'react';
11
+ import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
12
+
13
+ // Install once at module load — no-op if native support is present.
14
+ installInvokerCommandsPolyfill();
15
+
12
16
  import css from './Popover.module.css';
13
17
  import { cn } from '@boostdev/design-system-foundation';
14
18
  import type { WithClassName } from '../../../types';
15
19
 
16
- const GAP = 8; // --bds-space_xs
17
-
18
20
  // Load the CSS Anchor Positioning polyfill once, only in browsers that need it.
19
- // The CSS.supports guard is defensive: older environments may not expose it at all.
20
21
  if (
21
22
  typeof window !== 'undefined' &&
22
23
  !(typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('anchor-name: --a'))
@@ -40,90 +41,73 @@ export function Popover({
40
41
  }: Readonly<PopoverProps>) {
41
42
  const [isOpen, setIsOpen] = useState(false);
42
43
  const containerRef = useRef<HTMLSpanElement>(null);
43
- const dialogRef = useRef<HTMLDialogElement>(null);
44
+ const panelRef = useRef<HTMLDivElement>(null);
44
45
  const rawId = useId();
45
46
  // CSS anchor-name must be a valid custom-ident: strip non-word characters.
46
47
  const anchorName = `--popover-${rawId.replace(/\W/g, '') || 'a'}`;
47
48
  const panelId = rawId;
48
49
 
49
- // Light-dismiss: close on outside click or Escape key.
50
+ // Sync isOpen state via the popover toggle event (fires on both native and
51
+ // polyfilled open/close, including light-dismiss and Escape).
52
+ useEffect(() => {
53
+ const panel = panelRef.current;
54
+ if (!panel) return;
55
+ const handleToggle = (e: Event) => {
56
+ setIsOpen((e as ToggleEvent).newState === 'open');
57
+ };
58
+ panel.addEventListener('toggle', handleToggle);
59
+ return () => panel.removeEventListener('toggle', handleToggle);
60
+ }, []);
61
+
62
+ // Close the popover when the anchor scrolls out of the viewport.
63
+ // Light-dismiss (outside click) and Escape are handled natively by popover="auto".
50
64
  useEffect(() => {
51
65
  if (!isOpen) return;
52
- const handleMouseDown = (e: MouseEvent) => {
53
- if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
54
- setIsOpen(false);
66
+ const handleScroll = () => {
67
+ const rect = containerRef.current?.getBoundingClientRect();
68
+ if (!rect) return;
69
+ const { innerWidth: vw, innerHeight: vh } = window;
70
+ if (rect.bottom < 0 || rect.top > vh || rect.right < 0 || rect.left > vw) {
71
+ panelRef.current?.hidePopover();
55
72
  }
56
73
  };
57
- const handleKeyDown = (e: KeyboardEvent) => {
58
- if (e.key === 'Escape') setIsOpen(false);
59
- };
60
- document.addEventListener('mousedown', handleMouseDown);
61
- document.addEventListener('keydown', handleKeyDown);
74
+ window.addEventListener('scroll', handleScroll, { capture: true, passive: true });
75
+ window.addEventListener('resize', handleScroll, { passive: true });
62
76
  return () => {
63
- document.removeEventListener('mousedown', handleMouseDown);
64
- document.removeEventListener('keydown', handleKeyDown);
77
+ window.removeEventListener('scroll', handleScroll, { capture: true });
78
+ window.removeEventListener('resize', handleScroll);
65
79
  };
66
80
  }, [isOpen]);
67
81
 
68
- // CSS anchor positioning handles placement. JS only constrains max-height/max-width
69
- // so tall content scrolls rather than overflows the viewport.
70
- const constrainPanel = useCallback(() => {
71
- const dialog = dialogRef.current;
72
- const container = containerRef.current;
73
- if (!dialog || !container) return;
74
- const rect = container.getBoundingClientRect();
75
- const vw = window.innerWidth;
76
- const vh = window.innerHeight;
77
- dialog.style.maxHeight = '';
78
- dialog.style.maxWidth = '';
79
- if (placement === 'bottom') {
80
- dialog.style.maxHeight = `${Math.max(0, vh - rect.bottom - GAP * 2)}px`;
81
- dialog.style.maxWidth = `${Math.max(0, vw - rect.left - GAP)}px`;
82
- } else if (placement === 'top') {
83
- dialog.style.maxHeight = `${Math.max(0, rect.top - GAP * 2)}px`;
84
- dialog.style.maxWidth = `${Math.max(0, vw - rect.left - GAP)}px`;
85
- } else if (placement === 'right') {
86
- dialog.style.maxHeight = `${Math.max(0, vh - rect.top - GAP)}px`;
87
- dialog.style.maxWidth = `${Math.max(0, vw - rect.right - GAP * 2)}px`;
88
- } else {
89
- dialog.style.maxHeight = `${Math.max(0, vh - rect.top - GAP)}px`;
90
- dialog.style.maxWidth = `${Math.max(0, rect.left - GAP * 2)}px`;
91
- }
92
- }, [placement]);
93
-
94
- const toggle = useCallback(() => {
95
- if (isOpen) {
96
- setIsOpen(false);
97
- } else {
98
- constrainPanel();
99
- setIsOpen(true);
100
- }
101
- }, [isOpen, constrainPanel]);
102
-
82
+ // Inject invoker command attrs and accessibility props onto the trigger.
83
+ // commandfor/command="toggle-popover" replaces the onClick toggle handler
84
+ // the browser (or polyfill) calls togglePopover() on the panel, which fires
85
+ // the toggle event above to sync state.
103
86
  const trigger = isValidElement(children)
104
87
  ? cloneElement(children as ReactElement<Record<string, unknown>>, {
105
88
  'aria-expanded': isOpen,
106
89
  'aria-controls': panelId,
107
90
  'aria-haspopup': true,
91
+ commandfor: panelId,
92
+ command: 'toggle-popover',
108
93
  style: {
109
94
  ...((children.props as Record<string, unknown>).style as object | undefined),
110
95
  anchorName,
111
96
  },
112
- onClick: (e: MouseEvent) => {
113
- toggle();
114
- const existingOnClick = (children.props as Record<string, unknown>).onClick;
115
- if (typeof existingOnClick === 'function') existingOnClick(e);
116
- },
97
+ // Forward any existing onClick on the child (for custom side-effects).
98
+ // Do NOT add our own toggle here — commandfor/command handles it to
99
+ // avoid a double-toggle when both onClick and invoker fire.
100
+ onClick: (children.props as Record<string, unknown>).onClick as (() => void) | undefined,
117
101
  })
118
102
  : children;
119
103
 
120
104
  return (
121
105
  <span ref={containerRef} className={cn(css.wrapper, className)}>
122
106
  {trigger}
123
- <dialog
124
- ref={dialogRef}
107
+ <div
108
+ ref={panelRef}
125
109
  id={panelId}
126
- open={isOpen}
110
+ popover="auto"
127
111
  data-placement={placement}
128
112
  role={ariaLabel ? 'region' : undefined}
129
113
  aria-label={ariaLabel}
@@ -131,7 +115,7 @@ export function Popover({
131
115
  style={{ positionAnchor: anchorName }}
132
116
  >
133
117
  {content}
134
- </dialog>
118
+ </div>
135
119
  </span>
136
120
  );
137
121
  }
@@ -2,8 +2,8 @@
2
2
  .toastContainer {
3
3
  position: fixed;
4
4
  inset: unset; /* override UA popover default (inset: 0) */
5
- bottom: var(--bds-space_xl);
6
- right: var(--bds-space_xl);
5
+ inset-block-end: var(--bds-space_xl);
6
+ inset-inline-end: var(--bds-space_xl);
7
7
  display: flex;
8
8
  flex-direction: column;
9
9
  gap: var(--bds-space_m);
@@ -23,7 +23,7 @@
23
23
  display: flex;
24
24
  align-items: center;
25
25
  gap: var(--bds-space_m);
26
- min-width: 300px;
26
+ min-inline-size: 300px;
27
27
  animation: slideIn var(--bds-animation_duration--fast) var(--bds-animation_easing);
28
28
  }
29
29
 
@@ -56,8 +56,8 @@
56
56
  align-items: center;
57
57
  justify-content: center;
58
58
  flex-shrink: 0;
59
- width: 1.25rem;
60
- height: 1.25rem;
59
+ inline-size: 1.25rem;
60
+ block-size: 1.25rem;
61
61
  padding: 0;
62
62
  background: none;
63
63
  border: none;
@@ -66,8 +66,8 @@
66
66
  }
67
67
 
68
68
  .closeButton svg {
69
- width: 1rem;
70
- height: 1rem;
69
+ inline-size: 1rem;
70
+ block-size: 1rem;
71
71
  }
72
72
  }
73
73
 
@@ -14,8 +14,8 @@
14
14
 
15
15
  .checkbox {
16
16
  margin-block-start: 0.25em;
17
- width: var(--inputSize);
18
- height: var(--inputSize);
17
+ inline-size: var(--inputSize);
18
+ block-size: var(--inputSize);
19
19
  color: var(--checkbox_color, var(--bds-color_on-bg));
20
20
  border: var(--checkbox_border, none);
21
21
  --bdc_color: currentcolor;
@@ -40,10 +40,10 @@
40
40
  .checkbox:checked::after {
41
41
  content: '';
42
42
  position: absolute;
43
- left: 0.6em;
44
- top: 0.2em;
45
- width: 0.5em;
46
- height: 1em;
43
+ inset-inline-start: 0.6em;
44
+ inset-block-start: 0.2em;
45
+ inline-size: 0.5em;
46
+ block-size: 1em;
47
47
  border: solid var(--checkbox_color-on-active, var(--bdc-color_on-active));
48
48
  border-width: 0 2px 2px 0;
49
49
  transform: rotate(45deg);
@@ -0,0 +1,74 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Checkbox.native.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Checkbox (React Native)
7
+
8
+ React Native checkbox component with label, optional hint, and error state. Fully controlled — manage checked state via `checked` + `onChange`.
9
+
10
+ > **Status: alpha** — API may change before stable release.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { Checkbox } from '@boostdev/components';
16
+ import { useState } from 'react';
17
+
18
+ function Example() {
19
+ const [checked, setChecked] = useState(false);
20
+ return (
21
+ <Checkbox
22
+ name="terms"
23
+ label="Accept terms and conditions"
24
+ checked={checked}
25
+ onChange={setChecked}
26
+ />
27
+ );
28
+ }
29
+ ```
30
+
31
+ ## Examples
32
+
33
+ ### Interactive
34
+ <Canvas of={Stories.Interactive} />
35
+
36
+ ### Unchecked
37
+ <Canvas of={Stories.Unchecked} />
38
+
39
+ ### Checked
40
+ <Canvas of={Stories.Checked} />
41
+
42
+ ### With hint
43
+ <Canvas of={Stories.WithHint} />
44
+
45
+ ### With error
46
+ <Canvas of={Stories.WithError} />
47
+
48
+ ### Disabled
49
+ <Canvas of={Stories.Disabled} />
50
+
51
+ ### Disabled + checked
52
+ <Canvas of={Stories.DisabledChecked} />
53
+
54
+ ## Props
55
+
56
+ <ArgTypes of={Stories} />
57
+
58
+ ## Props reference
59
+
60
+ | Prop | Type | Default | Description |
61
+ |------|------|---------|-------------|
62
+ | `name` | string | — | Field name (used for accessibility grouping) |
63
+ | `label` | string | — | Visible label text |
64
+ | `checked` | boolean | `false` | Controlled checked state |
65
+ | `onChange` | `(checked: boolean) => void` | — | Called with the new value on press |
66
+ | `disabled` | boolean | `false` | Prevents interaction, reduces opacity |
67
+ | `hint` | string | — | Helper text below the label |
68
+ | `error` | string | — | Error message; shown instead of hint when set |
69
+
70
+ ## Accessibility
71
+
72
+ - The label, hint, and error are announced together by screen readers
73
+ - `disabled` communicates the disabled state via `accessibilityState`
74
+ - Group related checkboxes using a wrapping `View` with `accessibilityRole="radiogroup"` or a labelled `fieldset` equivalent
@@ -12,7 +12,7 @@
12
12
 
13
13
  .input {
14
14
  appearance: none;
15
- width: 100%;
15
+ inline-size: 100%;
16
16
  font-family: var(--bds-font_family--body);
17
17
  font-size: var(--bds-font_size--body);
18
18
  padding: var(--bds-space_s);
@@ -59,18 +59,17 @@
59
59
  }
60
60
 
61
61
  .chevron svg {
62
- width: 1rem;
63
- height: 1rem;
62
+ inline-size: 1rem;
63
+ block-size: 1rem;
64
64
  }
65
65
 
66
66
  .listbox {
67
67
  position: absolute;
68
- top: calc(100% + var(--bds-space_xxs));
69
- left: 0;
70
- right: 0;
68
+ inset-block-start: calc(100% + var(--bds-space_xxs));
69
+ inset-inline: 0;
71
70
  z-index: var(--bds-z-index_dropdown);
72
- max-height: 15rem;
73
- overflow-y: auto;
71
+ max-block-size: 15rem;
72
+ overflow-block: auto;
74
73
  list-style: none;
75
74
  margin: 0;
76
75
  padding: var(--bds-space_xxs) 0;
@@ -48,8 +48,8 @@
48
48
  }
49
49
 
50
50
  .icon {
51
- width: 2rem;
52
- height: 2rem;
51
+ inline-size: 2rem;
52
+ block-size: 2rem;
53
53
  flex-shrink: 0;
54
54
  }
55
55
 
@@ -70,8 +70,8 @@
70
70
 
71
71
  .hiddenInput {
72
72
  position: absolute;
73
- width: 1px;
74
- height: 1px;
73
+ inline-size: 1px;
74
+ block-size: 1px;
75
75
  overflow: hidden;
76
76
  clip: rect(0, 0, 0, 0);
77
77
  white-space: nowrap;
@@ -38,7 +38,7 @@
38
38
  padding: var(--bds-space_s);
39
39
  background: transparent;
40
40
  color: var(--numberInput_color, var(--bds-color_on-bg));
41
- min-width: 0;
41
+ min-inline-size: 0;
42
42
  appearance: textfield;
43
43
  }
44
44
 
@@ -29,8 +29,8 @@
29
29
  .radio {
30
30
  flex-shrink: 0;
31
31
  margin-block-start: 0.2em;
32
- width: var(--inputSize);
33
- height: var(--inputSize);
32
+ inline-size: var(--inputSize);
33
+ block-size: var(--inputSize);
34
34
  color: var(--radio_color, var(--bds-color_on-bg));
35
35
  border: var(--radio_border, none);
36
36
  --bdc_color: currentcolor;
@@ -55,11 +55,11 @@
55
55
  .radio:checked::after {
56
56
  content: '';
57
57
  position: absolute;
58
- left: 50%;
59
- top: 50%;
58
+ inset-inline-start: 50%;
59
+ inset-block-start: 50%;
60
60
  transform: translate(-50%, -50%);
61
- width: 0.4em;
62
- height: 0.4em;
61
+ inline-size: 0.4em;
62
+ block-size: 0.4em;
63
63
  border-radius: 50%;
64
64
  background-color: var(--radio_color-on-active, var(--bdc-color_on-active));
65
65
  }
@@ -12,10 +12,9 @@
12
12
 
13
13
  .thumb {
14
14
  position: absolute;
15
- top: var(--bds-space_xxxs);
16
- bottom: var(--bds-space_xxxs);
17
- left: var(--bds-space_xxxs);
18
- width: calc((100% - 2 * var(--bds-space_xxxs)) / var(--control_count, 1));
15
+ inset-block: var(--bds-space_xxxs);
16
+ inset-inline-start: var(--bds-space_xxxs);
17
+ inline-size: calc((100% - 2 * var(--bds-space_xxxs)) / var(--control_count, 1));
19
18
  border-radius: var(--bds-border_radius--s);
20
19
  background-color: var(--control_thumb-bg, var(--bds-color_bg));
21
20
  box-shadow: var(--bds-shadow_s);
@@ -43,8 +42,8 @@
43
42
  .radio {
44
43
  position: absolute;
45
44
  opacity: 0;
46
- width: 0;
47
- height: 0;
45
+ inline-size: 0;
46
+ block-size: 0;
48
47
  margin: 0;
49
48
  }
50
49
 
@@ -12,7 +12,7 @@
12
12
 
13
13
  .select {
14
14
  appearance: none;
15
- width: 100%;
15
+ inline-size: 100%;
16
16
  font-family: var(--bds-font_family--body);
17
17
  font-size: var(--bds-font_size--body);
18
18
  padding: var(--bds-space_s);
@@ -60,7 +60,7 @@
60
60
  }
61
61
 
62
62
  .chevron svg {
63
- width: 1rem;
64
- height: 1rem;
63
+ inline-size: 1rem;
64
+ block-size: 1rem;
65
65
  }
66
66
  }
@@ -24,8 +24,8 @@
24
24
 
25
25
  color: var(--slider_color, var(--bds-color_on-bg));
26
26
  appearance: none;
27
- width: 100%;
28
- height: var(--slider_track-height);
27
+ inline-size: 100%;
28
+ block-size: var(--slider_track-height);
29
29
  border-radius: 999px;
30
30
  background: linear-gradient(
31
31
  to right,
@@ -52,8 +52,8 @@
52
52
  /* Thumb — Webkit */
53
53
  .slider::-webkit-slider-thumb {
54
54
  appearance: none;
55
- width: var(--slider_thumb-size);
56
- height: var(--slider_thumb-size);
55
+ inline-size: var(--slider_thumb-size);
56
+ block-size: var(--slider_thumb-size);
57
57
  border-radius: 50%;
58
58
  background-color: var(--slider_color-active, var(--bdc-color_active));
59
59
  box-shadow: var(--bds-shadow_s);
@@ -63,8 +63,8 @@
63
63
 
64
64
  /* Thumb — Firefox */
65
65
  .slider::-moz-range-thumb {
66
- width: var(--slider_thumb-size);
67
- height: var(--slider_thumb-size);
66
+ inline-size: var(--slider_thumb-size);
67
+ block-size: var(--slider_thumb-size);
68
68
  border: none;
69
69
  border-radius: 50%;
70
70
  background-color: var(--slider_color-active, var(--bdc-color_active));
@@ -25,8 +25,8 @@
25
25
  display: inline-flex;
26
26
  align-items: center;
27
27
  flex-shrink: 0;
28
- width: var(--switch_track-width);
29
- height: var(--switch_track-height);
28
+ inline-size: var(--switch_track-width);
29
+ block-size: var(--switch_track-height);
30
30
  }
31
31
 
32
32
  /* Real input — invisible overlay to capture pointer/keyboard events */
@@ -35,8 +35,8 @@
35
35
  inset: 0;
36
36
  appearance: none;
37
37
  opacity: 0;
38
- width: 100%;
39
- height: 100%;
38
+ inline-size: 100%;
39
+ block-size: 100%;
40
40
  margin: 0;
41
41
  cursor: pointer;
42
42
  z-index: 1;
@@ -50,8 +50,8 @@
50
50
  .track {
51
51
  display: inline-flex;
52
52
  align-items: center;
53
- width: var(--switch_track-width);
54
- height: var(--switch_track-height);
53
+ inline-size: var(--switch_track-width);
54
+ block-size: var(--switch_track-height);
55
55
  border-radius: 999px;
56
56
  background-color: var(--switch_track-bg, var(--bds-color_grey--subtle));
57
57
  padding-inline: var(--switch_track-pad);
@@ -69,8 +69,8 @@
69
69
  /* Thumb */
70
70
  .thumb {
71
71
  display: block;
72
- width: var(--switch_thumb-size);
73
- height: var(--switch_thumb-size);
72
+ inline-size: var(--switch_thumb-size);
73
+ block-size: var(--switch_thumb-size);
74
74
  border-radius: 50%;
75
75
  background-color: var(--switch_thumb-bg, var(--bds-color_grey));
76
76
  box-shadow: var(--bds-shadow_s);
@@ -20,7 +20,7 @@
20
20
  background-color: var(--textarea_color_bg, var(--bds-color_bg));
21
21
  color: var(--textarea_color, var(--bds-color_on-bg));
22
22
  resize: vertical;
23
- min-height: calc(var(--bds-space_m) * 5);
23
+ min-block-size: calc(var(--bds-space_m) * 5);
24
24
  transition: --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing),
25
25
  background-color var(--bds-animation_transition-duration) var(--bds-animation_easing);
26
26
  }
@@ -1,7 +1,7 @@
1
1
  @layer component {
2
2
  .wrapper {
3
- width: 2em;
4
- height: 2em;
3
+ inline-size: 2em;
4
+ block-size: 2em;
5
5
  font-size: 3em;
6
6
  border-radius: 50%;
7
7
  display: flex;
@@ -13,11 +13,11 @@
13
13
  }
14
14
 
15
15
  .item {
16
- border-bottom: 1px solid var(--bds-color_bg--subtle);
16
+ border-block-end: 1px solid var(--bds-color_bg--subtle);
17
17
  }
18
18
 
19
19
  .item:last-child {
20
- border-bottom: none;
20
+ border-block-end: none;
21
21
  }
22
22
 
23
23
  .heading {
@@ -29,7 +29,7 @@
29
29
  display: flex;
30
30
  align-items: center;
31
31
  justify-content: space-between;
32
- width: 100%;
32
+ inline-size: 100%;
33
33
  padding: var(--bds-space_m);
34
34
  font-family: var(--bds-font_family--body);
35
35
  font-size: var(--bds-font_size--body);
@@ -63,8 +63,8 @@
63
63
  }
64
64
 
65
65
  .chevron {
66
- width: 1.25rem;
67
- height: 1.25rem;
66
+ inline-size: 1.25rem;
67
+ block-size: 1.25rem;
68
68
  flex-shrink: 0;
69
69
  transition: transform var(--bds-animation_transition-duration) var(--bds-animation_easing);
70
70
  }
@@ -0,0 +1,62 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Alert.native.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Alert (React Native)
7
+
8
+ React Native alert component for inline status messages. Supports four semantic variants, an optional title, and a dismissible mode — matching the web `Alert` API.
9
+
10
+ > **Status: alpha** — API may change before stable release.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { Alert } from '@boostdev/components';
16
+
17
+ <Alert variant="success" title="Saved">
18
+ Your changes have been saved successfully.
19
+ </Alert>
20
+ ```
21
+
22
+ ## Examples
23
+
24
+ ### All variants
25
+ <Canvas of={Stories.AllVariants} />
26
+
27
+ ### Info
28
+ <Canvas of={Stories.Info} />
29
+
30
+ ### Success
31
+ <Canvas of={Stories.Success} />
32
+
33
+ ### Warning
34
+ <Canvas of={Stories.Warning} />
35
+
36
+ ### Error
37
+ <Canvas of={Stories.Error} />
38
+
39
+ ### With title
40
+ <Canvas of={Stories.WithTitle} />
41
+
42
+ ### Dismissible
43
+ <Canvas of={Stories.Dismissible} />
44
+
45
+ ## Props
46
+
47
+ <ArgTypes of={Stories} />
48
+
49
+ ## Props reference
50
+
51
+ | Prop | Type | Default | Description |
52
+ |------|------|---------|-------------|
53
+ | `variant` | `"info" \| "success" \| "warning" \| "error"` | `"info"` | Semantic colour and icon |
54
+ | `title` | string | — | Optional heading above body content |
55
+ | `onDismiss` | `() => void` | — | When set, a dismiss button is shown; called on press |
56
+ | `children` | `ReactNode` | — | Alert body content |
57
+
58
+ ## Accessibility
59
+
60
+ - `error` and `warning` variants announce content as `assertive`
61
+ - `info` and `success` variants announce content as `polite`
62
+ - The dismiss button has a descriptive accessibility label