City of Ghent Style Guide

Modal

When to use this component

Use the modal component to show longer content or more complex functionality that would otherwise be included on a web page but:

  • Needs more screen space to be readable and/or usable
  • Is typically advanced or only necessary for a minority of users (progressive disclosure principle)

The modal component can also be used instead of a link that opens in a new window to show content or functionality that is necessary to users but cannot interrupt them in an ongoing process:

  • The user is filling out a form or a checkout process and needs to review, say, terms of service
  • The user is watching video or listening to audio

When not to use this component

Do not use an modal component to hide a simple piece or section of content or a simple functionality that is essential to all users.

How it works

The modal component shows a modal box that is over the web page content with a color overlay in between the modal box and the web page content. When opened, the focus is moved to the modal box and the components on the web page behind the color overlay are not accessible anymore. Clicking anywhere on the color overlay outside the modal box or the close button of the modal box closes the modal box.

The modal box consists of three parts:

  • Modal header with close-button at the right (required)
  • Modal content (required)
  • Modal actions panel (optional, typically used for “Confirm” and/or “Cancel” buttons)

There are two types of modals:

  • Default modal
  • Fixed-height modal

Default modal

The default modal has a modal box whose height automatically adapts to the length of the content or functionality that is shown inside the modal box. When the modal box becomes too high to be shown in the viewport, scrolling is enabled. In this case, the full modal box including the header, content and actions panel (if enabled) moves.

Fixed-height modal

The fixed-height modal has a modal box whose height is fixed to a height relative to the height of the viewport. The position of the modal box is fixed. The modal box is always centered in the viewport.

When the content or functionality inside the modal box is too long to be shown all in the available height, scrolling of the content or functionality inside the modal box is enabled. In this case, only the modal content scrolls, the modal header and the model actions panel stay are pinned to the modal box and stay in place.

Make sure to add tabindex=0 to the .modal--fixed-height element to enable the scrollable region with keyboard access.

Usage within the style guide

The modal component is used in the checkboxes dynamic component.

<div
    id="{{ id }}"
    class="modal{{ modifier ? ' modal--' ~ modifier }}{{ classes ? ' ' ~ classes }}"
    role="dialog"
    aria-modal="true"
  {% if title %}
    aria-labelledby="{{ id }}-title"
  {% endif %}
    tabindex="-1">

  <{{ modal_inner_tag and modal_inner_tag != '' ? modal_inner_tag : 'div'}} class="modal-inner" {{ modal_inner_action ? 'action="'~ modal_inner_action ~'"' : '' }}>
    <div class="modal-header">
      <button class="button button-secondary close icon-cross modal-close{{ cancelClasses ? ' ' ~ cancelClasses }}" data-target="{{ id }}">
        <span>Close</span>
      </button>
    </div>
    <div class="modal-content" {{ modifier == "fixed-height" and hasFocusables != true ? 'tabindex="0"' : '' }}>
      {% if title %}
      <{{ title_heading_level|default('h2') }} id="{{ id }}-title">{{ title }}</{{ title_heading_level|default('h2') }}>
      {% endif %}
      {{ content }}
    </div>
    {% if actions %}
      <div class="modal-actions">
        {{ actions }}
      </div>
    {% endif %}
  </{{ modal_inner_tag and modal_inner_tag != '' ? modal_inner_tag : 'div'}}>

  <div class="modal-overlay modal-close{{ cancelClasses ? ' ' ~ cancelClasses }}" data-target="{{ id }}" tabindex="-1"></div>
