twitter-bootstrapbootstrap-modalbootstrap-5bootstrap5-modal

Bootstrap 5 modal with zoom effect and static backdrop


The default bootstrap 5 modal has a "slide" effect, and if it has a static backdrop then it shows a "bounce" effect when clicking on the static backdrop.

Official example:

<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" />
<script src="//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>

<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#staticBackdrop">Launch</button>

<!-- Modal -->
<div class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
  <div class="modal-dialog modal-sm">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        ...
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Understood</button>
      </div>
    </div>
  </div>
</div>

I want to use a "zoom" effect instead (the css is different, the markup is identical):

#staticBackdrop2.modal.fade      .modal-dialog { transform: scale(0.8) }
#staticBackdrop2.modal.fade.show .modal-dialog { transform: scale(1) }
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" />
<script src="//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>

<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#staticBackdrop2">Launch</button>

<!-- Modal -->
<div class="modal fade" id="staticBackdrop2" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
  <div class="modal-dialog modal-sm">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        ...
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Understood</button>
      </div>
    </div>
  </div>
</div>

In the second example, the modal is shown with a zoom effect, but the bounce effect is missing when clicking on the static backdrop.

How can I do both: use a zoom effect to show/hide the modal, but retain the bounce effect when clicking on the static backdrop? A simple css-only solution would be preferred.


Solution

  • Eliminate CSS Transform Conflicts

    The problem can be fixed by eliminating the conflict between the custom modal zoom classes and Bootstrap's modal bounce. Each use transform scale for animation. Fortunately there is a simple css only fix for this problem.

    We need to keep the Bootstrap fade class on the modal. Bootstrap uses this internally to determine if the modal is animated. We can then override the fade animation with zoom as in the original code. And the key to avoiding the conflict with bounce is to only apply the zoom when the modal is not shown. In other words, the zoom is applied only while the modal is opening or closing.

    The fix using :not()

    .modal.fade.zoom:not(.show) .modal-dialog {
        transform: scale(0.8);
    }
    

    And we can then apply the zoom effect to any modal:

    <div class="modal fade zoom" data-bs-backdrop="static">
       modal content
    </div>
    

    Snippet

    The snippet demonstrates the modal with and without zoom applied. With the modal displayed, click in the gray backdrop area to test the bounce effect.

    zoom.addEventListener("change", (e) => {
      staticBackdrop.classList.toggle("zoom");
    })
    .modal.fade.zoom:not(.show) .modal-dialog {
       transform: scale(0.8);
    }
    
    
    /* rules not part of solution */
    
    body {
     padding: 1rem;
    }
    
    img.logo {
      width: 100%;
      height: auto;
    }
        <!-- Button trigger modal -->
        <button
          type="button"
          class="btn btn-primary btn-sm"
          data-bs-toggle="modal"
          data-bs-target="#staticBackdrop"
        >
          Show Modal
        </button>
    
        <label style="margin-left: 2rem;">
          <input type="checkbox" id="zoom" checked> Apply Zoom Effect
        </label>
    
        <!-- Modal -->
        <div
          class="modal fade zoom"
          id="staticBackdrop"
          data-bs-backdrop="static"
          data-bs-keyboard="false"
          tabindex="-1"
          aria-labelledby="staticBackdropLabel"
          aria-hidden="true"
        >
          <div class="modal-dialog">
            <div class="modal-content">
              <div class="modal-header">
                <h1 class="modal-title fs-5" id="staticBackdropLabel">
                  Modal Demo
                </h1>
                <button
                  type="button"
                  class="btn-close"
                  data-bs-dismiss="modal"
                  aria-label="Close"
                ></button>
              </div>
              <div class="modal-body">
                <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Stack_Overflow_logo.svg/1200px-Stack_Overflow_logo.svg.png" class="logo">
              </div>
              <div class="modal-footer">
                <button
                  type="button"
                  class="btn btn-secondary"
                  data-bs-dismiss="modal"
                >
                  Close
                </button>
              </div>
            </div>
          </div>
        </div>
        
    
    <!-- Bootstrap 5 -->
    
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"/>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>