{ __( 'Enter a custom name for this block.' ) }
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index ef00a1afd6e2d7..9fc7a1f98704fd 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -4,6 +4,7 @@
### Enhancements
+- Add new option `firstContentElement` to Modal's `focusOnMount` prop to allow consumers to focus the first element within the Modal's **contents** ([#54590](https://github.com/WordPress/gutenberg/pull/54590)).
- `Notice`: Improve accessibility by adding visually hidden text to clarify what a notice text is about and the notice type (success, error, warning, info) ([#54498](https://github.com/WordPress/gutenberg/pull/54498)).
- Making Circular Option Picker a `listbox`. Note that while this changes some public API, new props are optional, and currently have default values; this will change in another patch ([#52255](https://github.com/WordPress/gutenberg/pull/52255)).
- `ToggleGroupControl`: Rewrite backdrop animation using framer motion shared layout animations, add better support for controlled and uncontrolled modes ([#50278](https://github.com/WordPress/gutenberg/pull/50278)).
diff --git a/packages/components/src/modal/README.md b/packages/components/src/modal/README.md
index 01cad7d6ff2e0f..31cfdfa59c8c70 100644
--- a/packages/components/src/modal/README.md
+++ b/packages/components/src/modal/README.md
@@ -187,10 +187,16 @@ Titles are required for accessibility reasons, see `aria.labelledby` and `title`
- Required: No
-#### `focusOnMount`: `boolean | 'firstElement'`
+#### `focusOnMount`: `boolean | 'firstElement'` | 'firstContentElement'
If this property is true, it will focus the first tabbable element rendered in the modal.
+If this property is false, focus will not be transferred and it is the responsibility of the consumer to ensure accessible focus management.
+
+If set to `firstElement` focus will be placed on the first tabbable element anywhere within the Modal.
+
+If set to `firstContentElement` focus will be placed on the first tabbable element within the Modal's **content** (i.e. children). Note that it is the responsibility of the consumer to ensure there is at least one tabbable element within the children **or the focus will be lost**.
+
- Required: No
- Default: `true`
diff --git a/packages/components/src/modal/index.tsx b/packages/components/src/modal/index.tsx
index d21a3f9ae3535e..139b0805a04dbb 100644
--- a/packages/components/src/modal/index.tsx
+++ b/packages/components/src/modal/index.tsx
@@ -71,11 +71,23 @@ function UnforwardedModal(
} = props;
const ref = useRef< HTMLDivElement >();
+
const instanceId = useInstanceId( Modal );
const headingId = title
? `components-modal-header-${ instanceId }`
: aria.labelledby;
- const focusOnMountRef = useFocusOnMount( focusOnMount );
+
+ // The focus hook does not support 'firstContentElement' but this is a valid
+ // value for the Modal's focusOnMount prop. The following code ensures the focus
+ // hook will focus the first focusable node within the element to which it is applied.
+ // When `firstContentElement` is passed as the value of the focusOnMount prop,
+ // the focus hook is applied to the Modal's content element.
+ // Otherwise, the focus hook is applied to the Modal's ref. This ensures that the
+ // focus hook will focus the first element in the Modal's **content** when
+ // `firstContentElement` is passed.
+ const focusOnMountRef = useFocusOnMount(
+ focusOnMount === 'firstContentElement' ? 'firstElement' : focusOnMount
+ );
const constrainedTabbingRef = useConstrainedTabbing();
const focusReturnRef = useFocusReturn();
const focusOutsideProps = useFocusOutside( onRequestClose );
@@ -223,7 +235,9 @@ function UnforwardedModal(
ref={ useMergeRefs( [
constrainedTabbingRef,
focusReturnRef,
- focusOnMountRef,
+ focusOnMount !== 'firstContentElement'
+ ? focusOnMountRef
+ : null,
] ) }
role={ role }
aria-label={ contentLabel }
@@ -283,7 +297,17 @@ function UnforwardedModal(
) }
) }
-