</div>
<div id="modal-actions-fixed-height" class="modal modal--fixed-height" role="dialog" aria-modal="true" aria-labelledby="modal-actions-fixed-height-title" tabindex="-1">

    <div class="modal-inner">
        <div class="modal-header">
            <button class="button button-secondary close icon-cross modal-close" data-target="modal-actions-fixed-height">
                <span>Close</span>
            </button>
        </div>
        <div class="modal-content" tabindex="0">
            <h2 id="modal-actions-fixed-height-title">An example modal</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum imperdiet vestibulum ex, id tincidunt nulla porttitor nec. Cras aliquam interdum felis, nec efficitur quam varius sit amet. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut nec gravida tellus, quis pulvinar enim. Proin ut lectus dui. Pellentesque maximus orci quis aliquet bibendum. Fusce vestibulum velit a tellus fermentum, in laoreet est pharetra.</p>
            <p>Fusce sed lacus turpis. Praesent ultrices viverra neque vel fermentum. Donec pulvinar ligula et iaculis euismod. Donec rhoncus cursus lacus, et pharetra ligula tristique non. Morbi nec ligula ornare, semper orci sit amet, varius massa. Fusce magna quam, condimentum et rhoncus et, porttitor at turpis. Donec id nunc dapibus, consequat massa a, cursus odio. Aliquam in dignissim sem. Suspendisse potenti. Etiam at metus vitae urna viverra luctus. Donec convallis interdum convallis. Donec volutpat eget velit in varius. Nam porta varius urna at eleifend. In elit ex, vestibulum non ex a, suscipit semper dui. Integer dictum lacus augue, vitae rhoncus lorem tempor eget.</p>
            <p>Integer nec suscipit dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur non finibus felis. Nunc faucibus sapien at fermentum semper. Donec congue egestas felis, eget ultrices augue commodo in. Aliquam vitae elit diam. Nulla auctor velit sed nisl lacinia, sit amet tincidunt lectus consectetur. Aliquam sit amet hendrerit lacus. Curabitur lacus enim, tempus eget massa id, varius elementum quam. Praesent pulvinar facilisis lacinia. Quisque congue imperdiet nunc vitae dictum. Nulla a nisi neque. Mauris sed mollis orci. Pellentesque sagittis tempus diam. Phasellus malesuada diam sed lectus fringilla pellentesque. Nullam sit amet bibendum magna, et hendrerit tellus.</p>
            <p>In ultrices, libero in pretium elementum, arcu est eleifend leo, et luctus ante orci nec mi. Sed ac facilisis sapien, sit amet dignissim erat. Nulla malesuada quam vitae leo iaculis fermentum. Aliquam condimentum odio at metus malesuada, et gravida nisi ornare. Sed lobortis eros eu dolor pulvinar porta. Sed luctus auctor pulvinar. Etiam laoreet interdum rhoncus. Aliquam in quam posuere ligula faucibus auctor.</p>
            <p>Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer condimentum mauris non urna scelerisque bibendum. Nulla facilisi. Curabitur sed mi fermentum, maximus lorem eu, laoreet purus. Vivamus ultricies consequat odio, a tristique erat varius ac. Morbi feugiat, nisl eu vehicula elementum, ipsum orci laoreet eros, id hendrerit enim sem in dolor. Maecenas augue ligula, vehicula a risus blandit, volutpat euismod felis. Aliquam maximus, tortor sed tincidunt volutpat, lorem ex dictum sem, eu scelerisque augue mi vitae orci. Nam imperdiet magna porttitor nunc finibus accumsan. Nullam maximus, nulla sit amet lobortis ullamcorper, erat nulla ornare lacus, non ullamcorper felis mi a nibh. Integer eget pulvinar quam.</p>
            <p>Fusce sed lacus turpis. Praesent ultrices viverra neque vel fermentum. Donec pulvinar ligula et iaculis euismod. Donec rhoncus cursus lacus, et pharetra ligula tristique non. Morbi nec ligula ornare, semper orci sit amet, varius massa. Fusce magna quam, condimentum et rhoncus et, porttitor at turpis. Donec id nunc dapibus, consequat massa a, cursus odio. Aliquam in dignissim sem. Suspendisse potenti. Etiam at metus vitae urna viverra luctus. Donec convallis interdum convallis. Donec volutpat eget velit in varius. Nam porta varius urna at eleifend. In elit ex, vestibulum non ex a, suscipit semper dui. Integer dictum lacus augue, vitae rhoncus lorem tempor eget.</p>
            <p>Integer nec suscipit dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur non finibus felis. Nunc faucibus sapien at fermentum semper. Donec congue egestas felis, eget ultrices augue commodo in. Aliquam vitae elit diam. Nulla auctor velit sed nisl lacinia, sit amet tincidunt lectus consectetur. Aliquam sit amet hendrerit lacus. Curabitur lacus enim, tempus eget massa id, varius elementum quam. Praesent pulvinar facilisis lacinia. Quisque congue imperdiet nunc vitae dictum. Nulla a nisi neque. Mauris sed mollis orci. Pellentesque sagittis tempus diam. Phasellus malesuada diam sed lectus fringilla pellentesque. Nullam sit amet bibendum magna, et hendrerit tellus.</p>
            <p>In ultrices, libero in pretium elementum, arcu est eleifend leo, et luctus ante orci nec mi. Sed ac facilisis sapien, sit amet dignissim erat. Nulla malesuada quam vitae leo iaculis fermentum. Aliquam condimentum odio at metus malesuada, et gravida nisi ornare. Sed lobortis eros eu dolor pulvinar porta. Sed luctus auctor pulvinar. Etiam laoreet interdum rhoncus. Aliquam in quam posuere ligula faucibus auctor.</p>
            <p>Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer condimentum mauris non urna scelerisque bibendum. Nulla facilisi. Curabitur sed mi fermentum, maximus lorem eu, laoreet purus. Vivamus ultricies consequat odio, a tristique erat varius ac. Morbi feugiat, nisl eu vehicula elementum, ipsum orci laoreet eros, id hendrerit enim sem in dolor. Maecenas augue ligula, vehicula a risus blandit, volutpat euismod felis. Aliquam maximus, tortor sed tincidunt volutpat, lorem ex dictum sem, eu scelerisque augue mi vitae orci. Nam imperdiet magna porttitor nunc finibus accumsan. Nullam maximus, nulla sit amet lobortis ullamcorper, erat nulla ornare lacus, non ullamcorper felis mi a nibh. Integer eget pulvinar quam.</p>
        </div>
        <div class="modal-actions">
            <button type="button" class="button button-primary modal-close" data-target="modal-actions">Understood!</button>
        </div>
    </div>

    <div class="modal-overlay modal-close" data-target="modal-actions-fixed-height" tabindex="-1"></div>
