0

I'm encountering a peculiar issue with the positioning of Material-UI (MUI) Snackbar components (and other statically positioned components) when they are wrapped in a motion.div from the framer-motion library. It seems that the animation applied to the motion.div is affecting the positioning of the Snackbar, causing it to behave unexpectedly.

This is related to Why does applying a CSS-Filter on the parent break the child positioning?

Example: https://codesandbox.io/s/boring-silence-5c4gxy?file=/src/App.js:727-836

Note that all direct and indirect children of About will have the same issue making wrapping a page in a motion.div extremely difficult.

Removing motion.div in About brings the Snackbars to their correct position.

const About = () => (
  <Container maxWidth="xl">
    <nav>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
      </ul>
    </nav>

    <h1>About Page</h1>
    <SimpleSnackbar />
  </Container>
);

FreeDom Sy
  • 9
  • 1
  • 3

1 Answers1

0

It's because the filter property creates a containing block where any fixed or absolute child we be positioned relative to it. You can fix this by using display: contents on the motion.div element which causes the children to appear as a direct children of the motion.div element's parent, ignoring the element itself.

Below is the code sample:

const About = () => (
  <motion.div
    initial={{ filter: "blur(4px)" }}
    animate={{ filter: "blur(0)" }}
    exit={{ filter: "blur(4px)" }}
    style={{ display: "contents" }}
  >
    <Container maxWidth="xl">
      <nav>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
        </ul>
      </nav>

      <h1>About Page</h1>
      <SimpleSnackbar />
    </Container>
  </motion.div>
);

If the above solution is breaking the animation, another solution would be adding a fragment wrapper around the component. It will maintain the animation and place the snackbar in the proper position.

const About = () => (
  <>
    <motion.div
      initial={{ x: "-500%" }}
      animate={{ x: 100 }}
      exit={{ x: "100%" }}
    >
      <Container maxWidth="xl" style={{ backgroundColor: "red", margin: 5 }}>
        <nav>
          <ul>
            <li>
              <Link to="/home">Home</Link>
            </li>
          </ul>
        </nav>

        <h1>About Page</h1>
      </Container>
    </motion.div>
    <SimpleSnackbar />
  </>
);
Muhi
  • 116
  • 1
  • 4
  • That solved it. Thank you very much. – FreeDom Sy Aug 19 '23 at 17:43
  • Actually, this works (technically), but it completely breaks the animation which I did not notice it first. https://codesandbox.io/s/boring-silence-5c4gxy?file=/src/App.js:405-441 – FreeDom Sy Aug 19 '23 at 18:10
  • You're right, did not realize that the animation was broken. Another solution would be adding a fragment wrapper around the component. It will maintain the animation and place the `snackbar` in the proper position. I just updated the solution. – Muhi Aug 19 '23 at 19:08
  • The animation works, but the `Snackbar` is still misplaced. https://codesandbox.io/s/boring-silence-5c4gxy?file=/src/App.js – FreeDom Sy Aug 19 '23 at 19:28
  • The `snackbar` element has to be outside of the `motion.div` container. – Muhi Aug 19 '23 at 20:03
  • I see but I need it to be inside of it. Thank you either way. – FreeDom Sy Aug 19 '23 at 20:18