skip to Main Content

Have a look at this react code on creating a modal:

import * as React from "react";
import "./styles.css";

function Modal({ handleClose }) {
  return (
    <div onClick={handleClose}>
      <dialog>
        <header>
          <button onClick={handleClose}>X</button>
          <h2>Modal</h2>
        </header>
        <section>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
            tempor incididunt ut labore et dolore magna aliqua. Ullamcorper morbi
            tincidunt ornare massa. Morbi enim nunc faucibus a pellentesque sit
            amet. Duis tristique sollicitudin nibh sit amet commodo nulla facilisi
            nullam. Eget dolor morbi non arcu risus. Integer eget aliquet nibh
            praesent tristique magna sit amet purus. Nullam vehicula ipsum a arcu.
            Vitae proin sagittis nisl rhoncus mattis rhoncus. Risus feugiat in ante
            metus dictum at tempor.
          </p>
        </section>
      </dialog>
    </div>
  );
}

export default function App() {
  const [isOpen, setIsOpen] = React.useState(false);
  return (
    <div className="container">
      {/* Modal */}
      {isOpen && <Modal handleClose={() => setIsOpen(false)} />}

      {/* Main Content */}
      <main>
        <header>
          <h1>Test-Modal</h1>
        </header>
        {["red", "blue", "green", "pink", "purple", "yellow"].map(
          (color, index) => {
            return (
              <section
                key={index}
                style={{
                  backgroundColor: color,
                  width: "100%",
                  height: "50vh",
                }}
              />
            );
          }
        )}
        <button className="primary" onClick={() => setIsOpen(true)}>
          openModal
        </button>
      </main>
    </div>
  );
}

styles.css

html {
  background-color: black;
  color: white;
}

.container {
  margin: 0 auto;
  max-width: 1100px;
  padding: 50px;
  position: relative;
}

main {
  margin: auto;
  padding: 24px;
}

main > header {
  position: sticky;
  top: 0;
  background: black;
  text-align: center;
  padding: 24px;
}

dialog {
  position: fixed;
  display: grid;
  grid-template-rows: auto 1fr;
  top: 40px;
  background: transparent;
  width: 75vw;
  margin: auto;
  animation: expand 0.3s cubic-bezier(0.075, 0.82, 0.165, 1);
  border-radius: 8px;
  padding: 32px;
  z-index: 10;
  color: white;
}

dialog section {
  max-height: calc(90vh - 120px);
  overflow: auto;
}

dialog button {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
}

div:has(dialog) {
  background-color: rgba(0, 0, 0, 0.5);
  position: fixed;
  /* width: 100%; */
  /* height: 100%; */
  z-index: 9;

  display: flex;
  align-items: center;
  justify-content: center;
  inset: 0;
}

.primary {
  position: fixed;
  bottom: 40px;
  right: 40px;
}

You can run full code here.

I have a few questions:

  1. Why <main> is shinked and the contents are back to top when I open the modal?
  2. Why can’t I scroll the contents in main when the modal is opened?

I have worked on this for a whole night for solutions.

If I make the modal simpler like this:

<div
  style={{
    backgroundColor: "rgba(0,0,0,0.5)",
    zIndex: 9999,
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    inset: 0,
    position: "fixed",
  }}
  onClick={handleClose}
>
  <div style={{ backgroundColor: "white", padding: "30px" }}>
    <h2 style={{ color: "black" }}>some title...</h2>
    <p style={{ color: "black" }}>some content</p>
  </div>
</div>;

Then everything is fine.

The bug seems to be from css.

I want to figure out what caused the bug and how to fix it, if the modal is as complex as that.

2

