diff --git a/e2e/components/Overlay.test.ts b/e2e/components/Overlay.test.ts
index 556db3c4a10..3f604b69005 100644
--- a/e2e/components/Overlay.test.ts
+++ b/e2e/components/Overlay.test.ts
@@ -77,4 +77,51 @@ test.describe('Overlay ', () => {
}
})
}
+
+ // Test with CSS containment feature flag enabled
+ test.describe('with CSS containment feature flag', () => {
+ for (const story of stories) {
+ test.describe(story.title, () => {
+ for (const theme of themes) {
+ test.describe(theme, () => {
+ test('@vrt', async ({page}) => {
+ await visit(page, {
+ id: story.id,
+ globals: {
+ colorScheme: theme,
+ featureFlags: {
+ primer_react_overlay_css_containment: true,
+ },
+ },
+ args: {
+ open: true,
+ },
+ })
+
+ await expect(page).toHaveScreenshot(`Overlay.${story.title}.${theme}.with-containment.png`, {
+ animations: 'disabled',
+ })
+ })
+
+ test('axe @aat', async ({page}) => {
+ await visit(page, {
+ id: story.id,
+ globals: {
+ colorScheme: theme,
+ featureFlags: {
+ primer_react_overlay_css_containment: true,
+ },
+ },
+ args: {
+ open: true,
+ },
+ })
+
+ await expect(page).toHaveNoViolations()
+ })
+ })
+ }
+ })
+ }
+ })
})
diff --git a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts
index 4a2c0896f50..9b9ebd0fe51 100644
--- a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts
+++ b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts
@@ -4,6 +4,7 @@ export const DefaultFeatureFlags = FeatureFlagScope.create({
primer_react_action_list_item_as_button: false,
primer_react_breadcrumbs_overflow_menu: false,
primer_react_css_has_selector_perf: false,
+ primer_react_overlay_css_containment: false,
primer_react_overlay_overflow: false,
primer_react_select_panel_fullscreen_on_narrow: false,
primer_react_select_panel_order_selected_at_top: false,
diff --git a/packages/react/src/Overlay/Overlay.module.css b/packages/react/src/Overlay/Overlay.module.css
index a411b34c6f4..4ead53892ba 100644
--- a/packages/react/src/Overlay/Overlay.module.css
+++ b/packages/react/src/Overlay/Overlay.module.css
@@ -187,6 +187,10 @@
}
}
+.OverlayContainment {
+ contain: layout style;
+}
+
@media (prefers-reduced-motion: no-preference) {
.Overlay {
animation: overlay-appear 200ms cubic-bezier(0.33, 1, 0.68, 1);
diff --git a/packages/react/src/Overlay/Overlay.test.tsx b/packages/react/src/Overlay/Overlay.test.tsx
index d6dde8d506e..8ea6c6cc480 100644
--- a/packages/react/src/Overlay/Overlay.test.tsx
+++ b/packages/react/src/Overlay/Overlay.test.tsx
@@ -366,4 +366,28 @@ describe('Overlay', () => {
const container = getByRole('dialog')
expect(container).toHaveAttribute('data-reflow-container')
})
+
+ it('should not have OverlayContainment class if CSS containment FF is not enabled', async () => {
+ const user = userEvent.setup()
+ const {getByRole} = render()
+
+ await user.click(getByRole('button', {name: 'open overlay'}))
+
+ const container = getByRole('dialog')
+ expect(container).not.toHaveClass(classes.OverlayContainment)
+ })
+
+ it('should have OverlayContainment class if CSS containment FF is enabled', async () => {
+ const user = userEvent.setup()
+ const {getByRole} = render(
+
+
+ ,
+ )
+
+ await user.click(getByRole('button', {name: 'open overlay'}))
+
+ const container = getByRole('dialog')
+ expect(container).toHaveClass(classes.OverlayContainment)
+ })
})
diff --git a/packages/react/src/Overlay/Overlay.tsx b/packages/react/src/Overlay/Overlay.tsx
index 9f588146323..98959dac2b0 100644
--- a/packages/react/src/Overlay/Overlay.tsx
+++ b/packages/react/src/Overlay/Overlay.tsx
@@ -108,6 +108,7 @@ export const BaseOverlay = React.forwardRef(
forwardedRef,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): ReactElement => {
+ const cssContainmentEnabled = useFeatureFlag('primer_react_overlay_css_containment')
return (
)
},