모달

모달 (Modal)

모달 구조는 아주 간단합니다:

  • modal: 메인 컨테이너
  • modal-background: 모달을 닫는 클릭 대상으로 사용할 수 있는 투명 오버레이
  • modal-content: 최대 너비 640px로 가로·세로 가운데 정렬된 컨테이너. 어떤 콘텐츠든 넣을 수 있어요
  • modal-close: 오른쪽 위 모서리에 있는 간단한 X(닫기) 버튼

출처: 문서

본문

<div class="modal">
  <div class="modal-background"></div>
  <div class="modal-content">
    <!-- Any other Bulma elements you want -->
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>

모달을 활성화하려면 .modal 컨테이너에 is-active 수식어만 추가하면 됩니다. 또한 포함하는 요소(보통 html)에 is-clipped 수식어를 추가하면 스크롤 넘침을 막을 수 있어요.

Bulma에는 JavaScript가 포함되어 있지 않지만, 이 문서에서는 자유롭게 사용할 수 있는 JS 구현 예시를 제공합니다.

이미지 모달 (Image modal)

모달은 무엇이든 담을 수 있기 때문에, 예를 들어 아주 쉽게 이미지 갤러리를 만들 수도 있어요:

<div class="modal">
  <div class="modal-background"></div>
  <div class="modal-content">
    <p class="image is-4by3">
      <img src="https://bulma.io/assets/images/placeholders/1280x960.png" alt="">
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>

모달 카드 (Modal card)

머리(head)·본문(body)·꼬리(foot)가 있는 더 고전적인 모달을 원한다면 modal-card를 사용하세요:

<div class="modal">
  <div class="modal-background"></div>
  <div class="modal-card">
    <header class="modal-card-head">
      <p class="modal-card-title">Modal title</p>
      <button class="delete" aria-label="close"></button>
    </header>
    <section class="modal-card-body">
      <!-- Content ... -->
    </section>
    <footer class="modal-card-foot">
      <div class="buttons">
        <button class="button is-success">Save changes</button>
        <button class="button">Cancel</button>
      </div>
    </footer>
  </div>
</div>

JavaScript 구현 예시

Bulma 패키지에는 JavaScript가 포함되어 있지 않습니다. 아래는 vanilla JavaScript로 커스텀 요소에 click 핸들러를 설정하는 구현 예시입니다. 이 구현은 3부분으로 나뉘어요:

  1. 모달용 HTML 추가 (기본적으로 숨겨져 있음)
  2. 모달을 트리거할 버튼용 HTML 추가 (버튼 스타일은 자유)
  3. 트리거에 click 이벤트를 추가해 모달을 여는 JS 코드
<!-- 1. 페이지 끝, </body> 태그 앞에 추가 -->
<div id="modal-js-example" class="modal">
  <div class="modal-background"></div>
  <div class="modal-content">
    <div class="box">
      <p>Modal JS example</p>
      <!-- Your content -->
    </div>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>

id 속성의 값은 반드시 고유해야 합니다.

<!-- 2. 페이지 어딘가에 트리거 버튼 추가 -->
<button class="js-modal-trigger" data-target="modal-js-example">
  Open JS example modal
</button>

js-modal-trigger CSS 클래스와 적절한 data-target 값만 있다면 스타일은 자유롭게 지정할 수 있어요. 예를 들어 Bulma의 button is-primary 클래스를 추가할 수도 있습니다.

// 3. script 요소(또는 별도 .js 파일)에 JS 코드 추가
document.addEventListener('DOMContentLoaded', () => {
  // Functions to open and close a modal
  function openModal($el) {
    $el.classList.add('is-active');
  }

  function closeModal($el) {
    $el.classList.remove('is-active');
  }

  function closeAllModals() {
    (document.querySelectorAll('.modal') || []).forEach(($modal) => {
      closeModal($modal);
    });
  }

  // Add a click event on buttons to open a specific modal
  (document.querySelectorAll('.js-modal-trigger') || []).forEach(($trigger) => {
    const modal = $trigger.dataset.target;
    const $target = document.getElementById(modal);

    $trigger.addEventListener('click', () => {
      openModal($target);
    });
  });

  // Add a click event on various child elements to close the parent modal
  (document.querySelectorAll('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => {
    const $target = $close.closest('.modal');

    $close.addEventListener('click', () => {
      closeModal($target);
    });
  });

  // Add a keyboard event to close all modals
  document.addEventListener('keydown', (event) => {
    if(event.key === "Escape") {
      closeAllModals();
    }
  });
});
~~~

`modal` 요소의 `is-active` 수식어 클래스만 토글할 수 있다면 구현 방식은 자유롭게 선택하면 됩니다.

### Sass 및 CSS 변수 (Sass and CSS variables)

| Sass 변수 | CSS 변수 | 값 |
|---|---|---|
| `$modal-z` | `var(--bulma-modal-z)` | `40` |
| `$modal-background-background-color` | `var(--bulma-modal-background-background-color)` | `hsla(var(--bulma-scheme-h), var(--bulma-scheme-s), var(--bulma-scheme-invert-l), 0.86)` |
| `$modal-content-width` | `var(--bulma-modal-content-width)` | `40rem` |
| `$modal-content-margin-mobile` | `var(--bulma-modal-content-margin-mobile)` | `1.25rem` |
| `$modal-content-spacing-mobile` | `var(--bulma-modal-content-spacing-mobile)` | `10rem` |
| `$modal-content-spacing-tablet` | `var(--bulma-modal-content-spacing-tablet)` | `2.5rem` |
| `$modal-close-dimensions` | `var(--bulma-modal-close-dimensions)` | `2.5rem` |
| `$modal-close-right` | `var(--bulma-modal-close-right)` | `1.25rem` |
| `$modal-close-top` | `var(--bulma-modal-close-top)` | `1.25rem` |
| `$modal-card-spacing` | `var(--bulma-modal-card-spacing)` | `2.5rem` |
| `$modal-card-head-background-color` | `var(--bulma-modal-card-head-background-color)` | `var(--bulma-scheme-main)` |
| `$modal-card-head-padding` | `var(--bulma-modal-card-head-padding)` | `2rem` |
| `$modal-card-head-radius` | `var(--bulma-modal-card-head-radius)` | `var(--bulma-radius-large)` |
| `$modal-card-title-color` | `var(--bulma-modal-card-title-color)` | `var(--bulma-text-strong)` |
| `$modal-card-title-line-height` | `var(--bulma-modal-card-title-line-height)` | `1` |
| `$modal-card-title-size` | `var(--bulma-modal-card-title-size)` | `var(--bulma-size-4)` |
| `$modal-card-foot-background-color` | `var(--bulma-modal-card-foot-background-color)` | `var(--bulma-scheme-main-bis)` |
| `$modal-card-foot-radius` | `var(--bulma-modal-card-foot-radius)` | `var(--bulma-radius-large)` |
| `$modal-card-body-background-color` | `var(--bulma-modal-card-body-background-color)` | `var(--bulma-scheme-main)` |
| `$modal-card-body-padding` | `var(--bulma-modal-card-body-padding)` | `2rem` |

## 더 알아보기 (Learn more)

- [Dropdown 컴포넌트](https://bulma.io/documentation/components/dropdown/)
- [Card 컴포넌트](https://bulma.io/documentation/components/card/)