From d37b9b8e468b7b2c9cda8b27fe7019bb905ad2bf Mon Sep 17 00:00:00 2001 From: Shane Date: Tue, 15 Jul 2025 12:00:31 -0700 Subject: [PATCH] fix(modal): improve card modal background transition from portrait to landscape (#30551) Issue number: resolves internal --------- ## What is the current behavior? Currently, if you have multiple card modals open in portrait view and then transition to landscape, the background will not be transformed properly and will not cover the entire screen as expected. https://github.com/user-attachments/assets/bb2c7015-adb6-4c3b-8bc5-13360275fcf4 ## What is the new behavior? Now, we're correctly targeting the right element and will transform it as expected. This allows a more consistent UX for these transitions. https://github.com/user-attachments/assets/fff72387-d78b-4df4-a234-d89b54f694c4 ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information [Relevant (broken) screen (main branch)](https://ionic-framework-git-main-ionic1.vercel.app/src/components/modal/test/card?ionic:mode=ios) [Relevant (fixed) screen (this branch)](https://ionic-framework-git-fix-modal-landscape-background-ionic1.vercel.app/src/components/modal/test/card?ionic:mode=ios) --- .../modal/animations/ios.transition.ts | 26 ++++++------------- core/src/components/modal/modal.tsx | 2 +- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/core/src/components/modal/animations/ios.transition.ts b/core/src/components/modal/animations/ios.transition.ts index 6ce2cd75e1..264286db6b 100644 --- a/core/src/components/modal/animations/ios.transition.ts +++ b/core/src/components/modal/animations/ios.transition.ts @@ -72,22 +72,22 @@ export const portraitToLandscapeTransition = ( // need to care about layering and modal-specific styles. const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE; const fromTransform = `translateY(-10px) scale(${toPresentingScale})`; - const toTransform = `translateY(-10px) scale(${toPresentingScale})`; + const toTransform = `translateY(0px) scale(1)`; presentingAnimation - .addElement(presentingElRoot.querySelector('.modal-wrapper')!) + .addElement(presentingEl) .afterStyles({ transform: toTransform, }) .fromTo('transform', fromTransform, toTransform) - .fromTo('filter', 'contrast(0.85)', 'contrast(0.85)'); // Keep same contrast for card + .fromTo('filter', 'contrast(0.85)', 'contrast(1)'); const shadowAnimation = createAnimation() .addElement(presentingElRoot.querySelector('.modal-shadow')!) .afterStyles({ transform: toTransform, + opacity: '0', }) - .fromTo('opacity', '0', '0') // Shadow stays hidden in landscape for card modals .fromTo('transform', fromTransform, toTransform); baseAnimation.addAnimation([presentingAnimation, shadowAnimation]); @@ -148,17 +148,8 @@ export const landscapeToPortraitTransition = ( presentingAnimation .addElement(presentingEl) - .beforeStyles({ - transform: 'translateY(0px) scale(1)', - 'transform-origin': 'top center', - overflow: 'hidden', - }) .afterStyles({ transform: toTransform, - 'border-radius': '10px 10px 0 0', - filter: 'contrast(0.85)', - overflow: 'hidden', - 'transform-origin': 'top center', }) .beforeAddWrite(() => bodyEl.style.setProperty('background-color', 'black')) .keyframes([ @@ -173,22 +164,21 @@ export const landscapeToPortraitTransition = ( // to handle layering and modal-specific styles. const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE; const fromTransform = `translateY(-10px) scale(${toPresentingScale})`; - const toTransform = `translateY(-10px) scale(${toPresentingScale})`; + const toTransform = `translateY(0) scale(1)`; presentingAnimation - .addElement(presentingElRoot.querySelector('.modal-wrapper')!) + .addElement(presentingEl) .afterStyles({ transform: toTransform, }) - .fromTo('transform', fromTransform, toTransform) - .fromTo('filter', 'contrast(0.85)', 'contrast(0.85)'); // Keep same contrast for card + .fromTo('transform', fromTransform, toTransform); const shadowAnimation = createAnimation() .addElement(presentingElRoot.querySelector('.modal-shadow')!) .afterStyles({ transform: toTransform, + opacity: '0', }) - .fromTo('opacity', '0', '0') // Shadow stays hidden .fromTo('transform', fromTransform, toTransform); baseAnimation.addAnimation([presentingAnimation, shadowAnimation]); diff --git a/core/src/components/modal/modal.tsx b/core/src/components/modal/modal.tsx index 7843a47961..b1a494430d 100644 --- a/core/src/components/modal/modal.tsx +++ b/core/src/components/modal/modal.tsx @@ -1122,7 +1122,7 @@ export class Modal implements ComponentInterface, OverlayInterface { wrapperEl.style.opacity = '1'; } - if (presentingElement) { + if (presentingElement?.tagName === 'ION-MODAL') { const isPortrait = window.innerWidth < 768; if (isPortrait) {