@viraui/react 2026.0.13 → 2026.0.14

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.
@@ -77,12 +77,20 @@ export type AutocompleteProps = Omit<AutocompleteRoot.Props<any>, 'children'> &
77
77
  * @default No ref is attached.
78
78
  */
79
79
  ref?: React.Ref<HTMLInputElement>;
80
+ /**
81
+ * Portal mount node (iframe body, shadow root, or app overlay root).
82
+ * Omit for Base UI default (`document.body` from the module window —
83
+ * iframe hosts must pass the iframe document body explicitly).
84
+ *
85
+ * @default Base UI FloatingPortal resolution
86
+ */
87
+ container?: React.ComponentProps<typeof PrimitiveAutocomplete.Portal>['container'];
80
88
  /**
81
89
  * List content: static nodes (`Item` / `Group` / `Collection`) or a Base UI list render function.
82
90
  */
83
91
  children?: React.ReactNode | ((item: any, index: number) => React.ReactNode);
84
92
  };
85
- declare function AutocompleteRootComponent({ children, className, description, dirty, disabled, empty, endAddon, error, fitContent, invalid, label, name, placeholder, ref, startAddon, status, touched, validate, validationDebounceTime, validationMode, ...autocompleteRootProps }: AutocompleteProps): React.JSX.Element;
93
+ declare function AutocompleteRootComponent({ children, className, container, description, dirty, disabled, empty, endAddon, error, fitContent, invalid, label, name, placeholder, ref, startAddon, status, touched, validate, validationDebounceTime, validationMode, ...autocompleteRootProps }: AutocompleteProps): React.JSX.Element;
86
94
  declare const AutocompleteCollection: typeof PrimitiveAutocomplete.Collection;
87
95
  type AutocompleteComponent = typeof AutocompleteRootComponent & {
88
96
  Item: typeof AutocompleteItemPart;
@@ -7,7 +7,8 @@
7
7
  "Suggest options while allowing free-form text in the input",
8
8
  "Filterable tag, movie, or docs search with optional empty and status copy",
9
9
  "Grouped suggestion lists via Autocomplete.Group and Collection",
10
- "Async or limited result lists that need polite status announcements"
10
+ "Async or limited result lists that need polite status announcements",
11
+ "Custom portal host (iframe preview, shadow root, or app overlay root) via `container` on the portal-owning public part"
11
12
  ],
