Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Modal and dropdown

A modal (modal) appears over the page and dims everything else: confirm a deletion, fill in a short form. A dropdown (dropdown) opens a list of actions under a button. Bulma draws both, but your JavaScript has to open and close them: the element is visible only while it has the is-active class.

A modal has three parts: the modal-background overlay, the modal-content and the modal-close cross button. The markup goes at the end of <body>:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
 
<div style="min-height: 260px">
  <button class="button is-danger js-open" data-target="cancel-modal">Cancel order</button>
</div>
 
<div class="modal" id="cancel-modal">
  <div class="modal-background"></div>
  <div class="modal-content">
    <div class="box">
      <p class="title is-5">Cancel order #1042?</p>
      <p class="mb-4">The guest will be notified about the cancellation.</p>
      <div class="buttons">
        <button class="button is-danger">Yes, cancel</button>
        <button class="button js-close">No</button>
      </div>
    </div>
  </div>
  <button class="modal-close is-large" aria-label="Close"></button>
</div>
 
<script>
  const openModal = (modal) => modal.classList.add('is-active');
  const closeModal = (modal) => modal.classList.remove('is-active');
 
  document.querySelectorAll('.js-open').forEach((button) => {
    const modal = document.getElementById(button.dataset.target);
    button.addEventListener('click', () => openModal(modal));
  });
 
  document.querySelectorAll('.modal-background, .modal-close, .js-close').forEach((el) => {
    el.addEventListener('click', () => closeModal(el.closest('.modal')));
  });
 
  document.addEventListener('keydown', (event) => {
    if (event.key === 'Escape') {
      document.querySelectorAll('.modal.is-active').forEach(closeModal);
    }
  });
</script>

Click the red button: the background dims and a white window with the question appears in the centre. The cross, the "No" button, a click on the overlay and the Escape key close it — the script handles each of these separately. The min-height block is only there to give the modal enough height in the preview.

A modal card

For a modal with a header and buttons at the bottom, use modal-card instead of modal-content:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
 
<div style="min-height: 300px"></div>
 
<div class="modal is-active">
  <div class="modal-background"></div>
  <div class="modal-card">
    <header class="modal-card-head">
      <p class="modal-card-title">New dish</p>
      <button class="delete js-close" aria-label="Close"></button>
    </header>
    <section class="modal-card-body">A form with the name and price goes here.</section>
    <footer class="modal-card-foot">
      <div class="buttons">
        <button class="button is-success">Save</button>
        <button class="button js-close">Cancel</button>
      </div>
    </footer>
  </div>
</div>

A modal with a grey header, a white body and buttons at the bottom. To make it visible in the preview straight away, the modal already has is-active; in a project the script from the example above sets that class. The modal-close button is not used here: the delete cross in the header and "Cancel" close the modal, and their js-close class is already in that script's selector list.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
 
<div style="min-height: 180px">
  <div class="dropdown" id="order-actions">
    <div class="dropdown-trigger">
      <button class="button" aria-haspopup="true" aria-controls="order-menu">Actions ▾</button>
    </div>
    <div class="dropdown-menu" id="order-menu" role="menu">
      <div class="dropdown-content">
        <a href="#" class="dropdown-item">Print receipt</a>
        <a href="#" class="dropdown-item">Edit</a>
        <hr class="dropdown-divider">
        <a href="#" class="dropdown-item has-text-danger">Delete</a>
      </div>
    </div>
  </div>
</div>
 
<script>
  const dropdown = document.getElementById('order-actions');
  dropdown.querySelector('.dropdown-trigger button').addEventListener('click', (event) => {
    event.stopPropagation();
    dropdown.classList.toggle('is-active');
  });
  document.addEventListener('click', () => dropdown.classList.remove('is-active'));
</script>

Click "Actions" and a white list of three items opens under the button. A click anywhere else on the page closes it: stopPropagation stops the click on the button from reaching document straight away.

Without JavaScript

If opening the menu on mouse hover is enough, no script is needed: add the is-hoverable modifier to the dropdown. Phones have no hover, though, so for mobile sites the click version is more reliable. The is-right and is-up modifiers align the menu to the right edge or open it upwards.

Common mistake

Placing a modal inside a card or a column. If a parent has transform or overflow: hidden, the modal may be clipped or not centred on the screen. Keep the modal markup directly in <body>, at the end of the page.

Thanks for reading!

Donate to the site — we work hard for you.