From 714ff7baba621ee07de33d186a98edb328ec3490 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Tue, 7 Apr 2026 13:10:00 -0400 Subject: [PATCH 1/4] chore: graduate primer_react_overlay_max_height_clamp_to_viewport --- .../src/ActionMenu/ActionMenu.module.css | 29 +++---------------- packages/react/src/ActionMenu/ActionMenu.tsx | 3 -- .../src/FeatureFlags/DefaultFeatureFlags.ts | 1 - packages/react/src/Overlay/Overlay.module.css | 29 +++---------------- packages/react/src/Overlay/Overlay.tsx | 2 -- 5 files changed, 8 insertions(+), 56 deletions(-) diff --git a/packages/react/src/ActionMenu/ActionMenu.module.css b/packages/react/src/ActionMenu/ActionMenu.module.css index 4bcd8af0282..af8f43f9067 100644 --- a/packages/react/src/ActionMenu/ActionMenu.module.css +++ b/packages/react/src/ActionMenu/ActionMenu.module.css @@ -25,43 +25,22 @@ /* Max-height size tokens (mirror Overlay sizes) */ &:where([data-max-height-xsmall]) { - max-height: 192px; - } - - &:where([data-max-height-small]) { - max-height: 256px; - } - - &:where([data-max-height-medium]) { - max-height: 320px; - } - - &:where([data-max-height-large]) { - max-height: 432px; - } - - &:where([data-max-height-xlarge]) { - max-height: 600px; - } - - /* Max-height size tokens clamped to viewport (enabled via feature flag) */ - &:where([data-max-height-clamp-to-viewport][data-max-height-xsmall]) { max-height: min(192px, 100dvh); } - &:where([data-max-height-clamp-to-viewport][data-max-height-small]) { + &:where([data-max-height-small]) { max-height: min(256px, 100dvh); } - &:where([data-max-height-clamp-to-viewport][data-max-height-medium]) { + &:where([data-max-height-medium]) { max-height: min(320px, 100dvh); } - &:where([data-max-height-clamp-to-viewport][data-max-height-large]) { + &:where([data-max-height-large]) { max-height: min(432px, 100dvh); } - &:where([data-max-height-clamp-to-viewport][data-max-height-xlarge]) { + &:where([data-max-height-xlarge]) { max-height: min(600px, 100dvh); } diff --git a/packages/react/src/ActionMenu/ActionMenu.tsx b/packages/react/src/ActionMenu/ActionMenu.tsx index 47a61ae8c42..c43b949bd90 100644 --- a/packages/react/src/ActionMenu/ActionMenu.tsx +++ b/packages/react/src/ActionMenu/ActionMenu.tsx @@ -330,8 +330,6 @@ const Overlay: FCWithSlotMarker> = ({ } }, [anchorRef]) - const featureFlagMaxHeightClampToViewport = useFeatureFlag('primer_react_overlay_max_height_clamp_to_viewport') - const isInsideDialog = useContext(DialogContext) !== undefined return ( @@ -354,7 +352,6 @@ const Overlay: FCWithSlotMarker> = ({ ref={containerRef} className={styles.ActionMenuContainer} data-variant={responsiveVariant} - {...(featureFlagMaxHeightClampToViewport ? {'data-max-height-clamp-to-viewport': ''} : {})} {...(overlayProps.overflow ? {[`data-overflow-${overlayProps.overflow}`]: ''} : {})} {...(overlayProps.maxHeight ? {[`data-max-height-${overlayProps.maxHeight}`]: ''} : {})} > diff --git a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts index a52924d7dc3..4b4cdf23ea2 100644 --- a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts +++ b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts @@ -7,5 +7,4 @@ export const DefaultFeatureFlags = FeatureFlagScope.create({ primer_react_select_panel_order_selected_at_top: false, primer_react_select_panel_remove_active_descendant: false, primer_react_spinner_synchronize_animations: false, - primer_react_overlay_max_height_clamp_to_viewport: false, }) diff --git a/packages/react/src/Overlay/Overlay.module.css b/packages/react/src/Overlay/Overlay.module.css index 69dc40cd1b2..94d15f3d27e 100644 --- a/packages/react/src/Overlay/Overlay.module.css +++ b/packages/react/src/Overlay/Overlay.module.css @@ -102,43 +102,22 @@ } &:where([data-max-height-xsmall]) { - max-height: 192px; - } - - &:where([data-max-height-small]) { - max-height: 256px; - } - - &:where([data-max-height-medium]) { - max-height: 320px; - } - - &:where([data-max-height-large]) { - max-height: 432px; - } - - &:where([data-max-height-xlarge]) { - max-height: 600px; - } - - /* Max-height size tokens clamped to viewport (enabled via feature flag) */ - &:where([data-max-height-clamp-to-viewport][data-max-height-xsmall]) { max-height: min(192px, 100dvh); } - &:where([data-max-height-clamp-to-viewport][data-max-height-small]) { + &:where([data-max-height-small]) { max-height: min(256px, 100dvh); } - &:where([data-max-height-clamp-to-viewport][data-max-height-medium]) { + &:where([data-max-height-medium]) { max-height: min(320px, 100dvh); } - &:where([data-max-height-clamp-to-viewport][data-max-height-large]) { + &:where([data-max-height-large]) { max-height: min(432px, 100dvh); } - &:where([data-max-height-clamp-to-viewport][data-max-height-xlarge]) { + &:where([data-max-height-xlarge]) { max-height: min(600px, 100dvh); } diff --git a/packages/react/src/Overlay/Overlay.tsx b/packages/react/src/Overlay/Overlay.tsx index f495d015730..00d2483c09d 100644 --- a/packages/react/src/Overlay/Overlay.tsx +++ b/packages/react/src/Overlay/Overlay.tsx @@ -192,7 +192,6 @@ const Overlay = React.forwardRef( forwardedRef, // eslint-disable-next-line @typescript-eslint/no-explicit-any ): ReactElement => { - const featureFlagMaxHeightClampToViewport = useFeatureFlag('primer_react_overlay_max_height_clamp_to_viewport') const overlayRef = useRef(null) const mergedRef = useMergedRefs(forwardedRef, overlayRef) const slideAnimationDistance = 8 // var(--base-size-8), hardcoded to do some math @@ -245,7 +244,6 @@ const Overlay = React.forwardRef( height={height} visibility={visibility} data-responsive={responsiveVariant} - {...(featureFlagMaxHeightClampToViewport ? {'data-max-height-clamp-to-viewport': ''} : {})} {...props} /> ) From 3b157e6ade207ec69946f6589dce635be1a834e0 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Tue, 7 Apr 2026 13:13:43 -0400 Subject: [PATCH 2/4] add changeset --- .changeset/graduate-overlay-max-height-clamp.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/graduate-overlay-max-height-clamp.md diff --git a/.changeset/graduate-overlay-max-height-clamp.md b/.changeset/graduate-overlay-max-height-clamp.md new file mode 100644 index 00000000000..50f15c499c8 --- /dev/null +++ b/.changeset/graduate-overlay-max-height-clamp.md @@ -0,0 +1,7 @@ +--- +'@primer/react': minor +--- + +Overlay: Graduate `primer_react_overlay_max_height_clamp_to_viewport` feature flag + +The max-height of overlays is now clamped to the viewport height by default using `min(size, 100dvh)`. This prevents overlays from extending beyond the viewport on smaller screens. From cf4f8414b10e699729bb5d453ca4db5cd77c25e7 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Tue, 7 Apr 2026 13:21:53 -0400 Subject: [PATCH 3/4] remove unused import --- packages/react/src/ActionMenu/ActionMenu.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/src/ActionMenu/ActionMenu.tsx b/packages/react/src/ActionMenu/ActionMenu.tsx index c43b949bd90..2c7e3ec4b9c 100644 --- a/packages/react/src/ActionMenu/ActionMenu.tsx +++ b/packages/react/src/ActionMenu/ActionMenu.tsx @@ -18,7 +18,6 @@ import styles from './ActionMenu.module.css' import {useResponsiveValue, type ResponsiveValue} from '../hooks/useResponsiveValue' import {isSlot} from '../utils/is-slot' import type {FCWithSlotMarker, WithSlotMarker} from '../utils/types/Slots' -import {useFeatureFlag} from '../FeatureFlags' import {DialogContext} from '../Dialog/Dialog' export type MenuCloseHandler = ( From b90c0cec466186f0d68c895aab1ef2d74f3e3090 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:43:48 +0000 Subject: [PATCH 4/4] fix: add 100vh fallbacks for dvh in Overlay and ActionMenu CSS Agent-Logs-Url: https://github.com/primer/react/sessions/63c4dcf7-79fd-4d70-83db-9b7507eeb6f5 Co-authored-by: francinelucca <40550942+francinelucca@users.noreply.github.com> --- .../src/ActionMenu/ActionMenu.module.css | 30 +++++++++++++++---- packages/react/src/Overlay/Overlay.module.css | 30 +++++++++++++++---- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/packages/react/src/ActionMenu/ActionMenu.module.css b/packages/react/src/ActionMenu/ActionMenu.module.css index af8f43f9067..5b6780562d5 100644 --- a/packages/react/src/ActionMenu/ActionMenu.module.css +++ b/packages/react/src/ActionMenu/ActionMenu.module.css @@ -25,23 +25,43 @@ /* Max-height size tokens (mirror Overlay sizes) */ &:where([data-max-height-xsmall]) { - max-height: min(192px, 100dvh); + max-height: min(192px, 100vh); + + @supports (height: 100dvh) { + max-height: min(192px, 100dvh); + } } &:where([data-max-height-small]) { - max-height: min(256px, 100dvh); + max-height: min(256px, 100vh); + + @supports (height: 100dvh) { + max-height: min(256px, 100dvh); + } } &:where([data-max-height-medium]) { - max-height: min(320px, 100dvh); + max-height: min(320px, 100vh); + + @supports (height: 100dvh) { + max-height: min(320px, 100dvh); + } } &:where([data-max-height-large]) { - max-height: min(432px, 100dvh); + max-height: min(432px, 100vh); + + @supports (height: 100dvh) { + max-height: min(432px, 100dvh); + } } &:where([data-max-height-xlarge]) { - max-height: min(600px, 100dvh); + max-height: min(600px, 100vh); + + @supports (height: 100dvh) { + max-height: min(600px, 100dvh); + } } &:where([data-max-height-fit-content]) { diff --git a/packages/react/src/Overlay/Overlay.module.css b/packages/react/src/Overlay/Overlay.module.css index 94d15f3d27e..ee9cdc459a0 100644 --- a/packages/react/src/Overlay/Overlay.module.css +++ b/packages/react/src/Overlay/Overlay.module.css @@ -102,23 +102,43 @@ } &:where([data-max-height-xsmall]) { - max-height: min(192px, 100dvh); + max-height: min(192px, 100vh); + + @supports (height: 100dvh) { + max-height: min(192px, 100dvh); + } } &:where([data-max-height-small]) { - max-height: min(256px, 100dvh); + max-height: min(256px, 100vh); + + @supports (height: 100dvh) { + max-height: min(256px, 100dvh); + } } &:where([data-max-height-medium]) { - max-height: min(320px, 100dvh); + max-height: min(320px, 100vh); + + @supports (height: 100dvh) { + max-height: min(320px, 100dvh); + } } &:where([data-max-height-large]) { - max-height: min(432px, 100dvh); + max-height: min(432px, 100vh); + + @supports (height: 100dvh) { + max-height: min(432px, 100dvh); + } } &:where([data-max-height-xlarge]) { - max-height: min(600px, 100dvh); + max-height: min(600px, 100vh); + + @supports (height: 100dvh) { + max-height: min(600px, 100dvh); + } } &:where([data-max-height-fit-content]) {