` element which will replace the default backdrop.
* The backdrop should have `aria-hidden="true"`.
*
+ * Accepts an `appearance` prop to control backdrop visibility:
+ * - `'dimmed'`: Always shows a dimmed backdrop, regardless of nesting.
+ * - `'transparent'`: Always shows a transparent backdrop.
+ *
+ * @example
+ * ```tsx
+ *
+ * ```
*/
- backdrop?: Slot<'div'>;
+ backdrop?: Slot
;
root: Slot<'div'>;
/**
* For more information refer to the [Motion docs page](https://react.fluentui.dev/?path=/docs/motion-motion-slot--docs).
@@ -44,7 +66,6 @@ export type DialogSurfaceState = ComponentState &
Pick & {
open?: boolean;
unmountOnClose?: boolean;
-
/**
* Transition status for animation.
* In test environment, this is always `undefined`.
@@ -52,4 +73,5 @@ export type DialogSurfaceState = ComponentState &
* @deprecated Will be always `undefined`.
*/
transitionStatus?: 'entering' | 'entered' | 'idle' | 'exiting' | 'exited' | 'unmounted';
+ backdropAppearance?: DialogBackdropSlotProps['appearance'];
};
diff --git a/packages/react-components/react-dialog/library/src/components/DialogSurface/index.ts b/packages/react-components/react-dialog/library/src/components/DialogSurface/index.ts
index 6de98e07cf8067..78aec16f608ca7 100644
--- a/packages/react-components/react-dialog/library/src/components/DialogSurface/index.ts
+++ b/packages/react-components/react-dialog/library/src/components/DialogSurface/index.ts
@@ -1,5 +1,6 @@
export { DialogSurface } from './DialogSurface';
export type {
+ DialogBackdropSlotProps,
DialogSurfaceContextValues,
DialogSurfaceElement,
DialogSurfaceProps,
diff --git a/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurface.ts b/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurface.ts
index df05129c64297f..008a8f0210a011 100644
--- a/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurface.ts
+++ b/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurface.ts
@@ -79,8 +79,12 @@ export const useDialogSurface_unstable = (
elementType: 'div',
});
+ const backdropAppearance = backdrop?.appearance;
+
if (backdrop) {
backdrop.onClick = handledBackdropClick;
+ // remove backdrop.appearance so it is not passed to the DOM
+ delete backdrop.appearance;
}
const { disableBodyScroll, enableBodyScroll } = useDisableBodyScroll();
@@ -109,6 +113,7 @@ export const useDialogSurface_unstable = (
open,
backdrop,
isNestedDialog,
+ backdropAppearance,
unmountOnClose,
mountNode: props.mountNode,
root: slot.always(
diff --git a/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts b/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts
index d3fed44c49bd20..053c7c4c4f2120 100644
--- a/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts
+++ b/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts
@@ -1,7 +1,6 @@
'use client';
import { makeResetStyles, makeStyles, mergeClasses } from '@griffel/react';
-import type { SlotClassNames } from '@fluentui/react-utilities';
import { tokens } from '@fluentui/react-theme';
import { createFocusOutlineStyle } from '@fluentui/react-tabster';
import {
@@ -12,6 +11,7 @@ import {
SURFACE_PADDING,
} from '../../contexts';
import type { DialogSurfaceSlots, DialogSurfaceState } from './DialogSurface.types';
+import type { SlotClassNames } from '@fluentui/react-utilities';
export const dialogSurfaceClassNames: SlotClassNames> = {
root: 'fui-DialogSurface',
@@ -82,11 +82,12 @@ const useStyles = makeStyles({
export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): DialogSurfaceState => {
'use no memo';
- const { isNestedDialog, root, backdrop, open, unmountOnClose } = state;
+ const { root, backdrop, open, unmountOnClose, isNestedDialog, backdropAppearance } = state;
const rootBaseStyle = useRootBaseStyle();
const backdropBaseStyle = useBackdropBaseStyle();
const styles = useStyles();
+ const isBackdropTransparent = backdropAppearance ? backdropAppearance === 'transparent' : isNestedDialog;
const mountedAndClosed = !unmountOnClose && !open;
@@ -101,10 +102,14 @@ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): Dial
backdrop.className = mergeClasses(
dialogSurfaceClassNames.backdrop,
backdropBaseStyle,
- isNestedDialog && styles.nestedDialogBackdrop,
mountedAndClosed && styles.dialogHidden,
+ isBackdropTransparent && styles.nestedDialogBackdrop,
backdrop.className,
);
+
+ if (backdrop?.appearance) {
+ delete backdrop.appearance;
+ }
}
return state;
diff --git a/packages/react-components/react-dialog/library/src/index.ts b/packages/react-components/react-dialog/library/src/index.ts
index b3f35c11aea049..2803b3603d3e1f 100644
--- a/packages/react-components/react-dialog/library/src/index.ts
+++ b/packages/react-components/react-dialog/library/src/index.ts
@@ -59,6 +59,7 @@ export {
renderDialogSurface_unstable,
} from './DialogSurface';
export type {
+ DialogBackdropSlotProps,
DialogSurfaceProps,
DialogSurfaceSlots,
DialogSurfaceState,
diff --git a/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.md b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.md
new file mode 100644
index 00000000000000..e163cfe3ff7c9d
--- /dev/null
+++ b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.md
@@ -0,0 +1,12 @@
+The `backdrop` slot on `DialogSurface` accepts an `appearance` prop that allows you to explicitly control the backdrop appearance of the dialog.
+
+By default, DialogSurface automatically determines the backdrop appearance based on context: standalone dialogs show a dimmed backdrop, while nested dialogs (inside another Dialog) show a transparent backdrop to avoid stacking multiple dimmed layers.
+
+Use `backdrop={{ appearance: "dimmed" }}` when rendering a Dialog inside components that internally use Dialog (like `OverlayDrawer`) but the dialog should visually behave as standalone with a dimmed backdrop.
+
+- **`'dimmed'`**: Always shows a dimmed backdrop, regardless of nesting.
+- **`'transparent'`**: Always shows a transparent backdrop.
+
+```tsx
+
+```
diff --git a/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx
new file mode 100644
index 00000000000000..12c531d7bfc3df
--- /dev/null
+++ b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx
@@ -0,0 +1,112 @@
+import * as React from 'react';
+import type { JSXElement } from '@fluentui/react-components';
+import {
+ Dialog,
+ DialogTrigger,
+ DialogSurface,
+ DialogTitle,
+ DialogBody,
+ DialogActions,
+ DialogContent,
+ OverlayDrawer,
+ DrawerBody,
+ DrawerHeader,
+ DrawerHeaderTitle,
+ Button,
+ Label,
+ RadioGroup,
+ Radio,
+ useId,
+ tokens,
+ makeStyles,
+} from '@fluentui/react-components';
+import { Dismiss24Regular } from '@fluentui/react-icons';
+import story from './DialogBackdropAppearance.md';
+
+const useStyles = makeStyles({
+ field: {
+ display: 'grid',
+ gridRowGap: tokens.spacingVerticalS,
+ marginBottom: tokens.spacingVerticalL,
+ },
+});
+
+type BackdropAppearanceOption = 'dimmed' | 'transparent';
+
+export const BackdropAppearance = (): JSXElement => {
+ const styles = useStyles();
+ const labelId = useId('backdrop-appearance-label');
+
+ const [drawerOpen, setDrawerOpen] = React.useState(false);
+ const [backdropAppearance, setBackdropAppearance] = React.useState();
+ const backdropProp = backdropAppearance ? { appearance: backdropAppearance } : undefined;
+
+ return (
+ <>
+
+
+ setDrawerOpen(open)}>
+
+ }
+ onClick={() => setDrawerOpen(false)}
+ />
+ }
+ >
+ Drawer
+
+
+
+
+
+
+ setBackdropAppearance(data.value as BackdropAppearanceOption)}
+ aria-labelledby={labelId}
+ >
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+BackdropAppearance.parameters = {
+ docs: {
+ description: {
+ story,
+ },
+ },
+};
diff --git a/packages/react-components/react-dialog/stories/src/Dialog/index.stories.tsx b/packages/react-components/react-dialog/stories/src/Dialog/index.stories.tsx
index defa29b465345c..08b5b71bd7ac70 100644
--- a/packages/react-components/react-dialog/stories/src/Dialog/index.stories.tsx
+++ b/packages/react-components/react-dialog/stories/src/Dialog/index.stories.tsx
@@ -8,6 +8,7 @@ import ssrMd from './DialogSSR.md';
export { Default } from './DialogDefault.stories';
export { NonModal } from './DialogNonModal.stories';
export { Alert } from './DialogAlert.stories';
+export { BackdropAppearance } from './DialogBackdropAppearance.stories';
export { ScrollingLongContent } from './DialogScrollingLongContent.stories';
export { KeepRenderedInTheDOM } from './DialogKeepRenderedInTheDOM.stories';
export { Actions } from './DialogActions.stories';