Repository: carbon-design-system/carbon
Pull request #18553: feat(modal): refactor to use native dialog element
Author: login: tay1orjones
Labels: name: status: ready to merge 🎉
Created: 2025-02-10T21:21:37Z | Merged: 2025-03-14T20:23:37Z

Closes #17086

<details><summary>Original implementation notes</summary>
<p>

#### Todo
- [x] Refactor Modal to use `Dialog` behind a flag, and do not use the new Dialog* components to prevent changes in classnames for consumers.
- [x] Refactor ComposedModal to use `Dialog` behind a flag, and do not use the new Dialog* components to prevent changes in classnames for consumers.

#### Concerns beyond the scope of this PR that were moved to https://github.com/carbon-design-system/carbon/issues/18243
- [ ] Determine if we should deprecate Modal and ComposedModal in favor of using Dialog directly 
- [ ] Assess a strategy of migration for classnames. e.g. If someone updates to dialog-based modals and has existing modal override styles, what could/should they do? Can we help?
  - [ ] Maybe Modal and ComposedModal should use their own existing styles and not use any new dialog-based components or classes, even when using dialog under the hood. Just put everything that already exists into the empty dialog box. The migration path for consumers would be to directly use Dialog moving forward, and update existing usages of Modal to use `<Dialog modal>`. We'll keep Modal and ComposedModal around until usage drops super low and then we could remove them in a future major.

</p>
</details> 

Huge thanks to @heloiselui for pairing up with me on this one 🎉 

#### Changelog

**New**

- Add `enableDialogElement` feature flag

**Changed**

- Refactor Modal to use the native `<dialog>` behind the `enableDialogElement` flag
- Refactor ComposedModal to use the native `<dialog>` behind the `enableDialogElement` flag
- Update documentation for both components
- Update storybook docs for the feature flags
- Clarify `enableExperimentalFocusWrapWithoutSentinels` has effectively been renamed/superseded by `enableDialogElement`
- Modified styles to conditionally emit only when the flag is not in use
  - Updated the modal story styles to bring this in under a custom selector and prefix to avoid style collisions

**Removed**

- `Dialog-story.js` is now `Dialog.stories.js`

#### Testing / Reviewing

- Review the unstable__Dialog stories, they're not fully baked but you can see how the base component works for modal and non modal 
- Review the Modal feature flag story and docs
- Review the ComposedModal feature flag story and docs
### ⚠️ BEFORE MERGE ⚠️ 
* we should uncomment the `includeStories: []` in Dialog.stories.js to ensure they're not shown in the storybook. We'll eventually show them but it would be something for #18243 or it's parent #13807 

Linked issue #17086: Refactor Modal to use native dialog behind a feature flag
In https://github.com/carbon-design-system/carbon/pull/15926 the internal-only/non-exported `Dialog` was refactored to use the native dialog element. This exploration validated it supports a few general use cases, one of them being Modal.

This issue is for refactoring Modal behind a feature flag to use this new `Dialog` element. `Dialog` will remain internal only and continue to not be exported for now. The parent #13807 will contain/organize the work to consider exporting `Dialog`.
Linked issue comments: user: login: tay1orjones
created_at: 2024-09-11T17:20:58Z
body: If Modal is just a Dialog, we might want to evaluate if there's any remaining value in having a `<Modal>` at all. The whole system could be re-centered on using `<Dialog>` directly. Layout helpers like `ModalHeader` becoming `DialogHeader` maybe. A new Dialog section could be added to the website, and we could move the existing Modal docs to be a subsection/variant of Dialog.

```
Dialog
 ├── Modal
 ├── Non-modal
 ├── Tearsheet?
 ├── SidePanel?
 └── ... etc?
```

There's likely lots of implications with this and much to consider, but it would more closely align the system with web platform standards/organization.
author_association: MEMBER
user: login: tay1orjones
created_at: 2025-02-07T16:45:22Z
body: 

<details><summary>Implementation notes</summary>
<p>

* In the Dialog story, if a non-modal dialog is open and you change the `modal` control to true, an error is thrown. `Failed to execute 'showModal' on 'HTMLDialogElement': The dialog is already open as a non-modal dialog, and therefore cannot be opened as a modal dialog.`
  * This may or may not be an issue - all internal usages are likely to just hardcode `modal` and Dialog may not actually be exported for direct use by consumers.
* In chrome's implementation of `dialog` it includes the address bar of the browser in the tab order. This is expected and aligns with `alert()` behavior. It should be documented. https://github.com/w3c/html/issues/1514#issuecomment-404097904

</p>
</details> 
author_association: MEMBER

Diff:
diff --git a/packages/feature-flags/feature-flags.yml b/packages/feature-flags/feature-flags.yml
index 078d8595cef1..a8da84a0d1ea 100644
--- a/packages/feature-flags/feature-flags.yml
+++ b/packages/feature-flags/feature-flags.yml
@@ -45,6 +45,10 @@ feature-flags:
     description: >
       Enable the new focus wrap behavior that doesn't use sentinel nodes
     enabled: false
+  - name: enable-dialog-element
+    description: >
+      Enable components to utilize the native dialog element
+    enabled: false
   - name: enable-v12-dynamic-floating-styles
     description: >
       Enable dynamic setting of floating styles for components like Popover,
diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
index 039798ab8544..e2d75d269b4e 100644
--- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
+++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
@@ -9933,6 +9933,9 @@ Map {
       "children": Object {
         "type": "node",
       },
+      "enableDialogElement": Object {
+        "type": "bool",
+      },
       "enableExperimentalFocusWrapWithoutSentinels": Object {
         "type": "bool",
       },
diff --git a/packages/react/scss/components/dialog/_dialog.scss b/packages/react/scss/components/dialog/_dialog.scss
new file mode 100644
index 000000000000..9b4348bb18fe
--- /dev/null
+++ b/packages/react/scss/components/dialog/_dialog.scss
@@ -0,0 +1,9 @@
+// Code generated by @carbon/react. DO NOT EDIT.
+//
+// Copyright IBM Corp. 2018, 2025
+//
+// This source code is licensed under the Apache-2.0 license found in the
+// LICENSE file in the root directory of this source tree.
+//
+
+@forward '@carbon/styles/scss/components/dialog/dialog';
diff --git a/packages/react/scss/components/dialog/_index.scss b/packages/react/scss/components/dialog/_index.scss
new file mode 100644
index 000000000000..c3c7b3bbe941
--- /dev/null
+++ b/packages/react/scss/components/dialog/_index.scss
@@ -0,0 +1,9 @@
+// Code generated by @carbon/react. DO NOT EDIT.
+//
+// Copyright IBM Corp. 2018, 2025
+//
+// This source code is licensed under the Apache-2.0 license found in the
+// LICENSE file in the root directory of this source tree.
+//
+
+@forward '@carbon/styles/scss/components/dialog';
diff --git a/packages/react/src/components/ComposedModal/ComposedModal.featureflag.mdx b/packages/react/src/components/ComposedModal/ComposedModal.featureflag.mdx
index 42358509d7aa..4b5db9f12cfa 100644
--- a/packages/react/src/components/ComposedModal/ComposedModal.featureflag.mdx
+++ b/packages/react/src/components/ComposedModal/ComposedModal.featureflag.mdx
@@ -10,22 +10,26 @@ import { Meta } from '@storybook/blocks';
 &nbsp;|&nbsp;
 [Accessibility](https://www.carbondesignsystem.com/components/modal/accessibility)
 
-## Experimental focus wrap without sentinels
+## `enable-dialog-element`
 
-`ComposedModal` supports the `enable-experimental-focus-wrap-without-sentinels`
-feature flag. This enables a new approach to the focus wrap behavior that
-modifies the DOM to no longer include hidden "sentinel nodes" used to mark the
-beginning and end of the wrapped focus. The new behavior looks at all
-interactive child nodes and wraps focus based on tabbable order of those nodes.
-The focus direction is determined whether `tab` is being pressed (forward) or
-`shift`+`tab` is being pressed (backwards). In javascript you can enable this
-feature flag to use the new focus wrap behavior.
+`ComposedModal` supports the `enable-dialog-element` feature flag. This enables
+a new approach internal to the ComposedModal that uses the native `<dialog>`
+element. With this, the browser natively controls the focus wrap behavior. This
+means that the DOM no longer includes the "sentinel nodes" previously needed for
+the ComposedModal to manage focus wrap.
+
+`ComposedModal` also supports the
+`enable-experimental-focus-wrap-without-sentinels` feature flag that was
+implemented previous to the dialog element refactor. In ComposedModal, this flag
+is an alias for the `enable-dialog-element` flag. They do the same thing and you
+only need to use one, preferably `enable-dialog-element`.
+
+## Enabling the flag
+
+To enable the flag, use the `FeatureFlags` component and set the prop:
 
 ```js
-<FeatureFlags
-  flags={{
-    'enable-experimental-focus-wrap-without-sentinels': true,
-  }}>
-  <ComposedModal />
+<FeatureFlags enableDialogElement>
+  <ComposedModal ... />
 </FeatureFlags>
 ```
diff --git a/packages/react/src/components/ComposedModal/ComposedModal.featureflag.stories.js b/packages/react/src/components/ComposedModal/ComposedModal.featureflag.stories.js
index 660963ca7790..ccde68533876 100644
--- a/packages/react/src/components/ComposedModal/ComposedModal.featureflag.stories.js
+++ b/packages/react/src/components/ComposedModal/ComposedModal.featureflag.stories.js
@@ -45,7 +45,7 @@ export default {
   ],
 };
 
-export const FocusWrapWithoutSentinels = (args) => {
+export const EnableDialogElement = (args) => {
   const [open, setOpen] = useState(true);
   return (
     <>
@@ -84,7 +84,7 @@ export const FocusWrapWithoutSentinels = (args) => {
   );
 };
 
-FocusWrapWithoutSentinels.argTypes = {
+EnableDialogElement.argTypes = {
   children: {
     table: {
       disable: true,
diff --git a/packages/react/src/components/ComposedModal/ComposedModal.tsx b/packages/react/src/components/ComposedModal/ComposedModal.tsx
index d2a067130d44..fe6fd4b3292e 100644
--- a/packages/react/src/components/ComposedModal/ComposedModal.tsx
+++ b/packages/react/src/components/ComposedModal/ComposedModal.tsx
@@ -25,13 +25,13 @@ import toggleClass from '../../tools/toggleClass';
 import requiredIfGivenPropIsTruthy from '../../prop-types/requiredIfGivenPropIsTruthy';
 import wrapFocus, {
   elementOrParentIsFloatingMenu,
-  wrapFocusWithoutSentinels,
 } from '../../internal/wrapFocus';
 import { usePrefix } from '../../internal/usePrefix';
 import { keys, match } from '../../internal/keyboard';
 import { useFeatureFlag } from '../FeatureFlags';
 import { composeEventHandlers } from '../../tools/events';
 import deprecate from '../../prop-types/deprecate';
+import { unstable__Dialog as Dialog } from '../Dialog/index';
 
 export interface ModalBodyProps extends HTMLAttributes<HTMLDivElement> {
   /** Specify the content to be placed in the ModalBody. */
@@ -260,14 +260,14 @@ const ComposedModal = React.forwardRef<HTMLDivElement, ComposedModalProps>(
     const endSentinel = useRef<HTMLButtonElement>(null);
     const onMouseDownTarget: MutableRefObject<Node | null> =
       useRef<Node | null>(null);
-    const focusTrapWithoutSentinels = useFeatureFlag(
-      'enable-experimental-focus-wrap-without-sentinels'
-    );
+    const enableDialogElement =
+      // useFeatureFlag('enable-experimental-focus-wrap-without-sentinels') ||
+      useFeatureFlag('enable-dialog-element');
 
     // Keep track of modal open/close state
     // and propagate it to the document.body
     useEffect(() => {
-      if (open !== wasOpen) {
+      if (!enableDialogElement && open !== wasOpen) {
         setIsOpen(!!open);
         setWasOpen(!!open);
         toggleClass(document.body, `${prefix}--body--with-modal-open`, !!open);
@@ -275,28 +275,19 @@ const ComposedModal = React.forwardRef<HTMLDivElement, ComposedModalProps>(
     }, [open, wasOpen, prefix]);
     // Remove the document.body className on unmount
     useEffect(() => {
-      return () => {
-        toggleClass(document.body, `${prefix}--body--with-modal-open`, false);
-      };
+      if (!enableDialogElement) {
+        return () => {
+          toggleClass(document.body, `${prefix}--body--with-modal-open`, false);
+        };
+      }
     }, []); // eslint-disable-line react-hooks/exhaustive-deps
 
     function handleKeyDown(event) {
-      event.stopPropagation();
-      if (match(event, keys.Escape)) {
-        closeModal(event);
-      }
-
-      if (
-        focusTrapWithoutSentinels &&
-        open &&
-        match(event, keys.Tab) &&
-        innerModal.current
-      ) {
-        wrapFocusWithoutSentinels({
-          containerNode: innerModal.current,
-          currentActiveNode: event.target,
-          event: event,
-        });
+      if (!enableDialogElement) {
+        event.stopPropagation();
+        if (match(event, keys.Escape)) {
+          closeModal(event);
+        }
       }
 
       onKeyDown?.(event);
@@ -400,7 +391,7 @@ const ComposedModal = React.forwardRef<HTMLDivElement, ComposedModalProps>(
     });
 
     useEffect(() => {
-      if (!open && launcherButtonRef) {
+      if (!enableDialogElement && !open && launcherButtonRef) {
         setTimeout(() => {
           launcherButtonRef?.current?.focus();
         });
@@ -408,37 +399,39 @@ const ComposedModal = React.forwardRef<HTMLDivElement, ComposedModalProps>(
     }, [open, launcherButtonRef]);
 
     useEffect(() => {
-      const initialFocus = (focusContainerElement) => {
-        const containerElement = focusContainerElement || innerModal.current;
-        const primaryFocusElement = containerElement
-          ? containerElement.querySelector(
-              danger ? `.${prefix}--btn--secondary` : selectorPrimaryFocus
-            )
-          : null;
-
-        if (primaryFocusElement) {
-          return primaryFocusElement;
-        }
-
-        return button && button.current;
-      };
-
-      const focusButton = (focusContainerElement) => {
-        const target = initialFocus(focusContainerElement);
+      if (!enableDialogElement) {
+        const initialFocus = (focusContainerElement) => {
+          const containerElement = focusContainerElement || innerModal.current;
+          const primaryFocusElement = containerElement
+            ? containerElement.querySelector(
+                danger ? `.${prefix}--btn--secondary` : selectorPrimaryFocus
+              )
+            : null;
+
+          if (primaryFocusElement) {
+            return primaryFocusElement;
+          }
+
+          return button && button.current;
+        };
+
+        const focusButton = (focusContainerElement) => {
+          const target = initialFocus(focusContainerElement);
+
+          const closeButton = focusContainerElement.querySelector(
+            `.${prefix}--modal-close`
+          );
 
-        const closeButton = focusContainerElement.querySelector(
-          `.${prefix}--modal-close`
-        );
+          if (target) {
+            target.focus();
+          } else if (!target && closeButton) {
+            closeButton?.focus();
+          }
+        };
 
-        if (target) {
-          target.focus();
-        } else if (!target && closeButton) {
-          closeButton?.focus();
+        if (open && isOpen) {
+          focusButton(innerModal.current);
         }
-      };
-
-      if (open && isOpen) {
-        focusButton(innerModal.current);
       }
     }, [open, selectorPrimaryFocus, isOpen]);
 
@@ -458,6 +451,62 @@ const ComposedModal = React.forwardRef<HTMLDivElement, ComposedModalProps>(
       );
     }
 
+    const modalBody = enableDialogElement ? (
+      <Dialog
+        open={open}
+        modal
+        className={containerClass}
+        aria-label={ariaLabel ? ariaLabel : generatedAriaLabel}
+        aria-labelledby={ariaLabelledBy}>
+        <div ref={innerModal} className={`${prefix}--modal-container-body`}>
+          {slug ? (
+            normalizedDecorator
+          ) : decorator ? (
+            <div className={`${prefix}--modal--inner__decorator`}>
+              {normalizedDecorator}
+            </div>
+          ) : (
+            ''
+          )}
+          {childrenWithProps}
+        </div>
+      </Dialog>
+    ) : (
+      <div
+        className={containerClass}
+        role="dialog"
+        aria-modal="true"
+        aria-label={ariaLabel ? ariaLabel : generatedAriaLabel}
+        aria-labelledby={ariaLabelledBy}>
+        {/* Non-translatable: Focus-wrap code makes this `<button>` not actually read by screen readers */}
+        <button
+          type="button"
+          ref={startSentinel}
+          className={`${prefix}--visually-hidden`}>
+          Focus sentinel
+        </button>
+        <div ref={innerModal} className={`${prefix}--modal-container-body`}>
+          {slug ? (
+            normalizedDecorator
+          ) : decorator ? (
+            <div className={`${prefix}--modal--inner__decorator`}>
+              {normalizedDecorator}
+            </div>
+          ) : (
+            ''
+          )}
+          {childrenWithProps}
+        </div>
+        {/* Non-translatable: Focus-wrap code makes this `<button>` not actually read by screen readers */}
+        <button
+          type="button"
+          ref={endSentinel}
+          className={`${prefix}--visually-hidden`}>
+          Focus sentinel
+        </button>
+      </div>
+    );
+
     return (
       <Layer
         {...rest}
@@ -465,7 +514,7 @@ const ComposedModal = React.forwardRef<HTMLDivElement, ComposedModalProps>(
         role="presentation"
         ref={ref}
         aria-hidden={!open}
-        onBlur={!focusTrapWithoutSentinels ? handleBlur : () => {}}
+        onBlur={!enableDialogElement ? handleBlur : () => {}}
         onClick={composeEventHandlers([rest?.onClick, handleOnClick])}
         onMouseDown={composeEventHandlers([
           rest?.onMouseDown,
@@ -473,43 +522,7 @@ const ComposedModal = React.forwardRef<HTMLDivElement, ComposedModalProps>(
         ])}
         onKeyDown={handleKeyDown}
         className={modalClass}>
-        <div
-          className={containerClass}
-          role="dialog"
-          aria-modal="true"
-          aria-label={ariaLabel ? ariaLabel : generatedAriaLabel}
-          aria-labelledby={ariaLabelledBy}>
-          {/* Non-translatable: Focus-wrap code makes this `<button>` not actually read by screen readers */}
-          {!focusTrapWithoutSentinels && (
-            <button
-              type="button"
-              ref={startSentinel}
-              className={`${prefix}--visually-hidden`}>
-              Focus sentinel
-            </button>
-          )}
-          <div ref={innerModal} className={`${prefix}--modal-container-body`}>
-            {slug ? (
-              normalizedDecorator
-            ) : decorator ? (
-              <div className={`${prefix}--modal--inner__decorator`}>
-                {normalizedDecorator}
-              </div>
-            ) : (
-              ''
-            )}
-            {childrenWithProps}
-          </div>
-          {/* Non-translatable: Focus-wrap code makes this `<button>` not actually read by screen readers */}
-          {!focusTrapWithoutSentinels && (
-            <button
-              type="button"
-              ref={endSentinel}
-              className={`${prefix}--visually-hidden`}>
-              Focus sentinel
-            </button>
-          )}
-        </div>
+        {modalBody}
       </Layer>
     );
   }
diff --git a/packages/react/src/components/Dialog/Dialog-story.js b/packages/react/src/components/Dialog/Dialog-story.js
deleted file mode 100644
index 655ca469e0a5..000000000000
--- a/packages/react/src/components/Dialog/Dialog-story.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * Copyright IBM Corp. 2016, 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/* eslint-disable storybook/story-exports */
-
-import React, { useEffect, useState } from 'react';
-import { unstable__Dialog as Dialog } from './';
-import Button from '../Button';
-import { action } from '@storybook/addon-actions';
-
-export default {
-  title: 'Experimental/unstable_Dialog',
-  component: Dialog,
-  includeStories: [],
-};
-
-export const Default = ({ open: _open, ...args }) => {
-  const [open, setOpen] = useState(_open);
-
-  const handleOpenDialog = () => {
-    setOpen(!open);
-  };
-
-  const closeAction = action('Close action');
-
-  const handleCloseEvent = () => {
-    closeAction();
-    // keep local state the same as the dialog otherwise open will
-    // need two clicks after a close
-    setOpen(false);
-  };
-
-  const handleCloseClick = () => {
-    setOpen(false);
-  };
-
-  useEffect(() => {
-    setOpen(_open);
-  }, [_open]);
-
-  return (
-    <div>
-      <Button type="button" onClick={handleOpenDialog}>
-        Toggle open
-      </Button>
-      <Dialog onRequestClose={handleCloseEvent} open={open} {...args}>
-        <p>
-          Elit hic at labore culpa itaque fugiat. Consequuntur iure autem autem
-          officiis dolores facilis nulla earum! Neque quia nemo sequi assumenda
-          ratione officia Voluptate beatae eligendi placeat nemo laborum,
-          ratione.
-        </p>
-        <br></br>
-        <p>
-          Elit hic at labore culpa itaque fugiat. Consequuntur iure autem autem
-          officiis dolores facilis nulla earum! Neque quia nemo sequi assumenda
-          ratione officia Voluptate beatae eligendi placeat nemo laborum,
-          ratione.
-        </p>
-        <br></br>
-        <p>
-          Elit hic at labore culpa itaque fugiat. Consequuntur iure autem autem
-          officiis dolores facilis nulla earum! Neque quia nemo sequi assumenda
-          ratione officia Voluptate beatae eligendi placeat nemo laborum,
-          ratione.
-        </p>
-        <br></br>
-        <Button
-          type="button"
-          kind="secondary"
-          onClick={handleCloseClick}
-          autofocus>
-          Close
-        </Button>
-      </Dialog>
-    </div>
-  );
-};
diff --git a/packages/react/src/components/Dialog/Dialog.mdx b/packages/react/src/components/Dialog/Dialog.mdx
new file mode 100644
index 000000000000..f0d7f95adfe1
--- /dev/null
+++ b/packages/react/src/components/Dialog/Dialog.mdx
@@ -0,0 +1,37 @@
+import { ArgTypes, Meta } from '@storybook/blocks';
+
+<Meta isTemplate />
+
+# dialog
+
+[Source code](https://github.com/carbon-design-system/carbon/tree/main/packages/react/src/components/Dialog)
+
+{/* <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> */}
+
+## Table of Contents
+
+- [Overview](#overview)
+- [Component API](#component-api)
+- [Feedback](#feedback)
+
+{/* <!-- END doctoc generated TOC please keep comment here to allow auto update --> */}
+
+## Overview
+
+The `Dialog` component is a primitive that wraps the native `<dialog>` and
+provides a composable api of related components that match the spec for Modals
+and non-modal dialogs as documented in the Dialog pattern.
+
+Chrome's built in focus wrap behavior includes the browser bar in the tab order.
+This is expected and consistent with `alert()` behavior.
+https://github.com/w3c/html/issues/1514#issuecomment-404097904
+
+## Component API
+
+<ArgTypes />
+
+## Feedback
+
+Help us improve this component by providing feedback, asking questions on Slack,
+or updating this file on
+[GitHub](https://github.com/carbon-design-system/carbon/edit/main/packages/react/src/components/Dialog/Dialog.mdx).
diff --git a/packages/react/src/components/Dialog/Dialog.stories.js b/packages/react/src/components/Dialog/Dialog.stories.js
new file mode 100644
index 000000000000..8596818c76c6
--- /dev/null
+++ b/packages/react/src/components/Dialog/Dialog.stories.js
@@ -0,0 +1,173 @@
+/**
+ * Copyright IBM Corp. 2016, 2025
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/* eslint-disable storybook/story-exports */
+
+import React, { useEffect, useState } from 'react';
+import {
+  unstable__Dialog as Dialog,
+  DialogHeader,
+  DialogControls,
+  DialogCloseButton,
+} from './';
+import Button from '../Button';
+import { action } from '@storybook/addon-actions';
+import mdx from './Dialog.mdx';
+
+export default {
+  title: 'Experimental/unstable_Dialog',
+  component: Dialog,
+  // includeStories: [],
+  parameters: {
+    docs: {
+      page: mdx,
+    },
+  },
+  argTypes: {
+    children: {
+      table: {
+        disable: true,
+      },
+    },
+    onRequestClose: {
+      table: {
+        disable: true,
+      },
+    },
+  },
+};
+
+export const Modal = ({ open: _open, ...args }) => {
+  const [open, setOpen] = useState(_open);
+
+  function toggleDialog() {
+    setOpen(!open);
+  }
+
+  function closeDialog(e) {
+    setOpen(false);
+  }
+
+  function handleRequestClose(e) {
+    action('Dialog onRequestClose');
+    closeDialog(e);
+  }
+
+  useEffect(() => {
+    setOpen(_open);
+  }, [_open]);
+
+  return (
+    <div>
+      <Button type="button" onClick={toggleDialog}>
+        Toggle open
+      </Button>
+      <Dialog {...args} open={open} onRequestClose={handleRequestClose}>
+        <DialogHeader>
+          {/* <DialogSubtitle></DialogSubtitle>
+          <DialogTitle></DialogTitle> */}
+          <DialogControls>
+            <DialogCloseButton onClick={closeDialog} />
+          </DialogControls>
+        </DialogHeader>
+        <p>
+          Elit hic at labore culpa itaque fugiat. Consequuntur iure autem autem
+          officiis dolores facilis nulla earum! Neque quia nemo sequi assumenda
+          ratione officia Voluptate beatae eligendi placeat nemo laborum,
+          ratione.
+        </p>
+        <br></br>
+        <p>
+          Elit hic at labore culpa itaque fugiat. Consequuntur iure autem autem
+          officiis dolores facilis nulla earum! Neque quia nemo sequi assumenda
+          ratione officia Voluptate beatae eligendi placeat nemo laborum,
+          ratione.
+        </p>
+        <br></br>
+        <p>
+          Elit hic at labore culpa itaque fugiat. Consequuntur iure autem autem
+          officiis dolores facilis nulla earum! Neque quia nemo sequi assumenda
+          ratione officia Voluptate beatae eligendi placeat nemo laborum,
+          ratione.
+        </p>
+        <br></br>
+        <Button type="button" kind="secondary" onClick={closeDialog} autoFocus>
+          Close
+        </Button>
+      </Dialog>
+    </div>
+  );
+};
+Modal.args = {
+  modal: true,
+  open: true,
+};
+
+export const NonModal = ({ open: _open, ...args }) => {
+  const [open, setOpen] = useState(_open);
+
+  function toggleDialog() {
+    setOpen(!open);
+  }
+
+  function closeDialog(e) {
+    setOpen(false);
+  }
+
+  function handleRequestClose(e) {
+    action('Dialog onRequestClose');
+    closeDialog(e);
+  }
+
+  useEffect(() => {
+    setOpen(_open);
+  }, [_open]);
+
+  return (
+    <div>
+      <Button type="button" onClick={toggleDialog}>
+        Toggle open
+      </Button>
+      <Dialog {...args} open={open} onRequestClose={handleRequestClose}>
+        <DialogHeader>
+          <p>TODO: Modal example</p>
+          <DialogControls>
+            <DialogCloseButton onClick={closeDialog} />
+          </DialogControls>
+        </DialogHeader>
+        <p>
+          Elit hic at labore culpa itaque fugiat. Consequuntur iure autem autem
+          officiis dolores facilis nulla earum! Neque quia nemo sequi assumenda
+          ratione officia Voluptate beatae eligendi placeat nemo laborum,
+          ratione.
+        </p>
+        <br></br>
+        <p>
+          Elit hic at labore culpa itaque fugiat. Consequuntur iure autem autem
+          officiis dolores facilis nulla earum! Neque quia nemo sequi assumenda
+          ratione officia Voluptate beatae eligendi placeat nemo laborum,
+          ratione.
+        </p>
+        <br></br>
+        <p>
+          Elit hic at labore culpa itaque fugiat. Consequuntur iure autem autem
+          officiis dolores facilis nulla earum! Neque quia nemo sequi assumenda
+          ratione officia Voluptate beatae eligendi placeat nemo laborum,
+          ratione.
+        </p>
+        <br></br>
+        <Button type="button" kind="secondary" onClick={closeDialog} autoFocus>
+          Close
+        </Button>
+      </Dialog>
+    </div>
+  );
+};
+NonModal.args = {
+  modal: false,
+  open: true,
+};
diff --git a/packages/react/src/components/Dialog/index.tsx b/packages/react/src/components/Dialog/index.tsx
index d8e0bcdb0e9a..55efd6143751 100644
--- a/packages/react/src/components/Dialog/index.tsx
+++ b/packages/react/src/components/Dialog/index.tsx
@@ -21,10 +21,33 @@ export interface DialogProps extends ReactAttr<HTMLDialogElement> {
   children?: React.ReactNode;
 
   /**
-   * Specifies whether the dialog is modal or non-modal
+   * Specify an optional className to be applied to the modal root node
+   */
+  className?: string;
+
+  /**
+   * Specifies whether the dialog is modal or non-modal. This cannot be changed
+   * while open=true
    */
   modal?: boolean;
 
+  /**
+   * Specify a handler for the dialog's cancel event.
+   * The browser fires this event when the user presses the Esc key and is cancelable.
+   */
+  onCancel?: React.ReactEventHandler<HTMLDialogElement>;
+
+  /**
+   * Specify a click handler applied to the dialog.
+   */
+  onClick?: React.ReactEventHandler<HTMLDialogElement>;
+
+  /**
+   * Specify a handler the dialog's close event.
+   * The browser fires this event after the dialog has closed.
+   */
+  onClose?: React.ReactEventHandler<HTMLDialogElement>;
+
   /**
    * Specify a handler for closing Dialog.
    * The handler should care of closing Dialog, e.g. changing `open` prop.
@@ -37,84 +60,83 @@ export interface DialogProps extends ReactAttr<HTMLDialogElement> {
   open?: boolean;
 }
 
-const unstable__Dialog = React.forwardRef(
+export const unstable__Dialog = React.forwardRef(
   (
     {
       children,
+      className,
       modal,
+      onCancel = noopFn,
+      onClick = noopFn,
+      onClose = noopFn,
       onRequestClose = noopFn,
       open = false,
       ...rest
     }: DialogProps,
-    ref
+    forwardRef
   ) => {
-    const backupRef = useRef(null);
-    const localRef = (ref ?? backupRef) as MutableRefObject<HTMLDialogElement>;
-
     const prefix = usePrefix();
 
-    const handleClose = (ev) => {
-      if (onRequestClose) {
-        onRequestClose(ev);
+    // This component needs access to a ref, placed on the dialog, to call the
+    // various imperative dialog functions (show(), close(), etc.).
+    // If the parent component has not passed a ref for forwardRef, forwardRef
+    // will be null. A "backup" ref is needed to ensure the dialog's instance
+    // methods can always be called within this component.
+    const backupRef = useRef(null);
+    const ref = (forwardRef ??
+      backupRef) as MutableRefObject<HTMLDialogElement>;
+
+    // Clicks on the backdrop of an open modal dialog should request the consuming component to close
+    // the dialog. Clicks elsewhere, or on non-modal dialogs should not request
+    // to close the dialog.
+    function handleModalBackdropClick(e) {
+      if (open && modal && e.target === ref.current) {
+        onRequestClose(e);
       }
-    };
+    }
 
-    const handleCancel = () => {
-      localRef.current?.close();
-    };
+    function handleClick(e) {
+      handleModalBackdropClick(e);
 
-    const handleBackdropClick = (ev) => {
-      if (ev.target === localRef.current) {
-        localRef.current?.close();
-      }
-    };
+      // onClick should always be called, no matter if the target is a modal
+      // dialog, modal dialog backdrop, or non-modal dialog.
+      onClick(e);
+    }
 
     useEffect(() => {
-      if (localRef.current) {
+      if (ref.current) {
         if (open) {
           if (modal) {
-            localRef.current.showModal();
+            // Display the dialog as a modal, over the top of any other dialogs
+            // that might be present. Everything outside the dialog are inert
+            // with interactions outside the dialog being blocked.
+            ref.current.showModal();
           } else {
-            localRef.current.open = true;
+            // Display the dialog modelessly, i.e. still allowing interaction
+            // with content outside of the dialog.
+            ref.current.show();
           }
         } else {
-          localRef.current.close();
+          ref.current.close();
         }
       }
-    }, [localRef, modal, open]);
+    }, [modal, open]);
 
     return (
-      // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
       <dialog
         {...rest}
-        className={cx(`${prefix}--dialog`, {
-          [`${prefix}--dialog--modal`]: modal,
-        })}
-        ref={localRef}
-        onCancel={handleCancel}
-        onClick={handleBackdropClick}
-        onClose={handleClose}>
-        <div className={`${prefix}--dialog__header`}>
-          <div className={`${prefix}--dialog__header-controls`}>
-            <IconButton
-              kind="ghost"
-              className={`${prefix}--dialog__close`}
-              label="Close"
-              onClick={handleCancel}
-              title="Close"
-              aria-label="Close"
-              align="left">
-              <Close
-                size={20}
-                aria-hidden="true"
-                tabIndex="-1"
-                className={`${prefix}--icon__close`}
-              />
-            </IconButton>
-          </div>
-        </div>
-
-        <div className={`${prefix}--dialog__content`}>{children}</div>
+        className={cx(
+          `${prefix}--dialog`,
+          {
+            [`${prefix}--dialog--modal`]: modal,
+          },
+          className
+        )}
+        ref={ref}
+        onCancel={onCancel}
+        onClick={handleClick}
+        onClose={onClose}>
+        {children}
       </dialog>
     );
   }
@@ -129,7 +151,13 @@ unstable__Dialog.propTypes = {
   children: PropTypes.node,
 
   /**
-   * Modal specifies whether the Dialog is modal or non-modal
+   * Specify an optional className to be applied to the modal root node
+   */
+  className: PropTypes.string,
+
+  /**
+   * Modal specifies whether the Dialog is modal or non-modal. This cannot be
+   * changed while open=true
    */
   modal: PropTypes.bool,
 
@@ -145,5 +173,89 @@ unstable__Dialog.propTypes = {
   open: PropTypes.bool,
 };
 
-export { unstable__Dialog };
-export default unstable__Dialog;
+export interface DialogHeaderProps extends ReactAttr<HTMLDivElement> {
+  /**
+   * Provide the contents to be rendered inside of this component
+   */
+  children?: React.ReactNode;
+}
+export const DialogHeader = React.forwardRef<HTMLDivElement, DialogHeaderProps>(
+  ({ children, ...rest }, ref) => {
+    const prefix = usePrefix();
+    return (
+      <div className={`${prefix}--dialog__header`} ref={ref} {...rest}>
+        {children}
+      </div>
+    );
+  }
+);
+DialogHeader.propTypes = {
+  /**
+   * Provide the contents to be rendered inside of this component
+   */
+  children: PropTypes.node,
+};
+
+export interface DialogControlsProps extends ReactAttr<HTMLDivElement> {
+  /**
+   * Provide the contents to be rendered inside of this component
+   */
+  children?: React.ReactNode;
+}
+export const DialogControls = React.forwardRef<
+  HTMLDivElement,
+  DialogControlsProps
+>(({ children, ...rest }, ref) => {
+  const prefix = usePrefix();
+  return (
+    // @ts-ignore
+    <div className={`${prefix}--dialog__header-controls`} ref={ref} {...rest}>
+      {children}
+    </div>
+  );
+});
+DialogControls.propTypes = {
+  /**
+   * Provide children to be rendered inside of this component
+   */
+  children: PropTypes.node,
+};
+
+export interface DialogCloseButtonProps extends ReactAttr<HTMLDivElement> {
+  /**
+   * Specify a click handler applied to the IconButton
+   */
+  onClick?: React.MouseEventHandler;
+}
+export const DialogCloseButton = React.forwardRef<
+  HTMLDivElement,
+  DialogCloseButtonProps
+>(({ onClick, ...rest }, ref) => {
+  const prefix = usePrefix();
+  return (
+    // @ts-ignore
+    <IconButton
+      kind="ghost"
+      className={`${prefix}--dialog__close`}
+      label="Close"
+      title="Close"
+      aria-label="Close"
+      align="left"
+      onClick={onClick}
+      {...rest}>
+      <Close
+        size={20}
+        aria-hidden="true"
+        tabIndex="-1"
+        className={`${prefix}--icon__close`}
+      />
+    </IconButton>
+  );
+});
+
+DialogCloseButton.propTypes = {
+  /**
+   * Specify a click handler applied to the IconButton
+   */
+  onClick: PropTypes.func,
+};
diff --git a/packages/react/src/components/FeatureFlags/index.tsx b/packages/react/src/components/FeatureFlags/index.tsx
index 8fb3728b05ad..5e5a7cb6d011 100644
--- a/packages/react/src/components/FeatureFlags/index.tsx
+++ b/packages/react/src/components/FeatureFlags/index.tsx
@@ -28,6 +28,7 @@ export interface FeatureFlagsProps {
   enableV12Overflowmenu?: boolean;
   enableTreeviewControllable?: boolean;
   enableExperimentalFocusWrapWithoutSentinels?: boolean;
+  enableDialogElement?: boolean;
   enableV12DynamicFloatingStyles?: boolean;
 }
 /**
@@ -49,6 +50,7 @@ function FeatureFlags({
   enableV12Overflowmenu = false,
   enableTreeviewControllable = false,
   enableExperimentalFocusWrapWithoutSentinels = false,
+  enableDialogElement = false,
   enableV12DynamicFloatingStyles = false,
 }: FeatureFlagsProps): JSX.Element {
   const parentScope = useContext(FeatureFlagContext);
@@ -61,6 +63,7 @@ function FeatureFlags({
     'enable-treeview-controllable': enableTreeviewControllable,
     'enable-experimental-focus-wrap-without-sentinels':
       enableExperimentalFocusWrapWithoutSentinels,
+    'enable-dialog-element': enableDialogElement,
     'enable-v12-dynamic-floating-styles': enableV12DynamicFloatingStyles,
     ...flags,
   };
@@ -110,6 +113,7 @@ FeatureFlags.propTypes = {
   enableV12Overflowmenu: PropTypes.bool,
   enableTreeviewControllable: PropTypes.bool,
   enableExperimentalFocusWrapWithoutSentinels: PropTypes.bool,
+  enableDialogElement: PropTypes.bool,
   enableV12DynamicFloatingStyles: PropTypes.bool,
 };
 
diff --git a/packages/react/src/components/FeatureFlags/overview.mdx b/packages/react/src/components/FeatureFlags/overview.mdx
index 298b81e061ab..f26399e96b60 100644
--- a/packages/react/src/components/FeatureFlags/overview.mdx
+++ b/packages/react/src/components/FeatureFlags/overview.mdx
@@ -48,6 +48,7 @@ in the Carbon monorepo.
 | -------------------------------------------------- | ------------------------------------------------------------------------------------ | ------- | --------------- | --------- |
 | `enable-experimental-tile-contrast`                | Enable the improved styling for tiles that provides better contrast                  | `false` |                 | ✅        |
 | `enable-experimental-focus-wrap-without-sentinels` | Enable the new focus wrap behavior that doesn't use sentinel nodes                   | `false` | ✅              |           |
+| `enable-dialog-element`                            | Enable components to utilize the native dialog element                               | `false` | ✅              | ✅        |
 | `enable-treeview-controllable`                     | Enable the new TreeView controllable API                                             | `false` | ✅              |           |
 | `enable-v12-tile-default-icons`                    | Enable default icons for Tile components                                             | `false` | ✅              |           |
 | `enable-v12-overflowmenu`                          | Enable the use of the v12 OverflowMenu leveraging the Menu subcomponents             | `false` | ✅              |           |
diff --git a/packages/react/src/components/Modal/Modal.featureflag.mdx b/packages/react/src/components/Modal/Modal.featureflag.mdx
index c7e5be3c5577..ba06253f9e6e 100644
--- a/packages/react/src/components/Modal/Modal.featureflag.mdx
+++ b/packages/react/src/components/Modal/Modal.featureflag.mdx
@@ -10,22 +10,25 @@ import { Meta } from '@storybook/blocks';
 &nbsp;|&nbsp;
 [Accessibility](https://www.carbondesignsystem.com/components/modal/accessibility)
 
-## Experimental focus wrap without sentinels
+## `enable-dialog-element`
 
-`Modal` supports the `enable-experimental-focus-wrap-without-sentinels` feature
-flag. This enables a new approach to the focus wrap behavior that modifies the
-DOM to no longer include hidden "sentinel nodes" used to mark the beginning and
-end of the wrapped focus. The new behavior looks at all interactive child nodes
-and wraps focus based on tabbable order of those nodes. The focus direction is
-determined whether `tab` is being pressed (forward) or `shift`+`tab` is being
-pressed (backwards). In javascript you can enable this feature flag to use the
-new focus wrap behavior.
+`Modal` supports the `enable-dialog-element` feature flag. This enables a new
+approach internal to the Modal that uses the native `<dialog>` element. With
+this, the browser natively controls the focus wrap behavior. This means that the
+DOM no longer includes the "sentinel nodes" previously needed for the Modal to
+manage focus wrap.
+
+`Modal` also supports the `enable-experimental-focus-wrap-without-sentinels`
+feature flag that was implemented previous to the dialog element refactor. In
+Modal, this flag is an alias for the `enable-dialog-element` flag. They do the
+same thing and you only need to use one, preferably `enable-dialog-element`.
+
+## Enabling the flag
+
+To enable the flag, use the `FeatureFlags` component and set the prop:
 
 ```js
-<FeatureFlags
-  flags={{
-    'enable-experimental-focus-wrap-without-sentinels': true,
-  }}>
-  <Modal />
+<FeatureFlags enableDialogElement>
+  <Modal ... />
 </FeatureFlags>
 ```
diff --git a/packages/react/src/components/Modal/Modal.featureflag.stories.js b/packages/react/src/components/Modal/Modal.featureflag.stories.js
index 32043d7eb068..e7306e36c753 100644
--- a/packages/react/src/components/Modal/Modal.featureflag.stories.js
+++ b/packages/react/src/components/Modal/Modal.featureflag.stories.js
@@ -6,14 +6,16 @@
  */
 
 import React, { useState } from 'react';
-import Modal from './Modal';
+import Modal from './';
 import Button from '../Button';
 import Select from '../Select';
 import MultiSelect from '../MultiSelect';
 import Dropdown from '../Dropdown';
 import SelectItem from '../SelectItem';
 import TextInput from '../TextInput';
+import { ClassPrefix } from '../ClassPrefix';
 import { WithFeatureFlags } from '../../../.storybook/templates/WithFeatureFlags';
+import './Modal.stories.scss';
 
 export default {
   title: 'Components/Modal/Feature Flag',
@@ -28,60 +30,64 @@ export default {
   ],
 };
 
-export const FocusWrapWithoutSentinel = () => {
-  const [open, setOpen] = useState(true);
+export const DialogRefactor = () => {
+  const [open, setOpen] = useState(false);
   return (
     <>
       <Button onClick={() => setOpen(true)}>Launch modal</Button>
-      <Modal
-        open={open}
-        onRequestClose={() => setOpen(false)}
-        modalHeading="Add a custom domain"
-        modalLabel="Account resources"
-        primaryButtonText="Add"
-        secondaryButtonText="Cancel">
-        <p style={{ marginBottom: '1rem' }}>
-          Custom domains direct requests for your apps in this Cloud Foundry
-          organization to a URL that you own. A custom domain can be a shared
-          domain, a shared subdomain, or a shared domain and host.
-        </p>
-        <TextInput
-          data-modal-primary-focus
-          id="text-input-1"
-          labelText="Domain name"
-          placeholder="e.g. github.com"
-          style={{ marginBottom: '1rem' }}
-        />
-        <Select id="select-1" defaultValue="us-south" labelText="Region">
-          <SelectItem value="us-south" text="US South" />
-          <SelectItem value="us-east" text="US East" />
-        </Select>
-        <Dropdown
-          id="drop"
-          label="Dropdown"
-          titleText="Dropdown"
-          items={[
-            { id: 'one', label: 'one', name: 'one' },
-            { id: 'two', label: 'two', name: 'two' },
-          ]}
-        />
-        <MultiSelect
-          id="test"
-          label="Multiselect"
-          titleText="Multiselect"
-          items={[
-            {
-              id: 'downshift-1-item-0',
-              text: 'Option 1',
-            },
-            {
-              id: 'downshift-1-item-1',
-              text: 'Option 2',
-            },
-          ]}
-          itemToString={(item) => (item ? item.text : '')}
-        />
-      </Modal>
+      <ClassPrefix prefix="dialog-refactor">
+        <div className="experimental-modal">
+          <Modal
+            open={open}
+            onRequestClose={() => setOpen(false)}
+            modalHeading="Add a custom domain"
+            modalLabel="Account resources"
+            primaryButtonText="Add"
+            secondaryButtonText="Cancel">
+            <p style={{ marginBottom: '1rem' }}>
+              Custom domains direct requests for your apps in this Cloud Foundry
+              organization to a URL that you own. A custom domain can be a
+              shared domain, a shared subdomain, or a shared domain and host.
+            </p>
+            <TextInput
+              autofocus="true"
+              id="text-input-1"
+              labelText="Domain name"
+              placeholder="e.g. github.com"
+              style={{ marginBottom: '1rem' }}
+            />
+            <Select id="select-1" defaultValue="us-south" labelText="Region">
+              <SelectItem value="us-south" text="US South" />
+              <SelectItem value="us-east" text="US East" />
+            </Select>
+            <Dropdown
+              id="drop"
+              label="Dropdown"
+              titleText="Dropdown"
+              items={[
+                { id: 'one', label: 'one', name: 'one' },
+                { id: 'two', label: 'two', name: 'two' },
+              ]}
+            />
+            <MultiSelect
+              id="test"
+              label="Multiselect"
+              titleText="Multiselect"
+              items={[
+                {
+                  id: 'downshift-1-item-0',
+                  text: 'Option 1',
+                },
+                {
+                  id: 'downshift-1-item-1',
+                  text: 'Option 2',
+                },
+              ]}
+              itemToString={(item) => (item ? item.text : '')}
+            />
+          </Modal>
+        </div>
+      </ClassPrefix>
     </>
   );
 };
diff --git a/packages/react/src/components/Modal/Modal.stories.scss b/packages/react/src/components/Modal/Modal.stories.scss
index 65fbd148f813..4e0a7c3a0d92 100644
--- a/packages/react/src/components/Modal/Modal.stories.scss
+++ b/packages/react/src/components/Modal/Modal.stories.scss
@@ -1,3 +1,25 @@
+@use '@carbon/react/scss/config' with (
+  $prefix: 'dialog-refactor'
+);
+@use '@carbon/react/scss/components/modal/modal';
+@use '@carbon/react/scss/components/dialog/dialog';
+@use '@carbon/react/scss/components/text-input/text-input';
+@use '@carbon/react/scss/components/select/select';
+@use '@carbon/react/scss/components/dropdown/dropdown';
+@use '@carbon/react/scss/components/multiselect/multiselect';
+
+.experimental-modal {
+  @include modal.modal(
+    $enable-experimental-focus-wrap-without-sentinels: true,
+    $enable-dialog-element: true
+  );
+  @include dialog.dialog();
+  @include text-input.text-input();
+  @include select.select();
+  @include dropdown.dropdown();
+  @include multiselect.multiselect();
+}
+
 .sb-notification p {
   line-height: 1;
 }
diff --git a/packages/react/src/components/Modal/Modal.tsx b/packages/react/src/components/Modal/Modal.tsx
index 1759179473ba..2bccf76829e4 100644
--- a/packages/react/src/components/Modal/Modal.tsx
+++ b/packages/react/src/components/Modal/Modal.tsx
@@ -22,7 +22,6 @@ import InlineLoading from '../InlineLoading';
 import { Layer } from '../Layer';
 import requiredIfGivenPropIsTruthy from '../../prop-types/requiredIfGivenPropIsTruthy';
 import wrapFocus, {
-  wrapFocusWithoutSentinels,
   elementOrParentIsFloatingMenu,
 } from '../../internal/wrapFocus';
 import { debounce } from 'es-toolkit/compat';
@@ -38,6 +37,8 @@ import { InlineLoadingStatus } from '../InlineLoading/InlineLoading';
 import { useFeatureFlag } from '../FeatureFlags';
 import { composeEventHandlers } from '../../tools/events';
 import deprecate from '../../prop-types/deprecate';
+import { unstable__Dialog as Dialog } from '../Dialog/index';
+import { enable } from '@carbon/feature-flags';
 
 export const ModalSizes = ['xs', 'sm', 'md', 'lg'] as const;
 
@@ -285,9 +286,10 @@ const Modal = React.forwardRef(function Modal(
     [`${prefix}--btn--loading`]: loadingStatus !== 'inactive',
   });
   const loadingActive = loadingStatus !== 'inactive';
-  const focusTrapWithoutSentinels = useFeatureFlag(
-    'enable-experimental-focus-wrap-without-sentinels'
-  );
+
+  const enableDialogElement =
+    useFeatureFlag('enable-experimental-focus-wrap-without-sentinels') ||
+    useFeatureFlag('enable-dialog-element');
 
   function isCloseButton(element: Element) {
     return (
@@ -310,18 +312,6 @@ const Modal = React.forwardRef(function Modal(
       ) {
         onRequestSubmit(evt);
       }
-
-      if (
-        focusTrapWithoutSentinels &&
-        match(evt, keys.Tab) &&
-        innerModal.current
-      ) {
-        wrapFocusWithoutSentinels({
-          containerNode: innerModal.current,
-          currentActiveNode: evt.target,
-          event: evt as any,
-        });
-      }
     }
   }
 
@@ -417,55 +407,61 @@ const Modal = React.forwardRef(function Modal(
 
   useEffect(() => {
     return () => {
-      toggleClass(document.body, `${prefix}--body--with-modal-open`, false);
+      if (!enableDialogElement) {
+        toggleClass(document.body, `${prefix}--body--with-modal-open`, false);
+      }
     };
-  }, [prefix]);
+  }, [prefix, enableDialogElement]);
 
   useEffect(() => {
-    toggleClass(
-      document.body,
-      `${prefix}--body--with-modal-open`,
-      open ?? false
-    );
-  }, [open, prefix]);
+    if (!enableDialogElement) {
+      toggleClass(
+        document.body,
+        `${prefix}--body--with-modal-open`,
+        open ?? false
+      );
+    }
+  }, [open, prefix, enableDialogElement]);
 
   useEffect(() => {
-    if (!open && launcherButtonRef) {
+    if (!enableDialogElement && !open && launcherButtonRef) {
       setTimeout(() => {
         if ('current' in launcherButtonRef) {
           launcherButtonRef.current?.focus();
         }
       });
     }
-  }, [open, launcherButtonRef]);
+  }, [open, launcherButtonRef, enableDialogElement]);
 
   useEffect(() => {
-    const initialFocus = (focusContainerElement: HTMLElement | null) => {
-      const containerElement = focusContainerElement || innerModal.current;
-      const primaryFocusElement = containerElement
-        ? containerElement.querySelector<HTMLElement | SVGElement>(
-            danger ? `.${prefix}--btn--secondary` : selectorPrimaryFocus
-          )
-        : null;
-
-      if (primaryFocusElement) {
-        return primaryFocusElement;
-      }
+    if (!enableDialogElement) {
+      const initialFocus = (focusContainerElement: HTMLElement | null) => {
+        const containerElement = focusContainerElement || innerModal.current;
+        const primaryFocusElement = containerElement
+          ? containerElement.querySelector<HTMLElement | SVGElement>(
+              danger ? `.${prefix}--btn--secondary` : selectorPrimaryFocus
+            )
+          : null;
+
+        if (primaryFocusElement) {
+          return primaryFocusElement;
+        }
 
-      return button && button.current;
-    };
+        return button && button.current;
+      };
 
-    const focusButton = (focusContainerElement: HTMLElement | null) => {
-      const target = initialFocus(focusContainerElement);
-      if (target !== null) {
-        target.focus();
-      }
-    };
+      const focusButton = (focusContainerElement: HTMLElement | null) => {
+        const target = initialFocus(focusContainerElement);
+        if (target !== null) {
+          target.focus();
+        }
+      };
 
-    if (open) {
-      focusButton(innerModal.current);
+      if (open) {
+        focusButton(innerModal.current);
+      }
     }
-  }, [open, selectorPrimaryFocus, danger, prefix]);
+  }, [open, selectorPrimaryFocus, danger, prefix, enableDialogElement]);
 
   useIsomorphicEffect(() => {
     if (contentRef.current) {
@@ -525,17 +521,20 @@ const Modal = React.forwardRef(function Modal(
     </div>
   );
 
-  const modalBody = (
-    <div
+  // alertdialog is the only permitted aria role for a native dialog element
+  // https://www.w3.org/TR/html-aria/#docconformance:~:text=Role%3A-,alertdialog,-.%20(dialog%20is
+  const isAlertDialog = alert && !passiveModal;
+
+  const modalBody = enableDialogElement ? (
+    <Dialog
+      open={open}
+      modal
       ref={innerModal}
-      role="dialog"
-      {...alertDialogProps}
+      role={isAlertDialog ? 'alertdialog' : ''}
+      aria-describedby={isAlertDialog ? modalBodyId : ''}
       className={containerClasses}
-      aria-label={ariaLabel}
-      aria-modal="true"
-      tabIndex={-1}>
+      aria-label={ariaLabel}>
       <div className={`${prefix}--modal-header`}>
-        {passiveModal && modalButton}
         {modalLabel && (
           <Text
             as="h2"
@@ -550,16 +549,29 @@ const Modal = React.forwardRef(function Modal(
           className={`${prefix}--modal-header__heading`}>
           {modalHeading}
         </Text>
-        {slug ? (
-          normalizedDecorator
-        ) : decorator ? (
+        {decorator ? (
           <div className={`${prefix}--modal--inner__decorator`}>
             {normalizedDecorator}
           </div>
         ) : (
           ''
         )}
-        {!passiveModal && modalButton}
+        <div className={`${prefix}--modal-close-button`}>
+          <IconButton
+            className={modalCloseButtonClass}
+            label={closeButtonLabel}
+            onClick={onRequestClose}
+            aria-label={closeButtonLabel}
+            align="left"
+            ref={button}>
+            <Close
+              size={20}
+              aria-hidden="true"
+              tabIndex="-1"
+              className={`${modalCloseButtonClass}__icon`}
+            />
+          </IconButton>
+        </div>
       </div>
       <Layer
         ref={contentRef}
@@ -610,21 +622,11 @@ const Modal = React.forwardRef(function Modal(
           </Button>
         </ButtonSet>
       )}
-    </div>
-  );
-
-  return (
-    <Layer
-      {...rest}
-      level={0}
-      onKeyDown={handleKeyDown}
-      onClick={composeEventHandlers([rest?.onClick, handleOnClick])}
-      onBlur={!focusTrapWithoutSentinels ? handleBlur : () => {}}
-      className={modalClasses}
-      role="presentation"
-      ref={ref}>
+    </Dialog>
+  ) : (
+    <>
       {/* Non-translatable: Focus-wrap code makes this `<span>` not actually read by screen readers */}
-      {!focusTrapWithoutSentinels && (
+      {!enableDialogElement && (
         <span
           ref={startTrap}
           tabIndex={0}
@@ -633,9 +635,93 @@ const Modal = React.forwardRef(function Modal(
           Focus sentinel
         </span>
       )}
-      {modalBody}
+      <div
+        ref={innerModal}
+        role="dialog"
+        {...alertDialogProps}
+        className={containerClasses}
+        aria-label={ariaLabel}
+        aria-modal="true"
+        tabIndex={-1}>
+        <div className={`${prefix}--modal-header`}>
+          {passiveModal && modalButton}
+          {modalLabel && (
+            <Text
+              as="h2"
+              id={modalLabelId}
+              className={`${prefix}--modal-header__label`}>
+              {modalLabel}
+            </Text>
+          )}
+          <Text
+            as="h2"
+            id={modalHeadingId}
+            className={`${prefix}--modal-header__heading`}>
+            {modalHeading}
+          </Text>
+          {slug ? (
+            normalizedDecorator
+          ) : decorator ? (
+            <div className={`${prefix}--modal--inner__decorator`}>
+              {normalizedDecorator}
+            </div>
+          ) : (
+            ''
+          )}
+          {!passiveModal && modalButton}
+        </div>
+        <Layer
+          ref={contentRef}
+          id={modalBodyId}
+          className={contentClasses}
+          {...hasScrollingContentProps}>
+          {children}
+        </Layer>
+        {!passiveModal && (
+          <ButtonSet className={footerClasses} aria-busy={loadingActive}>
+            {Array.isArray(secondaryButtons) && secondaryButtons.length <= 2
+              ? secondaryButtons.map(
+                  ({ buttonText, onClick: onButtonClick }, i) => (
+                    <Button
+                      key={`${buttonText}-${i}`}
+                      kind="secondary"
+                      onClick={onButtonClick}>
+                      {buttonText}
+                    </Button>
+                  )
+                )
+              : secondaryButtonText && (
+                  <Button
+                    disabled={loadingActive}
+                    kind="secondary"
+                    onClick={onSecondaryButtonClick}
+                    ref={secondaryButton}>
+                    {secondaryButtonText}
+                  </Button>
+                )}
+            <Button
+              className={primaryButtonClass}
+              kind={danger ? 'danger' : 'primary'}
+              disabled={loadingActive || primaryButtonDisabled}
+              onClick={onRequestSubmit}
+              ref={button}>
+              {loadingStatus === 'inactive' ? (
+                primaryButtonText
+              ) : (
+                <InlineLoading
+                  status={loadingStatus}
+                  description={loadingDescription}
+                  iconDescription={loadingIconDescription}
+                  className={`${prefix}--inline-loading--btn`}
+                  onSuccess={onLoadingSuccess}
+                />
+              )}
+            </Button>
+          </ButtonSet>
+        )}
+      </div>
       {/* Non-translatable: Focus-wrap code makes this `<span>` not actually read by screen readers */}
-      {!focusTrapWithoutSentinels && (
+      {!enableDialogElement && (
         <span
           ref={endTrap}
           tabIndex={0}
@@ -644,6 +730,20 @@ const Modal = React.forwardRef(function Modal(
           Focus sentinel
         </span>
       )}
+    </>
+  );
+
+  return (
+    <Layer
+      {...rest}
+      level={0}
+      onKeyDown={handleKeyDown}
+      onClick={composeEventHandlers([rest?.onClick, handleOnClick])}
+      onBlur={!enableDialogElement ? handleBlur : () => {}}
+      className={modalClasses}
+      role="presentation"
+      ref={ref}>
+      {modalBody}
     </Layer>
   );
 });
diff --git a/packages/react/src/components/Modal/next/index.tsx b/packages/react/src/components/Modal/next/index.tsx
deleted file mode 100644
index 66e321b09255..000000000000
--- a/packages/react/src/components/Modal/next/index.tsx
+++ /dev/null
@@ -1,706 +0,0 @@
-/**
- * Copyright IBM Corp. 2016, 2025
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-import PropTypes from 'prop-types';
-import React, { useRef, useState, ReactNode } from 'react';
-import classNames from 'classnames';
-import { Close } from '@carbon/icons-react';
-import Button from '../../Button';
-import ButtonSet from '../../ButtonSet';
-import InlineLoading from '../../InlineLoading';
-import { Layer } from '../../Layer';
-import requiredIfGivenPropIsTruthy from '../../../prop-types/requiredIfGivenPropIsTruthy';
-import { debounce } from 'es-toolkit/compat';
-import useIsomorphicEffect from '../../../internal/useIsomorphicEffect';
-import { useId } from '../../../internal/useId';
-import { usePrefix } from '../../../internal/usePrefix';
-import { keys, match } from '../../../internal/keyboard';
-import { IconButton } from '../../IconButton';
-import { noopFn } from '../../../internal/noopFn';
-import { Text } from '../../Text';
-import { ReactAttr } from '../../../types/common';
-import { InlineLoadingStatus } from '../../InlineLoading/InlineLoading';
-import { unstable__Dialog as Dialog } from '../../Dialog/index';
-
-export const ModalSizes = ['xs', 'sm', 'md', 'lg'] as const;
-
-export type ModalSize = (typeof ModalSizes)[number];
-
-export interface ModalSecondaryButton {
-  buttonText?: string | ReactNode;
-
-  onClick?: React.MouseEventHandler<HTMLButtonElement>;
-}
-
-export interface BaseModalProps extends ReactAttr<HTMLDivElement> {
-  /**
-   * Required props for the accessibility label of the header
-   */
-  'aria-label'?: string;
-
-  /**
-   * Provide the contents of your Modal
-   */
-  children?: ReactNode;
-
-  /**
-   * Specify an optional className to be applied to the modal root node
-   */
-  className?: string;
-
-  /**
-   * Specify an label for the close button of the modal; defaults to close
-   */
-  closeButtonLabel?: string;
-
-  /**
-   * Specify whether the Modal is for dangerous actions
-   */
-  danger?: boolean;
-
-  /**
-   * **Experimental**: Provide a decorator component to be rendered inside the `Modal` component
-   */
-  decorator?: ReactNode;
-
-  /**
-   * Specify whether the modal contains scrolling content
-   */
-  hasScrollingContent?: boolean;
-
-  /**
-   * Specify the DOM element ID of the top-level node.
-   */
-  id?: string;
-
-  /**
-   * Specify whether or not the Modal content should have any inner padding.
-   */
-  isFullWidth?: boolean;
-
-  /**
-   * Specify the description for the loading text
-   */
-  loadingDescription?: string;
-
-  /**
-   * Specify the description for the loading text
-   */
-  loadingIconDescription?: string;
-
-  /**
-   * Specify loading status
-   */
-  loadingStatus?: InlineLoadingStatus;
-
-  /**
-   * Specify a label to be read by screen readers on the modal root node
-   */
-  modalAriaLabel?: string;
-
-  /**
-   * Specify the content of the modal header title.
-   */
-  modalHeading?: ReactNode;
-
-  /**
-   * Specify the content of the modal header label.
-   */
-  modalLabel?: ReactNode;
-
-  /**
-   * Specify a handler for keypresses.
-   * @deprecated this property is unused
-   */
-  onKeyDown?: React.KeyboardEventHandler<HTMLElement>;
-
-  /**
-   * Specify an optional handler to be invoked when loading is
-   * successful
-   */
-  onLoadingSuccess?: () => void;
-
-  /**
-   * Specify a handler for closing modal.
-   * The handler should care of closing modal, e.g. changing `open` prop.
-   */
-  onRequestClose?: React.ReactEventHandler<HTMLElement>;
-
-  /**
-   * Specify a handler for "submitting" modal.
-   * The handler should care of closing modal, e.g. changing `open` prop, if necessary.
-   */
-  onRequestSubmit?: React.ReactEventHandler<HTMLElement>;
-
-  /**
-   * Specify a handler for the secondary button.
-   * Useful if separate handler from `onRequestClose` is desirable
-   */
-  onSecondarySubmit?: React.ReactEventHandler<HTMLElement>;
-
-  /**
-   * Specify whether the Modal is currently open
-   */
-  open?: boolean;
-
-  /**
-   * Prevent closing on click outside of modal
-   */
-  preventCloseOnClickOutside?: boolean;
-
-  /**
-   * Specify whether the Button should be disabled, or not
-   */
-  primaryButtonDisabled?: boolean;
-
-  /**
-   * Specify the text for the primary button
-   */
-  primaryButtonText?: ReactNode;
-
-  /**
-   * Specify the text for the secondary button
-   */
-  secondaryButtonText?: ReactNode;
-
-  /**
-   * Specify an array of config objects for secondary buttons
-   */
-  secondaryButtons?: ModalSecondaryButton[];
-
-  /**
-   * Specify a CSS selector that matches the DOM element that should
-   * be focused when the Modal opens
-   */
-  selectorPrimaryFocus?: string;
-
-  /**
-   * Specify CSS selectors that match DOM elements working as floating menus.
-   * Focusing on those elements won't trigger "focus-wrap" behavior
-   */
-  selectorsFloatingMenus?: string[];
-
-  /**
-   * Specify if Enter key should be used as "submit" action
-   */
-  shouldSubmitOnEnter?: boolean;
-
-  /**
-   * Specify the size variant.
-   */
-  size?: ModalSize;
-}
-
-// passive modals can not use `alert`
-interface PassiveModal extends BaseModalProps {
-  /**
-   * Specify whether the modal should be button-less
-   */
-  passiveModal?: true;
-
-  /**
-   * Specify whether the Modal is displaying an alert, error or warning. Should
-   * go hand in hand with the `danger` prop. This changes the `role` to
-   * `alertdialog` and cannot be used with passive modals.
-   */
-  alert?: never;
-}
-
-// non passive modals can optionally set `alert`
-interface NonPassiveModal extends BaseModalProps {
-  /**
-   * Specify whether the modal should be button-less
-   */
-  passiveModal?: false;
-
-  /**
-   * Specify whether the Modal is displaying an alert, error or warning. Should
-   * go hand in hand with the `danger` prop. This changes the `role` to
-   * `alertdialog` and cannot be used with passive modals.
-   */
-  alert?: boolean;
-}
-
-export type ModalProps = PassiveModal | NonPassiveModal;
-
-const Modal = React.forwardRef(function Modal(
-  {
-    'aria-label': ariaLabelProp,
-    children,
-    className,
-    decorator,
-    modalHeading = '',
-    modalLabel = '',
-    modalAriaLabel,
-    passiveModal = false,
-    secondaryButtonText,
-    primaryButtonText,
-    open,
-    onRequestClose = noopFn,
-    onRequestSubmit = noopFn,
-    onSecondarySubmit,
-    primaryButtonDisabled = false,
-    danger,
-    alert,
-    secondaryButtons,
-    selectorPrimaryFocus = '[data-modal-primary-focus]',
-    selectorsFloatingMenus,
-    shouldSubmitOnEnter,
-    size,
-    hasScrollingContent = false,
-    closeButtonLabel = 'Close',
-    preventCloseOnClickOutside = false,
-    isFullWidth,
-    loadingStatus = 'inactive',
-    loadingDescription,
-    loadingIconDescription,
-    onLoadingSuccess = noopFn,
-    ...rest
-  }: ModalProps,
-  ref: React.LegacyRef<HTMLDivElement>
-) {
-  const prefix = usePrefix();
-  const button = useRef<HTMLButtonElement>(null);
-  const secondaryButton = useRef<HTMLButtonElement>(null);
-  const contentRef = useRef<HTMLDivElement>(null);
-  const innerModal = useRef<HTMLDialogElement>(null);
-  const [isScrollable, setIsScrollable] = useState(false);
-  const modalInstanceId = `modal-${useId()}`;
-  const modalLabelId = `${prefix}--modal-header__label--${modalInstanceId}`;
-  const modalHeadingId = `${prefix}--modal-header__heading--${modalInstanceId}`;
-  const modalBodyId = `${prefix}--modal-body--${modalInstanceId}`;
-  const modalCloseButtonClass = `${prefix}--modal-close`;
-  const primaryButtonClass = classNames({
-    [`${prefix}--btn--loading`]: loadingStatus !== 'inactive',
-  });
-  const loadingActive = loadingStatus !== 'inactive';
-
-  function isCloseButton(element: Element) {
-    return (
-      (!onSecondarySubmit && element === secondaryButton.current) ||
-      element.classList.contains(modalCloseButtonClass)
-    );
-  }
-
-  function handleKeyDown(evt: React.KeyboardEvent<HTMLDivElement>) {
-    evt.stopPropagation();
-    if (open) {
-      if (
-        match(evt, keys.Enter) &&
-        shouldSubmitOnEnter &&
-        !isCloseButton(evt.target as Element)
-      ) {
-        onRequestSubmit(evt);
-      }
-    }
-  }
-
-  const onSecondaryButtonClick = onSecondarySubmit
-    ? onSecondarySubmit
-    : onRequestClose;
-
-  const modalClasses = classNames(
-    `${prefix}--modal`,
-    {
-      [`${prefix}--modal-tall`]: !passiveModal,
-      [`${prefix}--modal--danger`]: danger,
-      [`${prefix}--modal--decorator`]: decorator,
-    },
-    className
-  );
-
-  const containerClasses = classNames(`${prefix}--modal-container`, {
-    [`${prefix}--modal-container--${size}`]: size,
-    [`${prefix}--modal-container--full-width`]: isFullWidth,
-  });
-
-  const contentClasses = classNames(`${prefix}--modal-content`, {
-    [`${prefix}--modal-scroll-content`]: hasScrollingContent || isScrollable,
-  });
-
-  const footerClasses = classNames(`${prefix}--modal-footer`, {
-    [`${prefix}--modal-footer--three-button`]:
-      Array.isArray(secondaryButtons) && secondaryButtons.length === 2,
-  });
-
-  const asStringOrUndefined = (node: ReactNode): string | undefined => {
-    return typeof node === 'string' ? node : undefined;
-  };
-  const modalLabelStr = asStringOrUndefined(modalLabel);
-  const modalHeadingStr = asStringOrUndefined(modalHeading);
-  const ariaLabel =
-    modalLabelStr || ariaLabelProp || modalAriaLabel || modalHeadingStr;
-  const getAriaLabelledBy = modalLabel ? modalLabelId : modalHeadingId;
-  const hasScrollingContentProps =
-    hasScrollingContent || isScrollable
-      ? {
-          tabIndex: 0,
-          role: 'region',
-          'aria-label': ariaLabel,
-          'aria-labelledby': getAriaLabelledBy,
-        }
-      : {};
-  useIsomorphicEffect(() => {
-    if (contentRef.current) {
-      setIsScrollable(
-        contentRef.current.scrollHeight > contentRef.current.clientHeight
-      );
-    }
-
-    function handler() {
-      if (contentRef.current) {
-        setIsScrollable(
-          contentRef.current.scrollHeight > contentRef.current.clientHeight
-        );
-      }
-    }
-
-    const debouncedHandler = debounce(handler, 200);
-    window.addEventListener('resize', debouncedHandler);
-    return () => {
-      debouncedHandler.cancel();
-      window.removeEventListener('resize', debouncedHandler);
-    };
-  }, []);
-
-  // alertdialog is the only permitted aria role for a native dialog element
-  // https://www.w3.org/TR/html-aria/#docconformance:~:text=Role%3A-,alertdialog,-.%20(dialog%20is
-  const isAlertDialog = alert && !passiveModal;
-
-  // When the decorator is a react element, like AILabel, it should always be given `size='sm'`
-  const normalizedDecorator = React.isValidElement(decorator)
-    ? React.cloneElement(decorator as React.ReactElement<any>, {
-        size: 'sm',
-      })
-    : decorator;
-
-  return (
-    <Layer
-      {...rest}
-      level={0}
-      onKeyDown={handleKeyDown}
-      onClick={rest?.onClick}
-      className={modalClasses}
-      role="presentation"
-      ref={ref}>
-      <Dialog
-        open={open}
-        modal
-        ref={innerModal}
-        role={isAlertDialog ? 'alertdialog' : ''}
-        aria-describedby={isAlertDialog ? modalBodyId : ''}
-        className={containerClasses}
-        aria-label={ariaLabel}>
-        <div className={`${prefix}--modal-header`}>
-          {modalLabel && (
-            <Text
-              as="h2"
-              id={modalLabelId}
-              className={`${prefix}--modal-header__label`}>
-              {modalLabel}
-            </Text>
-          )}
-          <Text
-            as="h2"
-            id={modalHeadingId}
-            className={`${prefix}--modal-header__heading`}>
-            {modalHeading}
-          </Text>
-          {decorator ? (
-            <div className={`${prefix}--modal--inner__decorator`}>
-              {normalizedDecorator}
-            </div>
-          ) : (
-            ''
-          )}
-          <div className={`${prefix}--modal-close-button`}>
-            <IconButton
-              className={modalCloseButtonClass}
-              label={closeButtonLabel}
-              onClick={onRequestClose}
-              aria-label={closeButtonLabel}
-              align="left"
-              ref={button}>
-              <Close
-                size={20}
-                aria-hidden="true"
-                tabIndex="-1"
-                className={`${modalCloseButtonClass}__icon`}
-              />
-            </IconButton>
-          </div>
-        </div>
-        <Layer
-          ref={contentRef}
-          id={modalBodyId}
-          className={contentClasses}
-          {...hasScrollingContentProps}>
-          {children}
-        </Layer>
-        {!passiveModal && (
-          <ButtonSet className={footerClasses} aria-busy={loadingActive}>
-            {Array.isArray(secondaryButtons) && secondaryButtons.length <= 2
-              ? secondaryButtons.map(
-                  ({ buttonText, onClick: onButtonClick }, i) => (
-                    <Button
-                      key={`${buttonText}-${i}`}
-                      kind="secondary"
-                      onClick={onButtonClick}>
-                      {buttonText}
-                    </Button>
-                  )
-                )
-              : secondaryButtonText && (
-                  <Button
-                    disabled={loadingActive}
-                    kind="secondary"
-                    onClick={onSecondaryButtonClick}
-                    ref={secondaryButton}>
-                    {secondaryButtonText}
-                  </Button>
-                )}
-            <Button
-              className={primaryButtonClass}
-              kind={danger ? 'danger' : 'primary'}
-              disabled={loadingActive || primaryButtonDisabled}
-              onClick={onRequestSubmit}
-              ref={button}>
-              {loadingStatus === 'inactive' ? (
-                primaryButtonText
-              ) : (
-                <InlineLoading
-                  status={loadingStatus}
-                  description={loadingDescription}
-                  iconDescription={loadingIconDescription}
-                  className={`${prefix}--inline-loading--btn`}
-                  onSuccess={onLoadingSuccess}
-                />
-              )}
-            </Button>
-          </ButtonSet>
-        )}
-      </Dialog>
-    </Layer>
-  );
-});
-
-Modal.propTypes = {
-  /**
-   * Specify whether the Modal is displaying an alert, error or warning
-   * Should go hand in hand with the danger prop.
-   */
-  alert: PropTypes.bool,
-
-  /**
-   * Required props for the accessibility label of the header
-   */
-  ['aria-label']: requiredIfGivenPropIsTruthy(
-    'hasScrollingContent',
-    PropTypes.string
-  ),
-
-  /**
-   * Provide the contents of your Modal
-   */
-  children: PropTypes.node,
-
-  /**
-   * Specify an optional className to be applied to the modal root node
-   */
-  className: PropTypes.string,
-
-  /**
-   * Specify an label for the close button of the modal; defaults to close
-   */
-  closeButtonLabel: PropTypes.string,
-
-  /**
-   * Specify whether the Modal is for dangerous actions
-   */
-  danger: PropTypes.bool,
-
-  /**
-   * **Experimental**: Provide a decorator component to be rendered inside the `Modal` component
-   */
-  decorator: PropTypes.node,
-
-  /**
-   * Specify whether the modal contains scrolling content
-   */
-  hasScrollingContent: PropTypes.bool,
-
-  /**
-   * Specify the DOM element ID of the top-level node.
-   */
-  id: PropTypes.string,
-
-  /**
-   * Specify whether or not the Modal content should have any inner padding.
-   */
-  isFullWidth: PropTypes.bool,
-
-  /**
-   * Specify the description for the loading text
-   */
-  loadingDescription: PropTypes.string,
-
-  /**
-   * Specify the description for the loading text
-   */
-  loadingIconDescription: PropTypes.string,
-
-  /**
-   * loading status
-   */
-  loadingStatus: PropTypes.oneOf(['inactive', 'active', 'finished', 'error']),
-
-  /**
-   * Specify a label to be read by screen readers on the modal root node
-   */
-  modalAriaLabel: PropTypes.string,
-
-  /**
-   * Specify the content of the modal header title.
-   */
-  modalHeading: PropTypes.node,
-
-  /**
-   * Specify the content of the modal header label.
-   */
-  modalLabel: PropTypes.node,
-
-  /**
-   * Specify a handler for keypresses.
-   */
-  onKeyDown: PropTypes.func,
-
-  /**
-   * Provide an optional handler to be invoked when loading is
-   * successful
-   */
-  onLoadingSuccess: PropTypes.func,
-
-  /**
-   * Specify a handler for closing modal.
-   * The handler should care of closing modal, e.g. changing `open` prop.
-   */
-  onRequestClose: PropTypes.func,
-
-  /**
-   * Specify a handler for "submitting" modal.
-   * The handler should care of closing modal, e.g. changing `open` prop, if necessary.
-   */
-  onRequestSubmit: PropTypes.func,
-
-  /**
-   * Specify a handler for the secondary button.
-   * Useful if separate handler from `onRequestClose` is desirable
-   */
-  onSecondarySubmit: PropTypes.func,
-
-  /**
-   * Specify whether the Modal is currently open
-   */
-  open: PropTypes.bool,
-
-  /**
-   * Specify whether the modal should be button-less
-   */
-  passiveModal: function (props, propName, componentName) {
-    if (props.passiveModal && props.alert) {
-      return new Error(
-        'Invalid prop `' +
-          propName +
-          '` supplied to' +
-          ' `' +
-          componentName +
-          '`. alert cannot be used with passive modals.'
-      );
-    }
-    return null;
-  },
-
-  /**
-   * Prevent closing on click outside of modal
-   */
-  preventCloseOnClickOutside: PropTypes.bool,
-
-  /**
-   * Specify whether the Button should be disabled, or not
-   */
-  primaryButtonDisabled: PropTypes.bool,
-
-  /**
-   * Specify the text for the primary button
-   */
-  primaryButtonText: PropTypes.node,
-
-  /**
-   * Specify the text for the secondary button
-   */
-  secondaryButtonText: PropTypes.node,
-
-  /**
-   * Specify an array of config objects for secondary buttons
-   * (`Array<{
-   *   buttonText: string,
-   *   onClick: function,
-   * }>`).
-   */
-  secondaryButtons: (props, propName, componentName) => {
-    if (props.secondaryButtons) {
-      if (
-        !Array.isArray(props.secondaryButtons) ||
-        props.secondaryButtons.length !== 2
-      ) {
-        return new Error(
-          `${propName} needs to be an array of two button config objects`
-        );
-      }
-
-      const shape = {
-        buttonText: PropTypes.node,
-        onClick: PropTypes.func,
-      };
-
-      props[propName].forEach((secondaryButton) => {
-        PropTypes.checkPropTypes(
-          shape,
-          secondaryButton,
-          propName,
-          componentName
-        );
-      });
-    }
-
-    return null;
-  },
-
-  /**
-   * Specify a CSS selector that matches the DOM element that should
-   * be focused when the Modal opens
-   */
-  selectorPrimaryFocus: PropTypes.string,
-
-  /**
-   * Specify CSS selectors that match DOM elements working as floating menus.
-   * Focusing on those elements won't trigger "focus-wrap" behavior
-   */
-  selectorsFloatingMenus: PropTypes.arrayOf(PropTypes.string.isRequired),
-
-  /**
-   * Specify if Enter key should be used as "submit" action
-   */
-  shouldSubmitOnEnter: PropTypes.bool,
-
-  /**
-   * Specify the size variant.
-   */
-  size: PropTypes.oneOf(ModalSizes),
-};
-
-export default Modal;
diff --git a/packages/react/telemetry.yml b/packages/react/telemetry.yml
index 783a51fde402..1392865d2615 100644
--- a/packages/react/telemetry.yml
+++ b/packages/react/telemetry.yml
@@ -327,6 +327,7 @@ collect:
         - tilePadding
         # FeatureFlags
         - enableExperimentalFocusWrapWithoutSentinels
+        - enableDialogElement
         - enableTreeviewControllable
         - enableV12DynamicFloatingStyles
         - enableV12Overflowmenu
diff --git a/packages/styles/scss/_feature-flags.scss b/packages/styles/scss/_feature-flags.scss
index 69125e4cb100..20159b0f8c57 100644
--- a/packages/styles/scss/_feature-flags.scss
+++ b/packages/styles/scss/_feature-flags.scss
@@ -13,6 +13,7 @@
     'enable-experimental-tile-contrast': false,
     'enable-v12-tile-radio-icons': false,
     'enable-v12-structured-list-visible-icons': false,
+    'enable-dialog-element': false,
   )
   !default
 );
diff --git a/packages/styles/scss/components/_index.scss b/packages/styles/scss/components/_index.scss
index 964403e3fc67..1c25ec97b26c 100644
--- a/packages/styles/scss/components/_index.scss
+++ b/packages/styles/scss/components/_index.scss
@@ -25,6 +25,7 @@
 @use 'data-table/skeleton';
 @use 'data-table/sort';
 @use 'date-picker';
+@use 'dialog';
 @use 'dropdown';
 @use 'file-uploader';
 @use 'fluid-combo-box';
diff --git a/packages/styles/scss/components/dialog/_dialog.scss b/packages/styles/scss/components/dialog/_dialog.scss
index df0c90aa7ef5..dbc018ec580b 100644
--- a/packages/styles/scss/components/dialog/_dialog.scss
+++ b/packages/styles/scss/components/dialog/_dialog.scss
@@ -18,16 +18,14 @@
 @use '../../utilities/component-reset';
 @use '../../utilities/focus-outline' as *;
 @use '../../utilities/high-contrast-mode' as *;
-@use '../../utilities/z-index' as *;
 
 /// Dialog styles
 /// @access public
 /// @group dialog
 @mixin dialog {
   .#{$prefix}--dialog {
-    /* size */
     padding: 0;
-    border: 1px solid $border-subtle-01;
+    border: none;
     background-color: $layer;
     color: $text-primary;
     inline-size: 48rem;
@@ -101,15 +99,6 @@
     min-block-size: $spacing-09;
   }
 
-  .#{$prefix}--dialog__content {
-    padding: $spacing-05;
-    block-size: 100%;
-  }
-
-  .#{$prefix}--dialog--modal {
-    border: 1px solid transparent;
-  }
-
   /* Transition the :backdrop when the dialog modal is promoted to the top layer */
   .#{$prefix}--dialog::backdrop {
     background-color: $overlay;
diff --git a/packages/styles/scss/components/modal/_modal.scss b/packages/styles/scss/components/modal/_modal.scss
index a066addc5105..e2e07f71556d 100644
--- a/packages/styles/scss/components/modal/_modal.scss
+++ b/packages/styles/scss/components/modal/_modal.scss
@@ -7,6 +7,7 @@
 
 @use '../button';
 @use '../../config' as *;
+@use '../../feature-flags' as *;
 @use '../../breakpoint' as *;
 @use '../../motion' as *;
 @use '../../spacing' as *;
@@ -23,34 +24,57 @@
 /// Modal styles
 /// @access public
 /// @group modal
-@mixin modal {
+@mixin modal(
+  $enable-experimental-focus-wrap-without-sentinels: false,
+  $enable-dialog-element: false
+) {
   .#{$prefix}--modal {
-    position: fixed;
-    z-index: z('modal');
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    background-color: $overlay;
-    block-size: 100vh;
-    content: '';
-    inline-size: 100vw;
-    inset-block-start: 0;
-    inset-inline-start: 0;
-    opacity: 0;
-    transition:
-      opacity $duration-moderate-02 motion(exit, expressive),
-      visibility 0ms linear $duration-moderate-02;
-    visibility: hidden;
-
-    &.is-visible {
-      opacity: 1;
+    // For modals using the native dialog element, the modal visibility is handled
+    // by the Dialog styles.
+    @if not(
+      enabled('enable-experimental-focus-wrap-without-sentinels') or
+        $enable-experimental-focus-wrap-without-sentinels or
+        enabled('enable-dialog-element') or
+        $enable-dialog-element
+    )
+    {
+      position: fixed;
+      z-index: z('modal');
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      background-color: $overlay;
+      block-size: 100vh;
+      content: '';
+      inline-size: 100vw;
+      inset-block-start: 0;
+      inset-inline-start: 0;
+      opacity: 0;
       transition:
-        opacity $duration-moderate-02 motion(entrance, expressive),
-        visibility 0ms linear;
-      visibility: inherit;
+        opacity $duration-moderate-02 motion(exit, expressive),
+        visibility 0ms linear $duration-moderate-02;
+      visibility: hidden;
+    }
 
-      @media screen and (prefers-reduced-motion: reduce) {
-        transition: none;
+    // For modals using the native dialog element, the modal visibility is handled
+    // by the Dialog styles.
+    @if not(
+      enabled('enable-experimental-focus-wrap-without-sentinels') or
+        $enable-experimental-focus-wrap-without-sentinels or
+        enabled('enable-dialog-element') or
+        $enable-dialog-element
+    )
+    {
+      &.is-visible {
+        opacity: 1;
+        transition:
+          opacity $duration-moderate-02 motion(entrance, expressive),
+          visibility 0ms linear;
+        visibility: inherit;
+
+        @media screen and (prefers-reduced-motion: reduce) {
+          transition: none;
+        }
       }
     }
 


Reviews:
user: login: heloiselui
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T15:23:34Z
user: login: tay1orjones
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T13:40:03Z
user: login: tay1orjones
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T13:40:29Z
user: login: tay1orjones
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T13:44:39Z
user: login: tay1orjones
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T13:44:47Z
user: login: heloiselui
body: Looks great! 🎉👍
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T17:15:55Z
user: login: ariellalgilmore
body: LGTM!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-14T19:24:18Z
user: login: AlanGreene
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T19:42:19Z

Review comments:
diff_hunk: @@ -0,0 +1,9 @@
+// Code generated by @carbon/react. DO NOT EDIT.
+//
+// Copyright IBM Corp. 2018, 2023
path: packages/react/scss/components/dialog/_dialog.scss
user: login: heloiselui
body: ```suggestion
// Copyright IBM Corp. 2018, 2025
```
created_at: 2025-03-12T10:15:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,9 @@
+// Code generated by @carbon/react. DO NOT EDIT.
+//
+// Copyright IBM Corp. 2018, 2023
path: packages/react/scss/components/dialog/_index.scss
user: login: heloiselui
body: ```suggestion
// Copyright IBM Corp. 2018, 2025
```
created_at: 2025-03-12T10:16:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,173 @@
+/**
+ * Copyright IBM Corp. 2016, 2023
path: packages/react/src/components/Dialog/Dialog.stories.js
user: login: heloiselui
body: ```suggestion
 * Copyright IBM Corp. 2016, 2025
```
created_at: 2025-03-12T10:17:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,173 @@
+/**
+ * Copyright IBM Corp. 2016, 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/* eslint-disable storybook/story-exports */
+
+import React, { useEffect, useState } from 'react';
+import {
+  unstable__Dialog as Dialog,
+  DialogHeader,
+  DialogControls,
+  DialogCloseButton,
+} from './';
+import Button from '../Button';
+import { action } from '@storybook/addon-actions';
+import mdx from './Dialog.mdx';
+
+export default {
+  title: 'Experimental/unstable_Dialog',
+  component: Dialog,
+  // includeStories: [],
path: packages/react/src/components/Dialog/Dialog.stories.js
user: login: heloiselui
body: Should we delete this comment? or uncomment includeStories before merging?
created_at: 2025-03-12T10:26:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,173 @@
+/**
+ * Copyright IBM Corp. 2016, 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/* eslint-disable storybook/story-exports */
+
+import React, { useEffect, useState } from 'react';
+import {
+  unstable__Dialog as Dialog,
+  DialogHeader,
+  DialogControls,
+  DialogCloseButton,
+} from './';
+import Button from '../Button';
+import { action } from '@storybook/addon-actions';
+import mdx from './Dialog.mdx';
+
+export default {
+  title: 'Experimental/unstable_Dialog',
+  component: Dialog,
+  // includeStories: [],
+  parameters: {
+    docs: {
+      page: mdx,
+    },
+  },
+  argTypes: {
+    children: {
+      table: {
+        disable: true,
+      },
+    },
+    onRequestClose: {
+      table: {
+        disable: true,
+      },
+    },
+  },
+};
+
+export const Modal = ({ open: _open, ...args }) => {
+  const [open, setOpen] = useState(_open);
+
+  function toggleDialog() {
+    setOpen(!open);
+  }
+
+  function closeDialog(e) {
+    setOpen(false);
+  }
+
+  function handleRequestClose(e) {
+    action('Dialog onRequestClose');
+    closeDialog(e);
+  }
+
+  useEffect(() => {
+    setOpen(_open);
+  }, [_open]);
+
+  return (
+    <div>
+      <Button type="button" onClick={toggleDialog}>
+        Toggle open
+      </Button>
+      <Dialog {...args} open={open} onRequestClose={handleRequestClose}>
+        <DialogHeader>
+          {/* <DialogSubtitle></DialogSubtitle>
path: packages/react/src/components/Dialog/Dialog.stories.js
user: login: heloiselui
body: Are DialogSubtitle and DialogTitle intentionally commented out? 
created_at: 2025-03-12T10:30:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -145,5 +173,92 @@ unstable__Dialog.propTypes = {
   open: PropTypes.bool,
 };
 
-export { unstable__Dialog };
-export default unstable__Dialog;
+export interface DialogHeaderProps extends ReactAttr<HTMLDivElement> {
+  /**
+   * Provide the contents to be rendered inside of this component
+   */
+  children?: React.ReactNode;
+}
+export const DialogHeader = React.forwardRef<HTMLDivElement, DialogHeaderProps>(
+  ({ children, ...rest }, ref) => {
+    const prefix = usePrefix();
+    return (
+      <div className={`${prefix}--dialog__header`} ref={ref} {...rest}>
+        {children}
+      </div>
+    );
+  }
+);
+DialogHeader.propTypes = {
+  /**
+   * Provide the contents to be rendered inside of this component
+   */
+  children: PropTypes.node,
+};
+
+export interface DialogControlsProps extends ReactAttr<HTMLDivElement> {
+  /**
+   * Provide the contents to be rendered inside of this component
+   */
+  children?: React.ReactNode;
+}
+export const DialogControls = React.forwardRef<
+  HTMLDivElement,
+  DialogControlsProps
+>(({ children, ...rest }, ref) => {
+  const prefix = usePrefix();
+  return (
+    // @ts-ignore
+    <div className={`${prefix}--dialog__header-controls`} ref={ref} {...rest}>
+      {children}
+    </div>
+  );
+});
+DialogControls.propTypes = {
+  /**
+   * Provide children to be rendered inside of this component
+   */
+  children: PropTypes.node,
+};
+
+export interface DialogCloseButtonProps extends ReactAttr<HTMLDivElement> {
+  /**
+   * Specify a click handler applied to the IconButton
+   */
+  onClick?: React.MouseEventHandler;
+}
+export const DialogCloseButton = React.forwardRef<
+  HTMLDivElement,
+  DialogCloseButtonProps
+>(({ onClick, ...rest }, ref) => {
+  const prefix = usePrefix();
+  return (
+    // @ts-ignore
+    <IconButton
+      kind="ghost"
+      className={`${prefix}--dialog__close`}
+      label="Close"
+      title="Close"
+      aria-label="Close"
+      align="left"
+      onClick={onClick}
+      {...rest}>
+      <Close
+        size={20}
+        aria-hidden="true"
+        tabIndex="-1"
+        className={`${prefix}--icon__close`}
+      />
+    </IconButton>
+  );
+});
+
+DialogCloseButton.propTypes = {
+  /**
+   * Specify a click handler applied to the IconButton
+   */
+  onClick: PropTypes.func,
+};
+
+// export { unstable__Dialog };
path: packages/react/src/components/Dialog/index.tsx
user: login: heloiselui
body: Should these be removed, or do we need to keep them for reference?
created_at: 2025-03-12T15:18:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -101,14 +99,9 @@
     min-block-size: $spacing-09;
   }
 
-  .#{$prefix}--dialog__content {
-    padding: $spacing-05;
-    block-size: 100%;
-  }
-
-  .#{$prefix}--dialog--modal {
-    border: 1px solid transparent;
-  }
+  // .#{$prefix}--dialog--modal {
path: packages/styles/scss/components/dialog/_dialog.scss
user: login: heloiselui
body: Should we delete this comment or uncomment it before merging?
created_at: 2025-03-12T15:18:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,173 @@
+/**
+ * Copyright IBM Corp. 2016, 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/* eslint-disable storybook/story-exports */
+
+import React, { useEffect, useState } from 'react';
+import {
+  unstable__Dialog as Dialog,
+  DialogHeader,
+  DialogControls,
+  DialogCloseButton,
+} from './';
+import Button from '../Button';
+import { action } from '@storybook/addon-actions';
+import mdx from './Dialog.mdx';
+
+export default {
+  title: 'Experimental/unstable_Dialog',
+  component: Dialog,
+  // includeStories: [],
+  parameters: {
+    docs: {
+      page: mdx,
+    },
+  },
+  argTypes: {
+    children: {
+      table: {
+        disable: true,
+      },
+    },
+    onRequestClose: {
+      table: {
+        disable: true,
+      },
+    },
+  },
+};
+
+export const Modal = ({ open: _open, ...args }) => {
+  const [open, setOpen] = useState(_open);
+
+  function toggleDialog() {
+    setOpen(!open);
+  }
+
+  function closeDialog(e) {
+    setOpen(false);
+  }
+
+  function handleRequestClose(e) {
+    action('Dialog onRequestClose');
+    closeDialog(e);
+  }
+
+  useEffect(() => {
+    setOpen(_open);
+  }, [_open]);
+
+  return (
+    <div>
+      <Button type="button" onClick={toggleDialog}>
+        Toggle open
+      </Button>
+      <Dialog {...args} open={open} onRequestClose={handleRequestClose}>
+        <DialogHeader>
+          {/* <DialogSubtitle></DialogSubtitle>
path: packages/react/src/components/Dialog/Dialog.stories.js
user: login: tay1orjones
body: Yeah, I left them in as a stub/example for #18243 
created_at: 2025-03-14T13:40:03Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,173 @@
+/**
+ * Copyright IBM Corp. 2016, 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/* eslint-disable storybook/story-exports */
+
+import React, { useEffect, useState } from 'react';
+import {
+  unstable__Dialog as Dialog,
+  DialogHeader,
+  DialogControls,
+  DialogCloseButton,
+} from './';
+import Button from '../Button';
+import { action } from '@storybook/addon-actions';
+import mdx from './Dialog.mdx';
+
+export default {
+  title: 'Experimental/unstable_Dialog',
+  component: Dialog,
+  // includeStories: [],
path: packages/react/src/components/Dialog/Dialog.stories.js
user: login: tay1orjones
body: uncomment before merging so they're hidden! I put this in the PR body above
created_at: 2025-03-14T13:40:29Z
author_association: MEMBER
diff_hunk: @@ -145,5 +173,92 @@ unstable__Dialog.propTypes = {
   open: PropTypes.bool,
 };
 
-export { unstable__Dialog };
-export default unstable__Dialog;
+export interface DialogHeaderProps extends ReactAttr<HTMLDivElement> {
+  /**
+   * Provide the contents to be rendered inside of this component
+   */
+  children?: React.ReactNode;
+}
+export const DialogHeader = React.forwardRef<HTMLDivElement, DialogHeaderProps>(
+  ({ children, ...rest }, ref) => {
+    const prefix = usePrefix();
+    return (
+      <div className={`${prefix}--dialog__header`} ref={ref} {...rest}>
+        {children}
+      </div>
+    );
+  }
+);
+DialogHeader.propTypes = {
+  /**
+   * Provide the contents to be rendered inside of this component
+   */
+  children: PropTypes.node,
+};
+
+export interface DialogControlsProps extends ReactAttr<HTMLDivElement> {
+  /**
+   * Provide the contents to be rendered inside of this component
+   */
+  children?: React.ReactNode;
+}
+export const DialogControls = React.forwardRef<
+  HTMLDivElement,
+  DialogControlsProps
+>(({ children, ...rest }, ref) => {
+  const prefix = usePrefix();
+  return (
+    // @ts-ignore
+    <div className={`${prefix}--dialog__header-controls`} ref={ref} {...rest}>
+      {children}
+    </div>
+  );
+});
+DialogControls.propTypes = {
+  /**
+   * Provide children to be rendered inside of this component
+   */
+  children: PropTypes.node,
+};
+
+export interface DialogCloseButtonProps extends ReactAttr<HTMLDivElement> {
+  /**
+   * Specify a click handler applied to the IconButton
+   */
+  onClick?: React.MouseEventHandler;
+}
+export const DialogCloseButton = React.forwardRef<
+  HTMLDivElement,
+  DialogCloseButtonProps
+>(({ onClick, ...rest }, ref) => {
+  const prefix = usePrefix();
+  return (
+    // @ts-ignore
+    <IconButton
+      kind="ghost"
+      className={`${prefix}--dialog__close`}
+      label="Close"
+      title="Close"
+      aria-label="Close"
+      align="left"
+      onClick={onClick}
+      {...rest}>
+      <Close
+        size={20}
+        aria-hidden="true"
+        tabIndex="-1"
+        className={`${prefix}--icon__close`}
+      />
+    </IconButton>
+  );
+});
+
+DialogCloseButton.propTypes = {
+  /**
+   * Specify a click handler applied to the IconButton
+   */
+  onClick: PropTypes.func,
+};
+
+// export { unstable__Dialog };
path: packages/react/src/components/Dialog/index.tsx
user: login: tay1orjones
body: Thanks, I removed them
created_at: 2025-03-14T13:44:39Z
author_association: MEMBER
diff_hunk: @@ -101,14 +99,9 @@
     min-block-size: $spacing-09;
   }
 
-  .#{$prefix}--dialog__content {
-    padding: $spacing-05;
-    block-size: 100%;
-  }
-
-  .#{$prefix}--dialog--modal {
-    border: 1px solid transparent;
-  }
+  // .#{$prefix}--dialog--modal {
path: packages/styles/scss/components/dialog/_dialog.scss
user: login: tay1orjones
body: Deleted, thanks!
created_at: 2025-03-14T13:44:47Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,173 @@
+/**
+ * Copyright IBM Corp. 2016, 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/* eslint-disable storybook/story-exports */
+
+import React, { useEffect, useState } from 'react';
+import {
+  unstable__Dialog as Dialog,
+  DialogHeader,
+  DialogControls,
+  DialogCloseButton,
+} from './';
+import Button from '../Button';
+import { action } from '@storybook/addon-actions';
+import mdx from './Dialog.mdx';
+
+export default {
+  title: 'Experimental/unstable_Dialog',
+  component: Dialog,
+  // includeStories: [],
path: packages/react/src/components/Dialog/Dialog.stories.js
user: login: AlanGreene
body: Looks like this wasn't uncommented before merging…
created_at: 2025-03-18T19:42:19Z
author_association: CONTRIBUTOR

PR comments:
user: login: netlify[bot]
created_at: 2025-02-10T21:29:25Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *carbon-elements* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | a23275bd8be8e25a9a3b413825fff4bb50ecd749 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/carbon-elements/deploys/67d432dd2d2b400008896072 |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-18553--carbon-elements.netlify.app](https://deploy-preview-18553--carbon-elements.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTE4NTUzLS1jYXJib24tZWxlbWVudHMubmV0bGlmeS5hcHAifQ.AKAP898Nh_lhVNgMEiWa2RG0yVK_KY6-PA5d7xCv8vc)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [carbon-elements Preview](https://deploy-preview-18553--carbon-elements.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/carbon-elements/configuration/notifications#deploy-webhooks)._
author_association: NONE
user: login: netlify[bot]
created_at: 2025-02-11T16:56:44Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *v11-carbon-web-components* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | a23275bd8be8e25a9a3b413825fff4bb50ecd749 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/v11-carbon-web-components/deploys/67d432ddce800f00086619d9 |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-18553--v11-carbon-web-components.netlify.app](https://deploy-preview-18553--v11-carbon-web-components.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTE4NTUzLS12MTEtY2FyYm9uLXdlYi1jb21wb25lbnRzLm5ldGxpZnkuYXBwIn0.UUYQGMW1LJPum5HocxE0eA6eeXW4FERtZRCmTGzxMWo)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [v11-carbon-web-components Preview](https://deploy-preview-18553--v11-carbon-web-components.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/v11-carbon-web-components/configuration/notifications#deploy-webhooks)._
author_association: NONE
user: login: netlify[bot]
created_at: 2025-02-11T17:00:43Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *v11-carbon-react* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | a23275bd8be8e25a9a3b413825fff4bb50ecd749 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/v11-carbon-react/deploys/67d432dde85ea00008eccb76 |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-18553--v11-carbon-react.netlify.app](https://deploy-preview-18553--v11-carbon-react.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTE4NTUzLS12MTEtY2FyYm9uLXJlYWN0Lm5ldGxpZnkuYXBwIn0.Kkf4TzQGvnbdMdJmoVQX76oiYxrUqmHsi62LMpW70XU)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [v11-carbon-react Preview](https://deploy-preview-18553--v11-carbon-react.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/v11-carbon-react/configuration/notifications#deploy-webhooks)._
author_association: NONE
user: login: codecov[bot]
created_at: 2025-03-07T17:56:57Z
body: ## [Codecov](https://app.codecov.io/gh/carbon-design-system/carbon/pull/18553?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system) Report
Attention: Patch coverage is `68.65672%` with `42 lines` in your changes missing coverage. Please review.
> Project coverage is 84.81%. Comparing base [(`2dc4bc2`)](https://app.codecov.io/gh/carbon-design-system/carbon/commit/2dc4bc297d2199db9a5276eeed71534c968505de?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system) to head [(`a23275b`)](https://app.codecov.io/gh/carbon-design-system/carbon/commit/a23275bd8be8e25a9a3b413825fff4bb50ecd749?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system).
> Report is 31 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/carbon-design-system/carbon/pull/18553?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system) | Patch % | Lines |
|---|---|---|
| [packages/react/src/components/Dialog/index.tsx](https://app.codecov.io/gh/carbon-design-system/carbon/pull/18553?src=pr&el=tree&filepath=packages%2Freact%2Fsrc%2Fcomponents%2FDialog%2Findex.tsx&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system#diff-cGFja2FnZXMvcmVhY3Qvc3JjL2NvbXBvbmVudHMvRGlhbG9nL2luZGV4LnRzeA==) | 21.87% | [25 Missing :warning: ](https://app.codecov.io/gh/carbon-design-system/carbon/pull/18553?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system) |
| [...act/src/components/ComposedModal/ComposedModal.tsx](https://app.codecov.io/gh/carbon-design-system/carbon/pull/18553?src=pr&el=tree&filepath=packages%2Freact%2Fsrc%2Fcomponents%2FComposedModal%2FComposedModal.tsx&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system#diff-cGFja2FnZXMvcmVhY3Qvc3JjL2NvbXBvbmVudHMvQ29tcG9zZWRNb2RhbC9Db21wb3NlZE1vZGFsLnRzeA==) | 73.33% | [12 Missing :warning: ](https://app.codecov.io/gh/carbon-design-system/carbon/pull/18553?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system) |
| [packages/react/src/components/Modal/Modal.tsx](https://app.codecov.io/gh/carbon-design-system/carbon/pull/18553?src=pr&el=tree&filepath=packages%2Freact%2Fsrc%2Fcomponents%2FModal%2FModal.tsx&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system#diff-cGFja2FnZXMvcmVhY3Qvc3JjL2NvbXBvbmVudHMvTW9kYWwvTW9kYWwudHN4) | 91.07% | [5 Missing :warning: ](https://app.codecov.io/gh/carbon-design-system/carbon/pull/18553?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main   #18553      +/-   ##
==========================================
+ Coverage   84.29%   84.81%   +0.51%     
==========================================
  Files         409      407       -2     
  Lines       14709    14609     -100     
  Branches     4813     4763      -50     
==========================================
- Hits        12399    12390       -9     
+ Misses       2146     2067      -79     
+ Partials      164      152      -12     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/carbon-design-system/carbon/pull/18553?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=carbon-design-system).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- 📦 [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: carbon-automation[bot]
created_at: 2025-03-17T16:56:46Z
body: Hey there! v11.78.1 was just released that references this issue/PR.
author_association: CONTRIBUTOR