Open benjaminpadula opened 3 years ago
I also ran into this issue.
In Safari 14 and iOS 14, CSS transitions don't work correctly with React Modal's transition class ReactModal__Overlay--after-open
. Instead of transitioning, the element instantly appears. Here is where React Modal's documentation explains how to apply transitions.
In my project, I have a fullscreen modal that is off-screen. When the modal is opened, I want it to slide into view from the left. When the modal is closed, I want it to slide out of view.
To do that, I set the modal's default state to be off-screen by translating it completely to the left. Here the class .fullscreen-menu
is the modal's parent container.
.fullscreen-menu .ReactModal__Overlay {
transform: translateX(-100%);
}
Next, I create a helper function. Instead of applying the transition with the CSS class ReactModal__Overlay--after-open
, I apply it with JavaScript. The secret sauce here seems to be to wrap the function in a setTimeout
. Smarter people than me will know why this works; I can't explain it. It probably has something to do with the event loop.
const slideIn = () => {
setTimeout(() => {
// Get React Modal's overlay
const overlay = document.querySelector(
'.fullscreen-menu .ReactModal__Overlay'
);
// If the modal hasn't rendered correctly and is null, exit the function to prevent errors
if (!overlay) return;
// Apply the transition, and slide the modal into view from the left
overlay.style.transform = 'translateX(0)';
}, 0);
};
Now, when I instantiate my <Modal />
component, I call the function slideIn
in React Modal's onAfterOpen
prop.
<Modal
isOpen={fullscreenMenuIsOpen}
onRequestClose={closeFullscreenMenu}
onAfterOpen={() => {
slideIn();
}}
portalClassName="fullscreen-menu"
>
...
</Modal>
So, it appears that if you apply CSS transitions with JavaScript like this in conjunction with setTimeout
, they will work as expected in iOS 14 and Safari 14.
Summary:
CSS
after-open
transitions are failing to fire in Safari 14 & mobile SafariSteps to reproduce:
after-open
transition works)after-open
transition fails)Expected behavior:
Transitions should always work when defined correctly no matter what browser is being used
Link to example of issue:
Existing (broken): https://codesandbox.io/s/heuristic-jennings-8c54r (CSS transition works in Chrome and old Safari browsers, but NOT in Safari 14+ or mobile Safari)
"Fixed": https://codesandbox.io/s/safari-14-bug-experimental-zlzdl ("fixed" using
onAfterOpen
with special consumer-definedshow-open
class)Additional notes:
It struck me that perhaps the issue was that when the modal is mounted, it is immediately mounted with the open state and initially renders with the after-open class. It's actually kind of surprising, then that Chrome shows a transition, when technically nothing is really changing. Perhaps the old browser interpreter is inferring developer intent or doing something wrong. ¯\_(ツ)_\/¯
So I thought it was worth a try to ensure that the modal mounts in the closed state first, and then apply the open class. (See modified sandbox) And that seems to do the trick! But since it's likely a problem affecting everyone, I decided to try fixing it directly in
react-modal
so everyone could benefit from working transitions in Safari 14 & mobile Safari.Moral: When using CSS transitions, ensure that the component is rendered first before applying the class that triggers the transition. Test in Safari 14 to ensure you got it right, as other browser seem to be more lenient.