12
13
  "whenNotToUse": [
13
14
  "Remembered selection from a closed list — use Select (or Combobox when available)",
@@ -27,6 +28,9 @@
27
28
  "related": ["Textfield", "Select", "Fieldset", "Stack", "Surface", "Spinner"],
28
29
  "properties": {
29
30
  "react": {
31
+ "container": {
32
+ "description": "Portal mount node (iframe body, shadow root, or app overlay root). Omit for Base UI default (`document.body` from the module window — iframe hosts must pass the iframe document body explicitly)."
33
+ },
30
34
  "description": {
31
35
  "description": "Helper copy rendered with `Field.Description` and `Text` while the field is valid."
32
36
  },
@@ -18,7 +18,7 @@ import { Autocomplete } from "@base-ui/react/autocomplete";
18
18
  import { useId } from "react";
19
19
  //#region src/components/autocomplete/autocomplete.tsx
20
20
  var AutocompletePrimitiveRoot = Autocomplete.Root;
21
- function AutocompleteRootComponent({ children, className, description, dirty, disabled, empty, endAddon, error, fitContent, invalid, label, name, placeholder, ref, startAddon, status, touched, validate, validationDebounceTime, validationMode, ...autocompleteRootProps }) {
21
+ function AutocompleteRootComponent({ children, className, container, description, dirty, disabled, empty, endAddon, error, fitContent, invalid, label, name, placeholder, ref, startAddon, status, touched, validate, validationDebounceTime, validationMode, ...autocompleteRootProps }) {
22
22
  const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...restAutocompleteProps } = autocompleteRootProps;
23
23
  const fieldLabelId = useId();
24
24
  const listAccessibleNameProps = {};
@@ -63,48 +63,51 @@ function AutocompleteRootComponent({ children, className, description, dirty, di
63
63
  ref,
64
64
  ...fitContent && { "data-sizing": "content" }
65
65
  })
66
- }), /* @__PURE__ */ jsx(Autocomplete.Portal, { children: /* @__PURE__ */ jsx(Autocomplete.Positioner, {
67
- sideOffset: 2,
68
- children: /* @__PURE__ */ jsx(Autocomplete.Popup, {
69
- className: autocomplete_module_default.AutocompletePopup,
70
- children: /* @__PURE__ */ jsx(Elevator, {
71
- resting: 1,
72
- children: /* @__PURE__ */ jsxs(Surface, {
73
- border: "all",
74
- className: autocomplete_module_default.PopupChrome,
75
- color: 1,
76
- "data-vibrant": "",
77
- hPadding: "x-small",
78
- overflow: "auto",
79
- radius: "medium",
80
- vPadding: "x-small",
81
- children: [
82
- /* @__PURE__ */ jsx(Autocomplete.Status, {
83
- className: autocomplete_module_default.Status,
84
- children: typeof status === "string" ? /* @__PURE__ */ jsx(Text, {
85
- size: "x-small",
86
- tone: "muted",
87
- children: status
88
- }) : status
89
- }),
90
- /* @__PURE__ */ jsx(Autocomplete.Empty, {
91
- className: autocomplete_module_default.Empty,
92
- children: typeof empty === "string" ? /* @__PURE__ */ jsx(Text, {
93
- size: "small",
94
- tone: "muted",
95
- children: empty
96
- }) : empty
97
- }),
98
- /* @__PURE__ */ jsx(Autocomplete.List, {
99
- ...listAccessibleNameProps,
100
- className: autocomplete_module_default.List,
101
- children
102
- })
103
- ]
66
+ }), /* @__PURE__ */ jsx(Autocomplete.Portal, {
67
+ container,
68
+ children: /* @__PURE__ */ jsx(Autocomplete.Positioner, {
69
+ sideOffset: 2,
70
+ children: /* @__PURE__ */ jsx(Autocomplete.Popup, {
71
+ className: autocomplete_module_default.AutocompletePopup,
72
+ children: /* @__PURE__ */ jsx(Elevator, {
73
+ resting: 1,
74
+ children: /* @__PURE__ */ jsxs(Surface, {
75
+ border: "all",
76
+ className: autocomplete_module_default.PopupChrome,
77
+ color: 1,
78
+ "data-vibrant": "",
79
+ hPadding: "x-small",
80
+ overflow: "auto",
81
+ radius: "medium",
82
+ vPadding: "x-small",
83
+ children: [
84
+ /* @__PURE__ */ jsx(Autocomplete.Status, {
85
+ className: autocomplete_module_default.Status,
86
+ children: typeof status === "string" ? /* @__PURE__ */ jsx(Text, {
87
+ size: "x-small",
88
+ tone: "muted",
89
+ children: status
90
+ }) : status
91
+ }),
92
+ /* @__PURE__ */ jsx(Autocomplete.Empty, {
93
+ className: autocomplete_module_default.Empty,
94
+ children: typeof empty === "string" ? /* @__PURE__ */ jsx(Text, {
95
+ size: "small",
96
+ tone: "muted",
97
+ children: empty
98
+ }) : empty
99
+ }),
100
+ /* @__PURE__ */ jsx(Autocomplete.List, {
101
+ ...listAccessibleNameProps,
102
+ className: autocomplete_module_default.List,
103
+ children
104
+ })
105
+ ]
106
+ })
104
107
  })
105
108
  })
106
109
  })
107
- }) })]
110
+ })]
108
111
  }),
109
112
  /* @__PURE__ */ jsx(FieldHelperCopy, {
110
113
  className: autocomplete_module_default.Description,
@@ -1,3 +1,4 @@
1
+ import { Drawer as PrimitiveDrawer } from '@base-ui/react';
1
2
  import { StackProps } from '..';
2
3
  import type * as React from 'react';
3
4
  /**
@@ -62,5 +63,13 @@ export type DialogSheetProps = {
62
63
  * @default `97dvh`
63
64
  */
64
65
  maxHeight?: string;
66
+ /**
67
+ * Portal mount node (iframe body, shadow root, or app overlay root).
68
+ * Omit for Base UI default (`document.body` from the module window —
69
+ * iframe hosts must pass the iframe document body explicitly).
70
+ *
71
+ * @default Base UI FloatingPortal resolution
72
+ */
73
+ container?: React.ComponentProps<typeof PrimitiveDrawer.Portal>['container'];
65
74
  };
66
75
  export declare const DialogSheet: React.FC<DialogSheetProps>;
@@ -9,76 +9,79 @@ import { Title } from "../title/title.js";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { Drawer } from "@base-ui/react";
11
11
  //#region src/components/dialog/dialog-sheet.tsx
12
- var DialogSheet = ({ children, description, title, showCloseButton = false, fitContent = false, showHandle = true, headerAlign = "start", maxContentWidth = "50ch", maxHeight = "97dvh" }) => /* @__PURE__ */ jsxs(Drawer.Portal, { children: [/* @__PURE__ */ jsx(Drawer.Backdrop, { className: dialog_module_default.Backdrop }), /* @__PURE__ */ jsx(Drawer.Viewport, {
13
- render: /* @__PURE__ */ jsx(Stack, {
14
- vAlign: "end",
15
- hPadding: "small",
16
- vPadding: "small",
17
- fillChildren: false,
18
- hAlign: fitContent ? "center" : void 0
19
- }),
20
- className: dialog_module_default.Viewport,
21
- children: /* @__PURE__ */ jsx(Elevator, {
22
- resting: 4,
23
- children: /* @__PURE__ */ jsxs(Drawer.Popup, {
24
- className: dialog_module_default.Popup,
25
- style: { "--vui-dialog-max-block-size": maxHeight },
26
- render: /* @__PURE__ */ jsx(Surface, {
27
- border: "top",
28
- render: /* @__PURE__ */ jsx(Stack, { hAlign: "center" }),
29
- color: 1,
30
- "data-vibrant": "",
31
- radius: "large",
32
- vPadding: ["medium", 0]
33
- }),
34
- children: [showHandle && /* @__PURE__ */ jsxs(Stack, {
35
- inline: true,
36
- direction: "row",
37
- vPadding: [0, "medium"],
38
- columnGap: "small",
39
- children: [
40
- /* @__PURE__ */ jsx("div", { className: dialog_module_default.HandleShape }),
41
- /* @__PURE__ */ jsx("div", { className: dialog_module_default.HandleShape }),
42
- /* @__PURE__ */ jsx("div", { className: dialog_module_default.HandleShape })
43
- ]
44
- }), /* @__PURE__ */ jsxs(Stack, {
45
- maxWidth: maxContentWidth,
46
- rowGap: "large",
47
- className: dialog_module_default.Body,
48
- children: [/* @__PURE__ */ jsxs(Stack, {
49
- className: dialog_module_default.Header,
50
- "data-shrink": "false",
51
- hAlign: headerAlign,
52
- hPadding: "medium",
53
- fillChildren: false,
54
- children: [/* @__PURE__ */ jsx(Drawer.Title, {
55
- render: /* @__PURE__ */ jsx(Title, { size: "4" }),
56
- children: title
57
- }), description && /* @__PURE__ */ jsx(Drawer.Description, {
58
- render: /* @__PURE__ */ jsx(Text, {
59
- tone: "muted",
60
- render: /* @__PURE__ */ jsx("p", {})
12
+ var DialogSheet = ({ children, description, title, showCloseButton = false, fitContent = false, showHandle = true, headerAlign = "start", maxContentWidth = "50ch", maxHeight = "97dvh", container }) => /* @__PURE__ */ jsxs(Drawer.Portal, {
13
+ container,
14
+ children: [/* @__PURE__ */ jsx(Drawer.Backdrop, { className: dialog_module_default.Backdrop }), /* @__PURE__ */ jsx(Drawer.Viewport, {
15
+ render: /* @__PURE__ */ jsx(Stack, {
16
+ vAlign: "end",
17
+ hPadding: "small",
18
+ vPadding: "small",
19
+ fillChildren: false,
20
+ hAlign: fitContent ? "center" : void 0
21
+ }),
22
+ className: dialog_module_default.Viewport,
23
+ children: /* @__PURE__ */ jsx(Elevator, {
24
+ resting: 4,
25
+ children: /* @__PURE__ */ jsxs(Drawer.Popup, {
26
+ className: dialog_module_default.Popup,
27
+ style: { "--vui-dialog-max-block-size": maxHeight },
28
+ render: /* @__PURE__ */ jsx(Surface, {
29
+ border: "top",
30
+ render: /* @__PURE__ */ jsx(Stack, { hAlign: "center" }),
31
+ color: 1,
32
+ "data-vibrant": "",
33
+ radius: "large",
34
+ vPadding: ["medium", 0]
35
+ }),
36
+ children: [showHandle && /* @__PURE__ */ jsxs(Stack, {
37
+ inline: true,
38
+ direction: "row",
39
+ vPadding: [0, "medium"],
40
+ columnGap: "small",
41
+ children: [
42
+ /* @__PURE__ */ jsx("div", { className: dialog_module_default.HandleShape }),
43
+ /* @__PURE__ */ jsx("div", { className: dialog_module_default.HandleShape }),
44
+ /* @__PURE__ */ jsx("div", { className: dialog_module_default.HandleShape })
45
+ ]
46
+ }), /* @__PURE__ */ jsxs(Stack, {
47
+ maxWidth: maxContentWidth,
48
+ rowGap: "large",
49
+ className: dialog_module_default.Body,
50
+ children: [/* @__PURE__ */ jsxs(Stack, {
51
+ className: dialog_module_default.Header,
52
+ "data-shrink": "false",
53
+ hAlign: headerAlign,
54
+ hPadding: "medium",
55
+ fillChildren: false,
56
+ children: [/* @__PURE__ */ jsx(Drawer.Title, {
57
+ render: /* @__PURE__ */ jsx(Title, { size: "4" }),
58
+ children: title
59
+ }), description && /* @__PURE__ */ jsx(Drawer.Description, {
60
+ render: /* @__PURE__ */ jsx(Text, {
61
+ tone: "muted",
62
+ render: /* @__PURE__ */ jsx("p", {})
63
+ }),
64
+ children: description
65
+ })]
66
+ }), /* @__PURE__ */ jsxs(Drawer.Content, {
67
+ render: /* @__PURE__ */ jsx(Stack, {
68
+ vAlign: "start",
69
+ fillChildren: false,
70
+ className: dialog_module_default.Content
61
71
  }),
62
- children: description
72
+ children: [showCloseButton && /* @__PURE__ */ jsx(Drawer.Close, {
73
+ className: dialog_module_default.CloseButton,
74
+ render: /* @__PURE__ */ jsx(IconButton, {
75
+ "aria-label": "Close dialog",
76
+ icon: /* @__PURE__ */ jsx(ChromeXmark, {}),
77
+ variant: "flat"
78
+ })
79
+ }), children]
63
80
  })]
64
- }), /* @__PURE__ */ jsxs(Drawer.Content, {
65
- render: /* @__PURE__ */ jsx(Stack, {
66
- vAlign: "start",
67
- fillChildren: false,
68
- className: dialog_module_default.Content
69
- }),
70
- children: [showCloseButton && /* @__PURE__ */ jsx(Drawer.Close, {
71
- className: dialog_module_default.CloseButton,
72
- render: /* @__PURE__ */ jsx(IconButton, {
73
- "aria-label": "Close dialog",
74
- icon: /* @__PURE__ */ jsx(ChromeXmark, {}),
75
- variant: "flat"
76
- })
77
- }), children]
78
81
  })]
79
- })]
82
+ })
80
83
  })
81
- })
82
- })] });
84
+ })]
85
+ });
83
86
  //#endregion
84
87
  export { DialogSheet };
@@ -8,7 +8,8 @@
8
8
  "Short forms or read-only content that needs a modal layer above the app",
9
9
  "Bottom sheets where swipe dismissal or snap points are useful (pass `snapPoints`, `snapPoint`/`onSnapPointChange`, or `defaultSnapPoint` directly on `Dialog`)",
10
10
  "Drill-down sheet flows (for example settings sub-pages) by nesting a `Dialog` inside `Dialog.Sheet` children",
11
- "App-shell indent effects by wrapping page content in `Dialog.Provider`, `Dialog.IndentBackground`, and `Dialog.Indent`"
11
+ "App-shell indent effects by wrapping page content in `Dialog.Provider`, `Dialog.IndentBackground`, and `Dialog.Indent`",
12
+ "Custom portal host (iframe preview, shadow root, or app overlay root) via `container` on the portal-owning public part"
12
13
  ],
13
14
  "whenNotToUse": [
14
15
  "Small anchored hints or quick actions that should use Popover or Menu",
@@ -31,6 +32,9 @@
31
32
  "Sheet.children": {
32
33
  "description": "Optional body content rendered below the description."
33
34
  },
35
+ "Sheet.container": {
36
+ "description": "Portal mount node (iframe body, shadow root, or app overlay root). Omit for Base UI default (`document.body` from the module window — iframe hosts must pass the iframe document body explicitly)."
37
+ },
34
38
  "Sheet.description": {
35
39
  "description": "Supporting copy announced with the title."
36
40
  },
@@ -1,4 +1,4 @@
1
- import { MenuPositioner } from '@base-ui/react/menu';
1
+ import { Menu as PrimitiveMenu, MenuPositioner } from '@base-ui/react/menu';
2
2
  import type * as React from 'react';
3
3
  type MenuPopupPositionerProps = Omit<MenuPositioner.Props, 'className' | 'style' | 'render'>;
4
4
  /**
@@ -13,6 +13,14 @@ type MenuPopupPositionerProps = Omit<MenuPositioner.Props, 'className' | 'style'
13
13
  */
14
14
  export type MenuPopupProps = MenuPopupPositionerProps & {
15
15
  children?: React.ReactNode;
16
+ /**
17
+ * Portal mount node (iframe body, shadow root, or app overlay root).
18
+ * Omit for Base UI default (`document.body` from the module window —
19
+ * iframe hosts must pass the iframe document body explicitly).
20
+ *
21
+ * @default Base UI FloatingPortal resolution
22
+ */
23
+ container?: React.ComponentProps<typeof PrimitiveMenu.Portal>['container'];
16
24
  };
17
25
  export declare const MenuPopup: React.FC<MenuPopupProps>;
18
26
  export {};
@@ -4,25 +4,28 @@ import menu_module_default from "./menu.module.js";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import { Menu } from "@base-ui/react/menu";
6
6
  //#region src/components/menu/menu-popup.tsx
7
- var MenuPopup = ({ children, sideOffset = 2, ...positionerProps }) => /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(Menu.Positioner, {
8
- sideOffset,
9
- ...positionerProps,
10
- children: /* @__PURE__ */ jsx(Elevator, {
11
- resting: 1,
12
- children: /* @__PURE__ */ jsx(Menu.Popup, {
13
- className: menu_module_default.MenuPopup,
14
- render: /* @__PURE__ */ jsx(Surface, {
15
- border: "all",
16
- color: 1,
17
- "data-vibrant": true,
18
- hPadding: "x-small",
19
- overflow: "auto",
20
- radius: "medium",
21
- vPadding: "x-small"
22
- }),
23
- children
7
+ var MenuPopup = ({ children, sideOffset = 2, container, ...positionerProps }) => /* @__PURE__ */ jsx(Menu.Portal, {
8
+ container,
9
+ children: /* @__PURE__ */ jsx(Menu.Positioner, {
10
+ sideOffset,
11
+ ...positionerProps,
12
+ children: /* @__PURE__ */ jsx(Elevator, {
13
+ resting: 1,
14
+ children: /* @__PURE__ */ jsx(Menu.Popup, {
15
+ className: menu_module_default.MenuPopup,
16
+ render: /* @__PURE__ */ jsx(Surface, {
17
+ border: "all",
18
+ color: 1,
19
+ "data-vibrant": true,
20
+ hPadding: "x-small",
21
+ overflow: "auto",
22
+ radius: "medium",
23
+ vPadding: "x-small"
24
+ }),
25
+ children
26
+ })
24
27
  })
25
28
  })
26
- }) });
29
+ });
27
30
  //#endregion
28
31
  export { MenuPopup };
@@ -8,7 +8,8 @@
8
8
  "Nested command lists with checkbox or radio options that stay open on toggle",
9
9
  "Link rows mixed with actions in one menu surface",
10
10
  "Detached or multi-trigger menus via `Menu.createHandle`",
11
- "Account / user widgets: `Menu.Trigger` as a Surface chip (avatar + name + email) opening a Popup with a static profile header plus action items"
11
+ "Account / user widgets: `Menu.Trigger` as a Surface chip (avatar + name + email) opening a Popup with a static profile header plus action items",
12
+ "Custom portal host (iframe preview, shadow root, or app overlay root) via `container` on the portal-owning public part"
12
13
  ],
13
14
  "whenNotToUse": [
14
15
  "Page-blocking modal flows (use Dialog)",
@@ -64,6 +65,9 @@
64
65
  "LinkItem.sentiment": {
65
66
  "description": "Visual sentiment. `destructive` uses highlight-red wash + text."
66
67
  },
68
+ "Popup.container": {
69
+ "description": "Portal mount node (iframe body, shadow root, or app overlay root). Omit for Base UI default (`document.body` from the module window — iframe hosts must pass the iframe document body explicitly)."
70
+ },
67
71
  "RadioGroup.label": {
68
72
  "description": "Optional accessible group label rendered with `Menu.GroupLabel`."
69
73
  },
@@ -1,8 +1,18 @@
1
- import { PopoverPositioner } from '@base-ui/react/popover';
1
+ import { Popover as PrimitivePopover, PopoverPositioner } from '@base-ui/react/popover';
2
2
  import { StackProps } from '..';
3
3
  import type * as React from 'react';
4
4
  type PopoverSheetPositionerProps = Omit<PopoverPositioner.Props, 'className' | 'style' | 'render'>;
5
- type PopoverSheetStyledProps = PopoverSheetPositionerProps & {
5
+ type PopoverSheetContainerProps = {
6
+ /**
7
+ * Portal mount node (iframe body, shadow root, or app overlay root).
8
+ * Omit for Base UI default (`document.body` from the module window —
9
+ * iframe hosts must pass the iframe document body explicitly).
10
+ *
11
+ * @default Base UI FloatingPortal resolution
12
+ */
13
+ container?: React.ComponentProps<typeof PrimitivePopover.Portal>['container'];
14
+ };
15
+ type PopoverSheetStyledProps = PopoverSheetPositionerProps & PopoverSheetContainerProps & {
6
16
  /**
7
17
  * Whether to skip the built-in Surface, header, and close chrome and render
8
18
  * only the positioning/animation shell (Portal + Positioner + Popup + Viewport).
@@ -33,7 +43,7 @@ type PopoverSheetStyledProps = PopoverSheetPositionerProps & {
33
43
  */
34
44
  headerAlign?: Extract<StackProps['hAlign'], 'start' | 'center' | 'end'>;
35
45
  };
36
- type PopoverSheetUnstyledProps = PopoverSheetPositionerProps & {
46
+ type PopoverSheetUnstyledProps = PopoverSheetPositionerProps & PopoverSheetContainerProps & {
37
47
  /**
38
48
  * Renders only the positioning/animation shell; compose `Popover.Title`,
39
49
  * `Popover.Description`, and any chrome inside `children`.
@@ -8,72 +8,75 @@ import { Title } from "../title/title.js";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  import { Popover } from "@base-ui/react/popover";
10
10
  //#region src/components/popover/popover-sheet.tsx
11
- var PopoverSheet = ({ unstyled, title, description, children, showCloseButton = false, headerAlign = "start", sideOffset = 8, ...positionerProps }) => /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, {
12
- className: popover_module_default.Sheet,
13
- sideOffset,
14
- ...positionerProps,
15
- children: unstyled ? /* @__PURE__ */ jsx(Popover.Popup, {
16
- className: popover_module_default.Popup,
17
- children: /* @__PURE__ */ jsx(Popover.Viewport, {
18
- className: popover_module_default.Viewport,
19
- "data-unstyled": "",
20
- children
21
- })
22
- }) : /* @__PURE__ */ jsx(Popover.Popup, {
23
- className: popover_module_default.Popup,
24
- render: /* @__PURE__ */ jsx(Surface, {
25
- color: 1,
26
- border: "all",
27
- radius: "large",
28
- "data-vibrant": "",
29
- render: /* @__PURE__ */ jsx(Stack, {
30
- rowGap: "medium",
31
- fillChildren: false
11
+ var PopoverSheet = ({ unstyled, title, description, children, showCloseButton = false, headerAlign = "start", sideOffset = 8, container, ...positionerProps }) => /* @__PURE__ */ jsx(Popover.Portal, {
12
+ container,
13
+ children: /* @__PURE__ */ jsx(Popover.Positioner, {
14
+ className: popover_module_default.Sheet,
15
+ sideOffset,
16
+ ...positionerProps,
17
+ children: unstyled ? /* @__PURE__ */ jsx(Popover.Popup, {
18
+ className: popover_module_default.Popup,
19
+ children: /* @__PURE__ */ jsx(Popover.Viewport, {
20
+ className: popover_module_default.Viewport,
21
+ "data-unstyled": "",
22
+ children
32
23
  })
33
- }),
34
- children: /* @__PURE__ */ jsxs(Popover.Viewport, {
35
- className: popover_module_default.Viewport,
36
- render: /* @__PURE__ */ jsx(Stack, {
37
- rowGap: "medium",
38
- vPadding: "medium",
39
- hPadding: "medium",
40
- fillChildren: false
24
+ }) : /* @__PURE__ */ jsx(Popover.Popup, {
25
+ className: popover_module_default.Popup,
26
+ render: /* @__PURE__ */ jsx(Surface, {
27
+ color: 1,
28
+ border: "all",
29
+ radius: "large",
30
+ "data-vibrant": "",
31
+ render: /* @__PURE__ */ jsx(Stack, {
32
+ rowGap: "medium",
33
+ fillChildren: false
34
+ })
41
35
  }),
42
- children: [
43
- /* @__PURE__ */ jsxs(Stack, {
44
- hAlign: headerAlign,
45
- rowGap: "small",
46
- vAlign: "start",
47
- vPadding: [0, children ? "medium" : 0],
48
- children: [/* @__PURE__ */ jsx(Popover.Title, {
49
- render: /* @__PURE__ */ jsx(Title, {
50
- align: headerAlign,
51
- size: "6",
52
- lineHeight: "x-small"
53
- }),
54
- children: title
55
- }), description && /* @__PURE__ */ jsx(Popover.Description, {
56
- render: /* @__PURE__ */ jsx(Text, {
57
- align: headerAlign,
58
- tone: "muted",
59
- render: /* @__PURE__ */ jsx("p", {})
60
- }),
61
- children: description
62
- })]
63
- }),
64
- showCloseButton && /* @__PURE__ */ jsx(Popover.Close, {
65
- className: popover_module_default.CloseButton,
66
- render: /* @__PURE__ */ jsx(IconButton, {
67
- "aria-label": "Close popover",
68
- icon: /* @__PURE__ */ jsx(ChromeXmark, {}),
69
- size: "small",
70
- variant: "flat"
71
- })
36
+ children: /* @__PURE__ */ jsxs(Popover.Viewport, {
37
+ className: popover_module_default.Viewport,
38
+ render: /* @__PURE__ */ jsx(Stack, {
39
+ rowGap: "medium",
40
+ vPadding: "medium",
41
+ hPadding: "medium",
42
+ fillChildren: false
72
43
  }),
73
- children
74
- ]
44
+ children: [
45
+ /* @__PURE__ */ jsxs(Stack, {
46
+ hAlign: headerAlign,
47
+ rowGap: "small",
48
+ vAlign: "start",
49
+ vPadding: [0, children ? "medium" : 0],
50
+ children: [/* @__PURE__ */ jsx(Popover.Title, {
51
+ render: /* @__PURE__ */ jsx(Title, {
52
+ align: headerAlign,
53
+ size: "6",
54
+ lineHeight: "x-small"
55
+ }),
56
+ children: title
57
+ }), description && /* @__PURE__ */ jsx(Popover.Description, {
58
+ render: /* @__PURE__ */ jsx(Text, {
59
+ align: headerAlign,
60
+ tone: "muted",
61
+ render: /* @__PURE__ */ jsx("p", {})
62
+ }),
63
+ children: description
64
+ })]
65
+ }),
66
+ showCloseButton && /* @__PURE__ */ jsx(Popover.Close, {
67
+ className: popover_module_default.CloseButton,
68
+ render: /* @__PURE__ */ jsx(IconButton, {
69
+ "aria-label": "Close popover",
70
+ icon: /* @__PURE__ */ jsx(ChromeXmark, {}),
71
+ size: "small",
72
+ variant: "flat"
73
+ })
74
+ }),
75
+ children
76
+ ]
77
+ })
75
78
  })
76
79
  })
77
- }) });
80
+ });
78
81
  //#endregion
79
82
  export { PopoverSheet };
@@ -8,7 +8,8 @@
8
8
  "Lightweight forms or confirmations that float beside the trigger without blocking the page",
9
9
  "Multi-trigger panels that swap content with direction-aware animations using `Popover.createHandle` and trigger `payload`",
10
10
  "Hover-revealed hints via `openOnHover` with `delay` and `closeDelay`",
11
- "Fully custom popup chrome via `Sheet` `unstyled` composed with `Popover.Title` and `Popover.Description`"
11
+ "Fully custom popup chrome via `Sheet` `unstyled` composed with `Popover.Title` and `Popover.Description`",
12
+ "Custom portal host (iframe preview, shadow root, or app overlay root) via `container` on the portal-owning public part"
12
13
  ],
13
14
  "whenNotToUse": [
14
15
  "Modal tasks that must block the page and demand focused attention (use Dialog)",
@@ -29,6 +30,9 @@
29
30
  "related": ["Surface", "Button", "IconButton", "Stack", "Dialog", "Tabs"],
30
31
  "properties": {
31
32
  "react": {
33
+ "Sheet.container": {
34
+ "description": "Portal mount node (iframe body, shadow root, or app overlay root). Omit for Base UI default (`document.body` from the module window — iframe hosts must pass the iframe document body explicitly)."
35
+ },
32
36
  "Sheet.description": {
33
37
  "description": "Supporting copy announced with the title."
34
38
  },
@@ -1,5 +1,5 @@
1
1
  import { FieldRoot } from '@base-ui/react/field';
2
- import { SelectRoot } from '@base-ui/react/select';
2
+ import { SelectRoot, Select as PrimitiveSelect } from '@base-ui/react/select';
3
3
  import { SelectGroup } from './select-group';
4
4
  import { SelectOption } from './select-option';
5
5
  import { SelectSeparator } from './select-separator';
@@ -59,8 +59,16 @@ export type SelectProps = SelectRoot.Props<string> & Omit<FieldRoot.Props, 'ref'
59
59
  * @default true
60
60
  */
61
61
  alignListWithTrigger?: boolean;
62
+ /**
63
+ * Portal mount node (iframe body, shadow root, or app overlay root).
64
+ * Omit for Base UI default (`document.body` from the module window —
65
+ * iframe hosts must pass the iframe document body explicitly).
66
+ *
67
+ * @default Base UI FloatingPortal resolution
68
+ */
69
+ container?: React.ComponentProps<typeof PrimitiveSelect.Portal>['container'];
62
70
  };
63
- declare function SelectRootComponent({ alignListWithTrigger, children, className, description, dirty, disabled, error, addon, invalid, label, name, placeholder, ref, touched, validate, validationDebounceTime, validationMode, ...selectRootProps }: SelectProps): React.JSX.Element;
71
+ declare function SelectRootComponent({ alignListWithTrigger, children, className, container, description, dirty, disabled, error, addon, invalid, label, name, placeholder, ref, touched, validate, validationDebounceTime, validationMode, ...selectRootProps }: SelectProps): React.JSX.Element;
64
72
  export declare const Select: typeof SelectRootComponent & {
65
73
  Option: typeof SelectOption;
66
74
  Group: typeof SelectGroup;
@@ -8,7 +8,8 @@
8
8
  "Grouped or separated option lists",
9
9
  "Rich option rows with a leading decorative addon and multi-line label content",
10
10
  "Trigger chrome aligned with Textfield control shell",
11
- "Select fields inside Fieldset when they share a section legend with other inputs"
11
+ "Select fields inside Fieldset when they share a section legend with other inputs",
12
+ "Custom portal host (iframe preview, shadow root, or app overlay root) via `container` on the portal-owning public part"
12
13
  ],
13
14
  "whenNotToUse": [
14
15
  "Multiple selection",
@@ -37,6 +38,9 @@
37
38
  "alignListWithTrigger": {
38
39
  "description": "When `true`, aligns the list popup width with the trigger via Base UI `alignItemWithTrigger`. Prefer `false` when options use a leading `addon`."
39
40
  },
41
+ "container": {
42
+ "description": "Portal mount node (iframe body, shadow root, or app overlay root). Omit for Base UI default (`document.body` from the module window — iframe hosts must pass the iframe document body explicitly)."
43
+ },
40
44
  "description": {
41
45
  "description": "Helper copy rendered with `Field.Description` and `Text` while the field is valid."
42
46
  },
@@ -18,7 +18,7 @@ import { Field } from "@base-ui/react/field";
18
18
  import { useId } from "react";
19
19
  import { Select } from "@base-ui/react/select";
20
20
  //#region src/components/select/select.tsx
21
- function SelectRootComponent({ alignListWithTrigger = true, children, className, description, dirty, disabled, error, addon = /* @__PURE__ */ jsx(ChromeChevronExpandY, { size: 16 }), invalid, label, name, placeholder, ref, touched, validate, validationDebounceTime, validationMode, ...selectRootProps }) {
21
+ function SelectRootComponent({ alignListWithTrigger = true, children, className, container, description, dirty, disabled, error, addon = /* @__PURE__ */ jsx(ChromeChevronExpandY, { size: 16 }), invalid, label, name, placeholder, ref, touched, validate, validationDebounceTime, validationMode, ...selectRootProps }) {
22
22
  const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...restSelectProps } = selectRootProps;
23
23
  const fieldLabelId = useId();
24
24
  const listAccessibleNameProps = {};
@@ -80,66 +80,69 @@ function SelectRootComponent({ alignListWithTrigger = true, children, className,
80
80
  }),
81
81
  children: addon
82
82
  })]
83
- }), /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsx(Select.Positioner, {
84
- alignItemWithTrigger: alignListWithTrigger,
85
- sideOffset: 2,
86
- children: /* @__PURE__ */ jsxs(Select.Popup, {
87
- className: select_module_default.SelectPopup,
88
- children: [
89
- /* @__PURE__ */ jsx(Select.ScrollUpArrow, {
90
- className: clsx(select_module_default.ScrollArrow, select_module_default.ScrollArrowUp),
91
- "data-vibrant": "",
92
- render: /* @__PURE__ */ jsx(Stack, {
93
- fillChildren: false,
94
- hAlign: "center",
95
- vAlign: "center"
96
- }),
97
- children: /* @__PURE__ */ jsx(IconButton, {
98
- "aria-label": "Scroll list up",
99
- className: select_module_default.ScrollButton,
100
- icon: /* @__PURE__ */ jsx(ChromeChevronUp, {}),
101
- size: "small",
102
- tabIndex: -1,
103
- variant: "flat"
104
- })
105
- }),
106
- /* @__PURE__ */ jsx(Elevator, {
107
- resting: 1,
108
- children: /* @__PURE__ */ jsx(Select.List, {
109
- ...listAccessibleNameProps,
110
- className: select_module_default.List,
111
- render: /* @__PURE__ */ jsx(Surface, {
112
- border: "all",
113
- color: 1,
114
- hPadding: "x-small",
115
- overflow: "auto",
116
- radius: "medium",
117
- vPadding: "x-small",
118
- "data-vibrant": ""
83
+ }), /* @__PURE__ */ jsx(Select.Portal, {
84
+ container,
85
+ children: /* @__PURE__ */ jsx(Select.Positioner, {
86
+ alignItemWithTrigger: alignListWithTrigger,
87
+ sideOffset: 2,
88
+ children: /* @__PURE__ */ jsxs(Select.Popup, {
89
+ className: select_module_default.SelectPopup,
90
+ children: [
91
+ /* @__PURE__ */ jsx(Select.ScrollUpArrow, {
92
+ className: clsx(select_module_default.ScrollArrow, select_module_default.ScrollArrowUp),
93
+ "data-vibrant": "",
94
+ render: /* @__PURE__ */ jsx(Stack, {
95
+ fillChildren: false,
96
+ hAlign: "center",
97
+ vAlign: "center"
119
98
  }),
120
- children
121
- })
122
- }),
123
- /* @__PURE__ */ jsx(Select.ScrollDownArrow, {
124
- className: clsx(select_module_default.ScrollArrow, select_module_default.ScrollArrowDown),
125
- "data-vibrant": "",
126
- render: /* @__PURE__ */ jsx(Stack, {
127
- fillChildren: false,
128
- hAlign: "center",
129
- vAlign: "center"
99
+ children: /* @__PURE__ */ jsx(IconButton, {
100
+ "aria-label": "Scroll list up",
101
+ className: select_module_default.ScrollButton,
102
+ icon: /* @__PURE__ */ jsx(ChromeChevronUp, {}),
103
+ size: "small",
104
+ tabIndex: -1,
105
+ variant: "flat"
106
+ })
130
107
  }),
131
- children: /* @__PURE__ */ jsx(IconButton, {
132
- "aria-label": "Scroll list down",
133
- className: select_module_default.ScrollButton,
134
- icon: /* @__PURE__ */ jsx(ChromeChevronDown, {}),
135
- size: "small",
136
- tabIndex: -1,
137
- variant: "flat"
108
+ /* @__PURE__ */ jsx(Elevator, {
109
+ resting: 1,
110
+ children: /* @__PURE__ */ jsx(Select.List, {
111
+ ...listAccessibleNameProps,
112
+ className: select_module_default.List,
113
+ render: /* @__PURE__ */ jsx(Surface, {
114
+ border: "all",
115
+ color: 1,
116
+ hPadding: "x-small",
117
+ overflow: "auto",
118
+ radius: "medium",
119
+ vPadding: "x-small",
120
+ "data-vibrant": ""
121
+ }),
122
+ children
123
+ })
124
+ }),
125
+ /* @__PURE__ */ jsx(Select.ScrollDownArrow, {
126
+ className: clsx(select_module_default.ScrollArrow, select_module_default.ScrollArrowDown),
127
+ "data-vibrant": "",
128
+ render: /* @__PURE__ */ jsx(Stack, {
129
+ fillChildren: false,
130
+ hAlign: "center",
131
+ vAlign: "center"
132
+ }),
133
+ children: /* @__PURE__ */ jsx(IconButton, {
134
+ "aria-label": "Scroll list down",
135
+ className: select_module_default.ScrollButton,
136
+ icon: /* @__PURE__ */ jsx(ChromeChevronDown, {}),
137
+ size: "small",
138
+ tabIndex: -1,
139
+ variant: "flat"
140
+ })
138
141
  })
139
- })
140
- ]
142
+ ]
143
+ })
141
144
  })
142
- }) })]
145
+ })]
143
146
  }),
144
147
  /* @__PURE__ */ jsx(FieldHelperCopy, {
145
148
  className: select_module_default.Description,
@@ -6,7 +6,8 @@
6
6
  "whenToUse": [
7
7
  "Confirming a completed action such as save or delete",
8
8
  "Offering a short undo window after a reversible change",
9
- "Showing contextual feedback anchored to the control that triggered it"
9
+ "Showing contextual feedback anchored to the control that triggered it",
10
+ "Custom portal host (iframe preview, shadow root, or app overlay root) via `container` on the portal-owning public part"
10
11
  ],
11
12
  "whenNotToUse": [
12
13
  "Blocking errors that need immediate correction — use inline field errors or a dialog",
@@ -51,6 +52,9 @@
51
52
  "Banner.toast": {
52
53
  "description": "Toast instance from `useToastManager().toasts` or `createToastManager()`."
53
54
  },
55
+ "Portal.container": {
56
+ "description": "Portal mount node (iframe body, shadow root, or app overlay root). Omit for Base UI default (`document.body` from the module window — iframe hosts must pass the iframe document body explicitly)."
57
+ },
54
58
  "Viewport.position": {
55
59
  "description": "Controls viewport placement and stacking layout hooks."
56
60
  }
@@ -1,4 +1,4 @@
1
- import { TooltipPositioner } from '@base-ui/react/tooltip';
1
+ import { Tooltip as PrimitiveTooltip, TooltipPositioner } from '@base-ui/react/tooltip';
2
2
  import type * as React from 'react';
3
3
  type TooltipContentPositionerProps = Omit<TooltipPositioner.Props, 'className' | 'style' | 'render'>;
4
4
  /**
@@ -10,6 +10,15 @@ type TooltipContentPositionerProps = Omit<TooltipPositioner.Props, 'className' |
10
10
  *
11
11
  * @default `sideOffset` is `8`.
12
12
  */
13
- export type TooltipContentProps = TooltipContentPositionerProps;
13
+ export type TooltipContentProps = TooltipContentPositionerProps & {
14
+ /**
15
+ * Portal mount node (iframe body, shadow root, or app overlay root).
16
+ * Omit for Base UI default (`document.body` from the module window —
17
+ * iframe hosts must pass the iframe document body explicitly).
18
+ *
19
+ * @default Base UI FloatingPortal resolution
20
+ */
21
+ container?: React.ComponentProps<typeof PrimitiveTooltip.Portal>['container'];
22
+ };
14
23
  export declare const TooltipContent: React.FC<TooltipContentProps>;
15
24
  export {};
@@ -4,29 +4,32 @@ import tooltip_module_default from "./tooltip.module.js";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import { Tooltip } from "@base-ui/react/tooltip";
6
6
  //#region src/components/tooltip/tooltip-content.tsx
7
- var TooltipContent = ({ children, sideOffset = 8, ...positionerProps }) => /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
8
- className: tooltip_module_default.Positioner,
9
- sideOffset,
10
- ...positionerProps,
11
- children: /* @__PURE__ */ jsx(Tooltip.Popup, {
12
- className: tooltip_module_default.Popup,
13
- role: "tooltip",
14
- render: /* @__PURE__ */ jsx(Surface, {
15
- color: 1,
16
- border: "all",
17
- radius: "medium",
18
- "data-vibrant": ""
19
- }),
20
- children: /* @__PURE__ */ jsx(Tooltip.Viewport, {
21
- className: tooltip_module_default.Viewport,
22
- render: /* @__PURE__ */ jsx(Stack, {
23
- vPadding: "small",
24
- hPadding: "small",
25
- fillChildren: false
7
+ var TooltipContent = ({ children, sideOffset = 8, container, ...positionerProps }) => /* @__PURE__ */ jsx(Tooltip.Portal, {
8
+ container,
9
+ children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
10
+ className: tooltip_module_default.Positioner,
11
+ sideOffset,
12
+ ...positionerProps,
13
+ children: /* @__PURE__ */ jsx(Tooltip.Popup, {
14
+ className: tooltip_module_default.Popup,
15
+ role: "tooltip",
16
+ render: /* @__PURE__ */ jsx(Surface, {
17
+ color: 1,
18
+ border: "all",
19
+ radius: "medium",
20
+ "data-vibrant": ""
26
21
  }),
27
- children
22
+ children: /* @__PURE__ */ jsx(Tooltip.Viewport, {
23
+ className: tooltip_module_default.Viewport,
24
+ render: /* @__PURE__ */ jsx(Stack, {
25
+ vPadding: "small",
26
+ hPadding: "small",
27
+ fillChildren: false
28
+ }),
29
+ children
30
+ })
28
31
  })
29
32
  })
30
- }) });
33
+ });
31
34
  //#endregion
32
35
  export { TooltipContent };
@@ -6,7 +6,8 @@
6
6
  "whenToUse": [
7
7
  "Short hints on hover or focus for toolbar controls and icon buttons",
8
8
  "Per-trigger labels on shared tooltip roots using `payload` and Root render-function children",
9
- "Adjacent controls that benefit from shared open/close delay via `Tooltip.Provider`"
9
+ "Adjacent controls that benefit from shared open/close delay via `Tooltip.Provider`",
10
+ "Custom portal host (iframe preview, shadow root, or app overlay root) via `container` on the portal-owning public part"
10
11
  ],
11
12
  "whenNotToUse": [
12
13
  "Rich interactive panels or forms (use Popover or Dialog)",
@@ -29,6 +30,11 @@
29
30
  "content-fade-duration": {
30
31
  "description": "content fade duration custom property on the component root."
31
32
  }
33
+ },
34
+ "react": {
35
+ "Content.container": {
36
+ "description": "Portal mount node (iframe body, shadow root, or app overlay root). Omit for Base UI default (`document.body` from the module window — iframe hosts must pass the iframe document body explicitly)."
37
+ }
32
38
  }
33
39
  }
34
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viraui/react",
3
- "version": "2026.0.13",
3
+ "version": "2026.0.14",
4
4
  "private": false,
5
5
  "files": [
6
6
  "dist"
@@ -35,7 +35,7 @@
35
35
  "unplugin-dts": "1.0.3",
36
36
  "vite": "8.1.4",
37
37
  "vite-plugin-static-copy": "4.1.1",
38
- "@viraui/foundation": "2026.0.13",
38
+ "@viraui/foundation": "2026.0.14",
39
39
  "@viraui/icons": "2026.0.0",
40
40
  "@viraui/postcss-config": "2026.0.0",
41
41
  "@viraui/tsconfig": "2026.0.0"