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. diff --git a/packages/react/src/ActionMenu/ActionMenu.module.css b/packages/react/src/ActionMenu/ActionMenu.module.css index 4bcd8af0282..5b6780562d5 100644 --- a/packages/react/src/ActionMenu/ActionMenu.module.css +++ b/packages/react/src/ActionMenu/ActionMenu.module.css @@ -25,44 +25,43 @@ /* Max-height size tokens (mirror Overlay sizes) */ &:where([data-max-height-xsmall]) { - max-height: 192px; - } + max-height: min(192px, 100vh); - &:where([data-max-height-small]) { - max-height: 256px; + @supports (height: 100dvh) { + max-height: min(192px, 100dvh); + } } - &:where([data-max-height-medium]) { - max-height: 320px; - } + &:where([data-max-height-small]) { + max-height: min(256px, 100vh); - &:where([data-max-height-large]) { - max-height: 432px; + @supports (height: 100dvh) { + max-height: min(256px, 100dvh); + } } - &:where([data-max-height-xlarge]) { - max-height: 600px; - } + &:where([data-max-height-medium]) { + max-height: min(320px, 100vh); - /* 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); + @supports (height: 100dvh) { + max-height: min(320px, 100dvh); + } } - &:where([data-max-height-clamp-to-viewport][data-max-height-small]) { - max-height: min(256px, 100dvh); - } + &:where([data-max-height-large]) { + max-height: min(432px, 100vh); - &:where([data-max-height-clamp-to-viewport][data-max-height-medium]) { - max-height: min(320px, 100dvh); + @supports (height: 100dvh) { + max-height: min(432px, 100dvh); + } } - &:where([data-max-height-clamp-to-viewport][data-max-height-large]) { - max-height: min(432px, 100dvh); - } + &:where([data-max-height-xlarge]) { + max-height: min(600px, 100vh); - &:where([data-max-height-clamp-to-viewport][data-max-height-xlarge]) { - max-height: min(600px, 100dvh); + @supports (height: 100dvh) { + max-height: min(600px, 100dvh); + } } &:where([data-max-height-fit-content]) { diff --git a/packages/react/src/ActionMenu/ActionMenu.tsx b/packages/react/src/ActionMenu/ActionMenu.tsx index 47a61ae8c42..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 = ( @@ -330,8 +329,6 @@ const Overlay: FCWithSlotMarker> = ({ } }, [anchorRef]) - const featureFlagMaxHeightClampToViewport = useFeatureFlag('primer_react_overlay_max_height_clamp_to_viewport') - const isInsideDialog = useContext(DialogContext) !== undefined return ( @@ -354,7 +351,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..ee9cdc459a0 100644 --- a/packages/react/src/Overlay/Overlay.module.css +++ b/packages/react/src/Overlay/Overlay.module.css @@ -102,44 +102,43 @@ } &:where([data-max-height-xsmall]) { - max-height: 192px; - } + max-height: min(192px, 100vh); - &:where([data-max-height-small]) { - max-height: 256px; + @supports (height: 100dvh) { + max-height: min(192px, 100dvh); + } } - &:where([data-max-height-medium]) { - max-height: 320px; - } + &:where([data-max-height-small]) { + max-height: min(256px, 100vh); - &:where([data-max-height-large]) { - max-height: 432px; + @supports (height: 100dvh) { + max-height: min(256px, 100dvh); + } } - &:where([data-max-height-xlarge]) { - max-height: 600px; - } + &:where([data-max-height-medium]) { + max-height: min(320px, 100vh); - /* 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); + @supports (height: 100dvh) { + max-height: min(320px, 100dvh); + } } - &:where([data-max-height-clamp-to-viewport][data-max-height-small]) { - max-height: min(256px, 100dvh); - } + &:where([data-max-height-large]) { + max-height: min(432px, 100vh); - &:where([data-max-height-clamp-to-viewport][data-max-height-medium]) { - max-height: min(320px, 100dvh); + @supports (height: 100dvh) { + max-height: min(432px, 100dvh); + } } - &:where([data-max-height-clamp-to-viewport][data-max-height-large]) { - max-height: min(432px, 100dvh); - } + &:where([data-max-height-xlarge]) { + max-height: min(600px, 100vh); - &:where([data-max-height-clamp-to-viewport][data-max-height-xlarge]) { - max-height: min(600px, 100dvh); + @supports (height: 100dvh) { + max-height: min(600px, 100dvh); + } } &:where([data-max-height-fit-content]) { 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} /> )