</div>
{
  "id": "modal-actions-fixed-height",
  "title": "An example modal",
  "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum imperdiet vestibulum ex, id tincidunt nulla porttitor nec. Cras aliquam interdum felis, nec efficitur quam varius sit amet. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut nec gravida tellus, quis pulvinar enim. Proin ut lectus dui. Pellentesque maximus orci quis aliquet bibendum. Fusce vestibulum velit a tellus fermentum, in laoreet est pharetra.</p><p>Fusce sed lacus turpis. Praesent ultrices viverra neque vel fermentum. Donec pulvinar ligula et iaculis euismod. Donec rhoncus cursus lacus, et pharetra ligula tristique non. Morbi nec ligula ornare, semper orci sit amet, varius massa. Fusce magna quam, condimentum et rhoncus et, porttitor at turpis. Donec id nunc dapibus, consequat massa a, cursus odio. Aliquam in dignissim sem. Suspendisse potenti. Etiam at metus vitae urna viverra luctus. Donec convallis interdum convallis. Donec volutpat eget velit in varius. Nam porta varius urna at eleifend. In elit ex, vestibulum non ex a, suscipit semper dui. Integer dictum lacus augue, vitae rhoncus lorem tempor eget.</p><p>Integer nec suscipit dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur non finibus felis. Nunc faucibus sapien at fermentum semper. Donec congue egestas felis, eget ultrices augue commodo in. Aliquam vitae elit diam. Nulla auctor velit sed nisl lacinia, sit amet tincidunt lectus consectetur. Aliquam sit amet hendrerit lacus. Curabitur lacus enim, tempus eget massa id, varius elementum quam. Praesent pulvinar facilisis lacinia. Quisque congue imperdiet nunc vitae dictum. Nulla a nisi neque. Mauris sed mollis orci. Pellentesque sagittis tempus diam. Phasellus malesuada diam sed lectus fringilla pellentesque. Nullam sit amet bibendum magna, et hendrerit tellus.</p><p>In ultrices, libero in pretium elementum, arcu est eleifend leo, et luctus ante orci nec mi. Sed ac facilisis sapien, sit amet dignissim erat. Nulla malesuada quam vitae leo iaculis fermentum. Aliquam condimentum odio at metus malesuada, et gravida nisi ornare. Sed lobortis eros eu dolor pulvinar porta. Sed luctus auctor pulvinar. Etiam laoreet interdum rhoncus. Aliquam in quam posuere ligula faucibus auctor.</p><p>Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer condimentum mauris non urna scelerisque bibendum. Nulla facilisi. Curabitur sed mi fermentum, maximus lorem eu, laoreet purus. Vivamus ultricies consequat odio, a tristique erat varius ac. Morbi feugiat, nisl eu vehicula elementum, ipsum orci laoreet eros, id hendrerit enim sem in dolor. Maecenas augue ligula, vehicula a risus blandit, volutpat euismod felis. Aliquam maximus, tortor sed tincidunt volutpat, lorem ex dictum sem, eu scelerisque augue mi vitae orci. Nam imperdiet magna porttitor nunc finibus accumsan. Nullam maximus, nulla sit amet lobortis ullamcorper, erat nulla ornare lacus, non ullamcorper felis mi a nibh. Integer eget pulvinar quam.</p><p>Fusce sed lacus turpis. Praesent ultrices viverra neque vel fermentum. Donec pulvinar ligula et iaculis euismod. Donec rhoncus cursus lacus, et pharetra ligula tristique non. Morbi nec ligula ornare, semper orci sit amet, varius massa. Fusce magna quam, condimentum et rhoncus et, porttitor at turpis. Donec id nunc dapibus, consequat massa a, cursus odio. Aliquam in dignissim sem. Suspendisse potenti. Etiam at metus vitae urna viverra luctus. Donec convallis interdum convallis. Donec volutpat eget velit in varius. Nam porta varius urna at eleifend. In elit ex, vestibulum non ex a, suscipit semper dui. Integer dictum lacus augue, vitae rhoncus lorem tempor eget.</p><p>Integer nec suscipit dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur non finibus felis. Nunc faucibus sapien at fermentum semper. Donec congue egestas felis, eget ultrices augue commodo in. Aliquam vitae elit diam. Nulla auctor velit sed nisl lacinia, sit amet tincidunt lectus consectetur. Aliquam sit amet hendrerit lacus. Curabitur lacus enim, tempus eget massa id, varius elementum quam. Praesent pulvinar facilisis lacinia. Quisque congue imperdiet nunc vitae dictum. Nulla a nisi neque. Mauris sed mollis orci. Pellentesque sagittis tempus diam. Phasellus malesuada diam sed lectus fringilla pellentesque. Nullam sit amet bibendum magna, et hendrerit tellus.</p><p>In ultrices, libero in pretium elementum, arcu est eleifend leo, et luctus ante orci nec mi. Sed ac facilisis sapien, sit amet dignissim erat. Nulla malesuada quam vitae leo iaculis fermentum. Aliquam condimentum odio at metus malesuada, et gravida nisi ornare. Sed lobortis eros eu dolor pulvinar porta. Sed luctus auctor pulvinar. Etiam laoreet interdum rhoncus. Aliquam in quam posuere ligula faucibus auctor.</p><p>Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer condimentum mauris non urna scelerisque bibendum. Nulla facilisi. Curabitur sed mi fermentum, maximus lorem eu, laoreet purus. Vivamus ultricies consequat odio, a tristique erat varius ac. Morbi feugiat, nisl eu vehicula elementum, ipsum orci laoreet eros, id hendrerit enim sem in dolor. Maecenas augue ligula, vehicula a risus blandit, volutpat euismod felis. Aliquam maximus, tortor sed tincidunt volutpat, lorem ex dictum sem, eu scelerisque augue mi vitae orci. Nam imperdiet magna porttitor nunc finibus accumsan. Nullam maximus, nulla sit amet lobortis ullamcorper, erat nulla ornare lacus, non ullamcorper felis mi a nibh. Integer eget pulvinar quam.</p>",
  "actions": "<button type=\"button\" class=\"button button-primary modal-close\" data-target=\"modal-actions\">Understood!</button>",
  "modifier": "fixed-height"
}
  • Content:
    // Needed for smooths scrolling on iphone / ipad inside modals.
    // Placing it on .modal is not sufficient for safari.
    * {
      -webkit-overflow-scrolling: touch; // sass-lint:disable-line no-vendor-prefixes
    }
    
    .modal {
      @include tablet-and-below {
        @include theme('background-color', 'color-none', 'modal-background');
      }
    
      @include tablet {
        animation: fade .2s ease-in-out;
      }
    
      display: flex;
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      max-height: 100vh;
      visibility: hidden;
      z-index: z('modal', 'base');
      overflow-y: auto;
    
      // states
      &.visible {
        visibility: visible;
    
        @include tablet {
          .modal-overlay {
            display: block;
          }
        }
      }
    
      // modifiers
      &.modal--fixed-height {
        &.visible > .modal-inner {
          max-height: 80vh;
    
          @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { // sass-lint:disable-line no-vendor-prefixes
            height: 80vh; // IE fix
          }
        }
    
        > .modal-inner {
          > .modal-content {
            flex-grow: 1;
            z-index: z('modal', 'inner');
            overflow-y: auto;
          }
    
          > .modal-actions {
            padding-top: 30px;
          }
        }
      }
    
      // children
      &.visible .modal-inner {
        display: flex;
        position: absolute;
        top: 0;
        left: 0;
        flex-direction: column;
        width: 100%;
        min-height: 100%;
        margin: 0 auto;
        z-index: z('modal', 'content');
    
        @include tablet {
          right: 0;
          width: 970px;
          max-width: calc(100% - #{$gutter-width * 2});
          height: auto;
          min-height: 0;
          margin: 10vh auto 0;
          padding-bottom: $gutter-width;
          opacity: 0;
          animation: fade .2s ease-in-out forwards;
          animation-delay: .1s;
        }
    
        > * {
          @include theme('background-color', 'color-none', 'modal-inner-background');
    
          &:first-of-type {
            border-top-left-radius: border-radius('radius-2');
            border-top-right-radius: border-radius('radius-2');
          }
    
          &:last-of-type {
            border-bottom-left-radius: border-radius('radius-2');
            border-bottom-right-radius: border-radius('radius-2');
          }
        }
      }
    
      ///
      /// Header
      ///
      &-header {
        display: block;
        flex-shrink: 0;
        text-align: right;
      }
    
      ///
      /// Content
      ///
      &-content {
        padding: 16px 20px;
    
        @include tablet {
          padding: 16px 70px 60px;
        }
    
        figure {
          img {
            width: auto;
          }
        }
    
        p {
          &:last-child {
            margin-bottom: 0;
          }
        }
      }
    
      ///
      /// Actions
      ///
      &-actions {
        @include theme('border-color', 'color-primary--lighten-4', 'modal-action-border-bottom');
        @include theme('background-color', 'color-none', 'modal-action-background-color');
    
        display: flex;
        flex-shrink: 0;
        align-items: center;
        width: 100%;
        padding: 30px 20px;
        z-index: z('modal', 'actions');
    
        @include tablet {
          position: relative;
          margin-top: -15px;
          padding: 0 70px 30px;
        }
      }
    
      ///
      /// Overlay
      ///
      .modal-overlay {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        opacity: .9;
        z-index: z('modal', 'overlay');
    
        &,
        &:hover,
        &:focus {
          @include theme('background-color', 'color-tertiary-light', 'modal-overlay-background-color');
        }
      }
    }
    
    @keyframes fade {
      0% {
        opacity: 0;
      }
    
      100% {
        opacity: 1;
      }
    }
    
  • URL: /components/raw/modal/_modal.scss
  • Filesystem Path: components/31-molecules/modal/_modal.scss
  • Size: 3.4 KB
  • Content:
    /* global Modal */
    'use strict';
    
    (function () {
    
      if (!Modal) {
        return;
      }
    
      const selected = document.querySelectorAll('.modal:not(.has-custom-binding)');
      for (let i = selected.length; i--;) {
        new Modal(selected[i]);
      }
    
    })();
    
  • URL: /components/raw/modal/modal.bindings.js
  • Filesystem Path: components/31-molecules/modal/modal.bindings.js
  • Size: 243 Bytes