Search code examples
csstwitter-bootstrapbootstrap-4bootstrap-modal

How to vertically align Bootstrap v4 modal dialogs


Vertically center modal dialogues in Bootstrap 4.

Note: The requirements below have been added to make it clear I am looking for a proper way to vertically center a Bootstrap modal, covering all possible cases, on all possible devices, in all browsers. In my case, I wanted it for a large SPA reusing the same modal throughout the app so I needed it to work in each case.

It should:

  • keep modal contents accessible, on all devices, even when taller than device height
  • work on any device+browser combination with a market share larger than 1%
  • not use display:table-cell or similar hacks (any layout-ing technique not meant or designed for layout-ing)
  • close on click or tap anywhere outside of .modal-content (including above and below).
  • limit usage of jQuery/JavaScript as much as possible
  • (optional) work on default Bootstrap examples without need of markup modifications

Solution

  • Update, as of Beta 3, [docs]:

    Add .modal-dialog-centered to .modal-dialog to vertically center the modal.


    Original answer:

    SCSS:

    .modal-dialog {
      min-height: calc(100vh - 60px);
      display: flex;
      flex-direction: column;
      justify-content: center;
      overflow: auto;
      @media(max-width: 768px) {
        min-height: calc(100vh - 20px);
      }
    }
    

    or unprefixed CSS:

    .modal-dialog {
        min-height: calc(100vh - 60px);
        display: flex;
        flex-direction: column;
        justify-content: center;
        overflow: auto;
    }
    @media(max-width: 768px) {
      .modal-dialog {
        min-height: calc(100vh - 20px);
      }
    }
    

    Note 1: Please note fully prefixed CSS gradually becomes obsolete as browser support for certain properties changes. The right way of getting the updated prefixed CSS is to:

    • copy/paste the unprefixed CSS into Autoprefixer.
    • set the filter in the bottom box to the desired setting (for max. browser support use > 0%).
    • get the latest code from the box on the right.

    Note 2: This answer was added in early stages of v4 (alpha 3 or 4), which is now currently in beta. You can safely replace the CSS part of this answer by adding the following classes to .modal-dialog:

    h-100 d-flex flex-column justify-content-center my-0
    

    ..., as pointed out by @Androbaut in the comment below. You will still need the JavaScript (see below) to close the modal window on click tap below/above the modal window.


    jQuery (needed to close modal on click/tap above/below):

    $('.modal-dialog').on('click tap', function(e){
      if ($(e.target).hasClass('modal-dialog')) {
        $('.modal').modal('hide');
      }
    })
    

    That's it.


    Working snippet, fully-prefixed CSS and markup using different modal sizes:

    $('.modal-dialog').on('click tap', function(e){
      if ($(e.target).hasClass('modal-dialog')) {
      	$('.modal').modal('hide');
      }
    })
    .modal-dialog {
      min-height: -webkit-calc(100vh - 60px);
      min-height: -moz-calc(100vh - 60px);
      min-height: calc(100vh - 60px);
      display: -webkit-box;
      display: -webkit-flex;
      display: -moz-box;
      display: -ms-flexbox;
      display: flex;
      -webkit-box-orient: vertical;
      -webkit-box-direction: normal;
      -webkit-flex-direction: column;
         -moz-box-orient: vertical;
         -moz-box-direction: normal;
          -ms-flex-direction: column;
              flex-direction: column;
      -webkit-box-pack: center;
      -webkit-justify-content: center;
         -moz-box-pack: center;
          -ms-flex-pack: center;
              justify-content: center;
      overflow: auto; 
    }
    @media (max-width: 768px) {
      .modal-dialog {
        min-height: -webkit-calc(100vh - 20px);
        min-height: -moz-calc(100vh - 20px);
        min-height: calc(100vh - 20px);   
      }
    }
    
    /* you don't need the CSS below this line. It's mainly cosmetic and for aligning the modal launch buttons */
    
    .modal-content {
      display: -webkit-box;
      display: -webkit-flex;
      display: -moz-box;
      display: -ms-flexbox;
      display: flex;
      -webkit-box-orient: vertical;
      -webkit-box-direction: normal;
      -webkit-flex-direction: column;
         -moz-box-orient: vertical;
         -moz-box-direction: normal;
          -ms-flex-direction: column;
              flex-direction: column; }
    .modal-content > * {
      -webkit-box-flex: 0;
      -webkit-flex: 0 0 auto;
         -moz-box-flex: 0;
          -ms-flex: 0 0 auto;
              flex: 0 0 auto; 
    }
    .modal-content > *.modal-body {
      -webkit-box-flex: 1;
      -webkit-flex-grow: 1;
         -moz-box-flex: 1;
          -ms-flex-positive: 1;
              flex-grow: 1; 
    }
    
    #Modal_2 .modal-content {
      min-height: 50vh; 
    }
    
    #Modal_3 .modal-content {
      min-height: 85vh; 
    }
    
    #Modal_4 .modal-content {
      min-height: 200vh; 
    }
    
    .full-page-center {
      display: -webkit-box;
      display: -webkit-flex;
      display: -moz-box;
      display: -ms-flexbox;
      display: flex;
      -webkit-box-pack: center;
      -webkit-justify-content: center;
         -moz-box-pack: center;
          -ms-flex-pack: center;
              justify-content: center;
      -webkit-box-align: center;
      -webkit-align-items: center;
         -moz-box-align: center;
          -ms-flex-align: center;
              align-items: center;
      min-height: 100vh; 
    }
    .full-page-center button {
      margin: 15px; 
    }
    @media (max-width: 768px) {
      .full-page-center {
        -webkit-flex-wrap: wrap;
            -ms-flex-wrap: wrap;
                flex-wrap: wrap;   
      }
      .full-page-center button {
        display: block;
        min-width: 100%;
        margin: 10px 15px;
      }
      .full-page-center::after {
        display: none;
        -webkit-box-flex: 0;
        -webkit-flex-grow: 0;
           -moz-box-flex: 0;
            -ms-flex-positive: 0;
                flex-grow: 0;
      }
    }
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css" rel="stylesheet"/>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://npmcdn.com/[email protected]/dist/js/tether.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js"></script>
    
    
    <div class="container full-page-center">
      <button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#Modal_1">
        Tiny modal
      </button>
      <button type="button" class="btn btn-default btn-lg" data-toggle="modal" data-target="#Modal_2">
        Normal modal
      </button>
      <button type="button" class="btn btn-success btn-lg" data-toggle="modal" data-target="#Modal_3">
        Large modal
      </button>
      <button type="button" class="btn btn-warning btn-lg" data-toggle="modal" data-target="#Modal_4">
        Very large modal
      </button>
    </div>
    <div class="modal fade" id="Modal_1" tabindex="-1" role="dialog" aria-labelledby="modalLabel_1" aria-hidden="true">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
            <h4 class="modal-title" id="modalLabel_1">Tiny modal</h4>
          </div>
          <div class="modal-body">
            I am cute...
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary">Save changes</button>
          </div>
        </div>
      </div>
    </div>
    <div class="modal fade" id="Modal_2" tabindex="-1" role="dialog" aria-labelledby="modalLabel_2" aria-hidden="true">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
            <h4 class="modal-title" id="modalLabel_2">Dull modal</h4>
          </div>
          <div class="modal-body">
            I am normal...
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary">Some action</button>
          </div>
        </div>
      </div>
    </div>
    <div class="modal fade" id="Modal_3" tabindex="-1" role="dialog" aria-labelledby="modalLabel_3" aria-hidden="true">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
            <h4 class="modal-title" id="modalLabel_3">Don't call me fat</h4>
          </div>
          <div class="modal-body">
            Call me "oversized".
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
            <button type="button" class="btn btn-success">Some action</button>
          </div>
        </div>
      </div>
    </div>
    <div class="modal fade" id="Modal_4" tabindex="-1" role="dialog" aria-labelledby="modalLabel_4" aria-hidden="true">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
            <h4 class="modal-title" id="modalLabel_4">Huge modal</h4>
          </div>
          <div class="modal-body">
            Comments, anyone?
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
            <button type="button" class="btn btn-warning">Some action</button>
          </div>
        </div>
      </div>
    </div>

    If you find any bugs or shortcomings please let me know. I will take the time to improve the answer and keep it useful. Help with this task is welcome.