A Responsive Modal With Flexbox and No Media Queries

← ← ←   5/2/2022, 2:29:26 AM | Posted by: Felippe Regazio


No bla bla bla, lets get things done. To a better comprehension of this post would be good for you to know something about HTML, SCSS and CSS Vars.

The ideia is to write a responsive modal front end structure using only flexbox and no media queries. A fluid modal. I'll try to highlight the key points of the (S)CSS style, anyway this is the result:

There is no behavior added on this modal, its Style Only. Do as you prefer or feel free to ask me in the comments how to add JS functions to open, close, callbacks etc.

So, lets start with the modal HTML. We gonna need a wrapper (which will also be our backdrop), and a content div to hold the header, main content and footer. Because we are using flexbox, this modal will be also dynamic, you could omit the header or the footer without break anything.

  
  <div class="container">
    <div class="simple-modal" tabindex="0">
          <div class="simple-modal__content">
              <header>
                  <h4>Title</h4>
                  <span>close</span>
              </header>
              
              <div class="modal-main">
                  <!-- Content goes here -->
              </div>

              <footer>
                  <button>Ok</button>
              </footer>
          </div>
      </div>
  </div>    
  

Really simple, huhn? There is no much to say about it. The container could be anything in the page, its for eye candy purposes only (we'll add a background on it).

The modal starts in the .simple-modal div. This will be our wrapper and backdrop. This is also where you apply the visibility and display rules to show or hide the modal when adding behavior.

Then we add the .simple-modal__content div, which will be the content of our modal, containing the header, main-content and footer.

So, lets start defining some CSS vars that we may need:

  
  .simple-modal {
    --gutter: 14px; // spacing reference
    --modal-color: #800000; // just color
    --soft-color: #fafafa; // just another color
  }    
  

Then lets do our wrapper/backdrop:

  
  .simple-modal {
    --gutter: 14px;
    --modal-color: #800000;
    --soft-color: #fafafa;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    z-index: 2000;
    position: fixed;
    background-color: rgba(0,0,0,.7);
    padding-top: 2%;
  }    
  

Key points: the .simple-modal div will be fixed on the screen, so will be where the modal content will be placed. The z-index will keep the modal always on front of the other page elements. The padding-top can be modified, but i think 2 or 3 percent scales really well from desk to mobo, this padding will determine how distant from the page top the modal content will be.

Now, lets add the content element, a blank square on the middle.
To do it we gonna use the .simple-modal__content div:

  
  .simple-modal {
    /* ... previous stuff ... */
    &__content {
      width: 95vw;
      max-width: 600px;
      height: 90vh;
      max-height: 700px;
      position: relative;
      overflow: hidden;
      border-radius: 4px;
      margin: 0 auto;
      background-color: #ffffff;
      display: flex;
      flex-direction: column;
    }
  }    
  

Key points: We start given the modal a width: 95vw and max-width: 600px. So, we get a decent content width on desktop which also fits well on mobile, cuz on < 600px screens, the width will be 95vh, or 95% of the viewport, given 5% of space to emulate the padding. The height and max-height here will do the same thing with the height.

Then at last not least, the display: flex will give a fluid behavior for our modal content elements. But flex is horizontal driven by default, so we use flex-direction: column to change it to vertical. Now, things will be vertically stretched on our modal content. Dont forget the margin:0 auto to centralize and overflow:hidden to prevent unwanted scroll bars.

Its time to add the modal elements:
Header, Main Content and Footer

  
  header {
    min-height: 60px;
    height: 60px;
    color: #ffffff;
    background-color: var(--modal-color);
    display: grid;
    padding-left: var(--gutter);
    align-items: center;
    grid-template-columns: auto 60px;
    h4 {
      margin: 0;
      text-align: left;
    }
    span {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 100%;
      width: 100%;
      font-size: 20px;
      opacity: .8;
      cursor: pointer;
      &:hover {
        opacity: 1;
      }
    }
  }
  .modal-main {
    flex: 1;
    text-align: left;
    overflow: auto;
    padding: var(--gutter);
  }
  footer {
    height: auto;
    text-align: right;
    border-top: solid 1px #cccccc;
    padding: var(--gutter);
    background-color: #ffffff;
    background-color: var(--soft-color);
    button, input {
      margin: 0;
      &:not(:last-child) {
        margin-right: var(--gutter);
      }
    }
  }
  

If you note, this part of the code its almost pure aesthetics. Almost no style behavior added, except for the flex:1 on the .modal-main.

The flex 1 tells to the .modal-main to grow along the parent as much as possible, but it will be stopped by the &__content div limits. Then we add the min-height on the header. For the footer id prefer to not to given a height and let the children do it for me, then when the footer is empty, it will became just a cool border on the bottom.

Conclusion

Because our modal content holder is display:flex with column direction, all the content will be vertically placed in a way that the main content (flex 1) will grow in a delimited size (&__content height and width) and without break its header, but will grow. Also important to add the overflow:auto on your main content .modal-main, so you can have large contents inside the .modal-main.

The final code must be like this:

  
  .simple-modal {
    --gutter: 14px;
    --modal-color: #800000;
    --soft-color: #fafafa;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    z-index: 2000;
    position: fixed;
    background-color: rgba(0,0,0,.7);
    padding-top: 2%;
    &__content {
      width: 95vw;
      height: 90vh;
      max-height: 700px;
      max-width: 600px;
      position: relative;
      overflow: hidden;
      border-radius: 4px;
      margin: 0 auto;
      background-color: #ffffff;
      display: flex;
      flex-direction: column;
      header {
        min-height: 60px;
        height: 60px;
        color: #ffffff;
        background-color: var(--modal-color);
        display: grid;
        padding-left: var(--gutter);
        align-items: center;
        grid-template-columns: auto 60px;
        h4 {
          margin: 0;
          text-align: left;
        }
        span {
          display: flex;
          align-items: center;
          justify-content: center;
          height: 100%;
          width: 100%;
          font-size: 20px;
          opacity: .8;
          cursor: pointer;
          &:hover {
            opacity: 1;
          }
        }
      }
      .modal-main {
        flex: 1;
        text-align: left;
        overflow: auto;
        padding: var(--gutter);
      }
      footer {
        height: auto;
        text-align: right;
        border-top: solid 1px #cccccc;
        padding: var(--gutter);
        background-color: #ffffff;
        background-color: var(--soft-color);
        button, input {
          margin: 0;
          &:not(:last-child) {
            margin-right: var(--gutter);
          }
        }
      }
    }
  }    
  

You can use the .container div to add a cool background:

  
  // just an eye candy
  .container {
    width: 100vw;
    height: 100vh;
    background-size: cover;
    background-repeat: no-repeat;
    background-image: url(http://picsum.photos/2000);
  }
  

Thats all folks :)