Answers


  1. //App.js
    
    
    import "./styles.css";
    import React from "react";
    
    function Modal({ handleClose }) {
      return (
        <div onClick={handleClose}>
          <dialog>
            <header>
              <button onClick={handleClose}>X</button>
              <h2>Modal</h2>
            </header>
            <section>
              <p>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
                eiusmod tempor incididunt ut labore et dolore magna aliqua.
                Ullamcorper morbi tincidunt ornare massa. Morbi enim nunc faucibus a
                pellentesque sit amet. Duis tristique sollicitudin nibh sit amet
                commodo nulla facilisi nullam. Eget dolor morbi non arcu risus.
                Integer eget aliquet nibh praesent tristique magna sit amet purus.
                Nullam vehicula ipsum a arcu. Vitae proin sagittis nisl rhoncus
                mattis rhoncus. Risus feugiat in ante metus dictum at tempor.
              </p>
            </section>
          </dialog>
        </div>
      );
    }
    
    export default function App() {
      const [isOpen, setIsOpen] = React.useState(false);
      return (
        <div className="container">
          {/* Modal */}
          {isOpen && <Modal handleClose={() => setIsOpen(false)} />}
    
          {/* Main Content */}
          <main className="colors">
            <header>
              <h1>Test-Modal</h1>
            </header>
            {["red", "blue", "green", "pink", "purple", "yellow"].map(
              (color, index) => {
                return (
                  <section
                    key={index}
                    style={{
                      backgroundColor: color,
                      width: "100%",
                      height: "50vh",
                    }}
                  />
                );
              }
            )}
            <button
              className="primary"
              onClick={() => {
                setIsOpen(true);
                 //make body scrollable.
                if (setIsOpen) {
                  document.body.style.overflow = "scroll";
                }
              }}
            >
              openModal
            </button>
          </main>
        </div>
      );
    }
    /* style.css*/
    
    
    html {
      background-color: black;
      color: white;
    }
    
    .container {
      margin: 0 auto;
      max-width: 1100px;
      padding: 50px;
      position: relative;
    }
    
    main {
      margin: auto;
      padding: 24px;
    }
    
    main > header {
      position: sticky;
      top: 0;
      background: black;
      text-align: center;
      padding: 24px;
    }
    
    dialog {
      position: fixed;
      display: grid;
      grid-template-rows: auto 1fr;
      top: 40px;
    
      width: 75vw;
      margin: auto;
      animation: expand 0.3s cubic-bezier(0.075, 0.82, 0.165, 1);
      border-radius: 8px;
      padding: 32px;
      z-index: 10;
      /* changed for visibility */
      background: rgba(255, 255, 255, 0.8);
      color: #111;
      line-height: 1.3;
    }
    
    dialog section {
      max-height: calc(90vh - 120px);
      overflow: auto;
    }
    
    dialog button {
      position: absolute;
      top: 16px;
      right: 16px;
      width: 32px;
      height: 32px;
      border-radius: 50%;
    }
    
    div:has(dialog) {
      background-color: rgba(0, 0, 0, 0.5);
      position: relative;
      /* width: 100%; */
      /* height: 100%; */
      z-index: 9;
    
      display: block;
      align-items: center;
      justify-content: center;
      inset: 0;
    }
    
    .primary {
      position: fixed;
      bottom: 40px;
      right: 40px;
    }

    you can use ReactModal from "react-modal" which gives you functionality when isOpen prop is true, In other words when Modal is open.

    It has function like "onAfterOpen". Read more on –
    http://reactcommunity.org/react-modal/

    But, for now you can use this work around. when "isSetOpen" is true, I made some changes in App.
    Also made some changes in dialog element for make it more visible. Like background color, color, line-height.

    Login or Signup to reply.
  2. The issue lies div:has(dialog). You have added flex properties with in it which is messing layout of main content and if you remove it then issue will get resolved and according to my inspection flex properties is not serving any purpose to your layout and if it does then do share.

    Also instead of using position: fixed use position: absolute it is best for this usecase. Below is updated css which you need to fix the issue. If I have misunderstood or missed something then do share.

    div:has(dialog) {
        background-color: rgba(0, 0, 0, 0.5);
        position: absolute; /* Added Absolute*/
        z-index: 9;
        /* display: flex; */ /* Removed Flex*/
        align-items: center;
        justify-content: center;
        inset: 0;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search