I hate modals. Not because they are not useful or because they are annoying. No, I hate them because 80% of the time they are implemented poorly on the web. Either they are the wrong size for the inner content or they are not centered in the middle of the screen.

One of the greatest offenders is surprisingly Bootstrap. For all the good Bootstrap provides, its modals are more or less trash. They appear at the top of the screen, and if they are too big, the entire modal scrolls instead of just the inner content. As a user of modern operating systems, I don’t expect either of these behaviors.

In this tutorial, I will solve this problem by showing you how to create a centered, responsive modal with a fixed header, like the one in the animated gif up above. To achieve this, I will break the tutorial into three parts: how to center a modal, make it responsive, and lastly, how to make the inner content scrollable.

Centering the Modal

There are numerous answers on Stack Overflow on how to center a div with an unknown height. It is so popular that CSS-Tricks has two different articles covering it and there is even an entire website dedicated to tackling vertical alignment. For this tutorial, I will cover my favorite method for modern browsers: moving the element 50% of the page down and then 50% of the div back.

<html>
  <head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css>
  </head>
  <body>
    <div class="modal">
      <div class="header">
        <h1>Header</h1>
      </div>
      <div class="content">
          Nullam quis risus eget urna mollis ornare vel eu leo.
      </div>
    </div>
  </body>
</html>
.modal {
  position:fixed;
  top:50%;
  left:50%;
  transform: translate(-50%,-50%);
  box-sizing:border-box;
  width:600px;
}

Making your Modal Responsive

There are two different ways you could make your modal responsive. The most obvious way is to write a bunch of media queries like a chump. I instead suggest using max-width and max-height. When combined with calc() this allows you to have a consistent padding between the modal and the edge of the screen on smaller devices. For example, if you wanted to have a 40px padding between the edge of the screen and your modal, you would use the following block of code.

.modal {
  max-width:calc(100% - 80px);
  max-height:calc(100% - 80px);
}

Notice that I subtracted 80px from the full width. This was done to take into account both sides of the modal.

Fixing the Header and Making the Content Scrollable

At this point the header is already fixed. The problem is that the content leaks out of the bottom of the modal if it gets too small. This problem is surprisingly difficult to solve. You could apply overflow:auto to the entire modal, but then the header would no longer be fixed. The alternative is to just apply overflow:auto to the inner content, but it is not immediately clear how to set the max-height of the content to enable scrolling.

You might think that we could simply set the max-height of the content as a percentage of the modal. This does not work, however, because the height of the modal is not explicitly defined (because we are using max-height there too).

The trick is to give up on setting the height based on the height of the modal and to instead use vh to calculate the height based on the viewport height. CSS-Tricks has a great article on viewport units if you want to learn more about them. So assuming again that you have a 40px padding around your modal and that you have a header with a height of 45px, the CSS for your modal content should look like this.

.modal .content {
  overflow:auto;
  max-height: calc(100vh - 125px);
}

This takes into account both the padding and height of the header (40px + 40px + 45px = 125px). What you probably noticed is that this means that the header must remain at a fixed height (no wrapping text). This is unfortunate, but I have yet to find a work around. Below you’ll find the final product.

See the Pen Super Modal by Matthew Bolanos (@matthewbolanos) on CodePen.