diff --git a/.yarn/cache/tabbable-npm-6.2.0-5a74c8b4e2-980fa73476.zip b/.yarn/cache/tabbable-npm-6.2.0-5a74c8b4e2-980fa73476.zip
new file mode 100644
index 000000000000..5dd7c388a803
Binary files /dev/null and b/.yarn/cache/tabbable-npm-6.2.0-5a74c8b4e2-980fa73476.zip differ
diff --git a/packages/feature-flags/feature-flags.yml b/packages/feature-flags/feature-flags.yml
index 88a21e6e420a..db06ca3a7908 100644
--- a/packages/feature-flags/feature-flags.yml
+++ b/packages/feature-flags/feature-flags.yml
@@ -42,3 +42,7 @@ feature-flags:
description: >
Enable the new TreeView controllable API
enabled: false
+ - name: enable-experimental-focus-wrap-without-sentinels
+ description: >
+ Enable the new focus wrap behavior that doesn't use sentinel nodes
+ enabled: false
diff --git a/packages/react/package.json b/packages/react/package.json
index 159b0f540058..f98a8b95b854 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -65,6 +65,7 @@
"lodash.throttle": "^4.1.1",
"prop-types": "^15.7.2",
"react-is": "^18.2.0",
+ "tabbable": "^6.2.0",
"use-resize-observer": "^6.0.0",
"wicg-inert": "^3.1.1",
"window-or-global": "^1.0.1"
diff --git a/packages/react/src/components/ComposedModal/ComposedModal.featureflag.mdx b/packages/react/src/components/ComposedModal/ComposedModal.featureflag.mdx
new file mode 100644
index 000000000000..b798b2923b25
--- /dev/null
+++ b/packages/react/src/components/ComposedModal/ComposedModal.featureflag.mdx
@@ -0,0 +1,27 @@
+# ComposedModal
+
+[Source code](https://github.com/carbon-design-system/carbon/tree/main/packages/react/src/components/ComposedModal)
+ |
+[Usage guidelines](https://www.carbondesignsystem.com/components/modal/usage)
+ |
+[Accessibility](https://www.carbondesignsystem.com/components/modal/accessibility)
+
+## Experimental focus wrap without sentinels
+
+`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.
+
+```js
+
+
+
+```
diff --git a/packages/react/src/components/ComposedModal/ComposedModal.featureflag.stories.js b/packages/react/src/components/ComposedModal/ComposedModal.featureflag.stories.js
new file mode 100644
index 000000000000..3d004a4131f8
--- /dev/null
+++ b/packages/react/src/components/ComposedModal/ComposedModal.featureflag.stories.js
@@ -0,0 +1,430 @@
+/**
+ * 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.
+ */
+
+import React, { useState, useRef } from 'react';
+import ReactDOM from 'react-dom';
+import ComposedModal, { ModalBody } from './ComposedModal';
+import { ModalHeader } from './ModalHeader';
+import { ModalFooter } from './ModalFooter';
+import MultiSelect from '../MultiSelect';
+import Dropdown from '../Dropdown';
+import Select from '../Select';
+import SelectItem from '../SelectItem';
+import TextInput from '../TextInput';
+import Button from '../Button';
+import {
+ StructuredListWrapper,
+ StructuredListHead,
+ StructuredListBody,
+ StructuredListRow,
+ StructuredListCell,
+} from '../StructuredList';
+import mdx from './ComposedModal.featureflag.mdx';
+import { WithFeatureFlags } from '../../../.storybook/templates/WithFeatureFlags';
+
+export default {
+ title: 'Experimental/Feature Flags/ComposedModal',
+ component: ComposedModal,
+ subcomponents: {
+ ModalHeader,
+ ModalBody,
+ ModalFooter,
+ },
+ parameters: {
+ docs: {
+ page: mdx,
+ },
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+};
+
+export const Default = () => {
+ const [open, setOpen] = useState(true);
+ return (
+ <>
+
+ setOpen(false)}>
+
+
+
+ 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.
+
+
+
+
+
+
+ >
+ );
+};
+
+export const FullWidth = () => {
+ const [open, setOpen] = useState(true);
+ return (
+ <>
+
+ setOpen(false)} isFullWidth>
+
+
+
+
+
+
+ Column A
+
+
+ Column B
+
+
+ Column C
+
+
+
+
+
+ Row 1
+ Row 1
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc
+ dui magna, finibus id tortor sed, aliquet bibendum augue.
+ Aenean posuere sem vel euismod dignissim. Nulla ut cursus
+ dolor. Pellentesque vulputate nisl a porttitor interdum.
+
+
+
+ Row 2
+ Row 2
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc
+ dui magna, finibus id tortor sed, aliquet bibendum augue.
+ Aenean posuere sem vel euismod dignissim. Nulla ut cursus
+ dolor. Pellentesque vulputate nisl a porttitor interdum.
+
+
+
+ Row 3
+ Row 3
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc
+ dui magna, finibus id tortor sed, aliquet bibendum augue.
+ Aenean posuere sem vel euismod dignissim. Nulla ut cursus
+ dolor. Pellentesque vulputate nisl a porttitor interdum.
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export const PassiveModal = () => {
+ const [open, setOpen] = useState(true);
+ return (
+ <>
+
+ setOpen(false)}>
+
+
+
+ >
+ );
+};
+
+export const WithStateManager = () => {
+ const button = useRef();
+
+ /**
+ * Simple state manager for modals.
+ */
+ const ModalStateManager = ({
+ renderLauncher: LauncherContent,
+ children: ModalContent,
+ }) => {
+ const [open, setOpen] = useState(false);
+ return (
+ <>
+ {!ModalContent || typeof document === 'undefined'
+ ? null
+ : ReactDOM.createPortal(
+ ,
+ document.body
+ )}
+ {LauncherContent && }
+ >
+ );
+ };
+ return (
+ (
+
+ )}>
+ {({ open, setOpen }) => (
+ {
+ setOpen(false);
+ }}
+ launcherButtonRef={button}>
+
+
+
+ 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.
+
+ 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.
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus
+ eu nibh odio. Nunc a consequat est, id porttitor sapien. Proin vitae
+ leo vitae orci tincidunt auctor eget eget libero. Ut tincidunt
+ ultricies fringilla. Aliquam erat volutpat. Aenean arcu odio,
+ elementum vel vehicula vitae, porttitor ac lorem. Sed viverra elit
+ ac risus tincidunt fermentum. Ut sollicitudin nibh id risus ornare
+ ornare. Etiam gravida orci ut lectus dictum, quis ultricies felis
+ mollis. Mauris nec commodo est, nec faucibus nibh. Nunc commodo ante
+ quis pretium consectetur. Ut ac nisl vitae mi mattis vulputate a at
+ elit. Nullam porttitor ex eget mi feugiat mattis. Nunc non sodales
+ magna. Proin ornare tellus quis hendrerit egestas. Donec pharetra
+ leo nec molestie sollicitudin.{' '}
+
+ 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.
+
+ 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.
+