City of Ghent Style Guide

Image gallery

When to use this component

Use the image gallery component on detail pages to present a set of images in a modal image gallery format (lightbox) using thumbnails of the first images.

When not to use this component

Do not use the image gallery component to present a set of images that should be all visible at once without any interaction except scrolling.

Do not use the image gallery component on overview pages or filter pages. Only use the image gallery component on detail pages.

How it works

  • An image gallery is a list of images presented by using figures.
  • The images can have different aspect ratios.
  • Thumbnails are used to preview the set of images.
  • The thumbnails must have aspect ratio 8:5.
  • The number of thumbnails that are shown depends on the device’s screen resolution.
  • Up to 5 thumbnails can be shown.
  • If there are hidden images an overlay is placed on the last visible thumbnail indicating the total number of images.
  • Clicking a thumbail or the overlay on the last visible image shows all images in a modal image gallery (lightbox). The images are all fully shown in their respective aspect ratio.

Implementation

  • Using the class ‘image-gallery’ on the list element triggers lightbox initialisation on page load.

Accessibility

  • The images are placed in a unordered list.
  • When a image caption is provided, this is also used as aria-label for the lightbox link.
  • All lightbox links are described by a hidden span to identify it’s purpose (e.a.: ‘open gallery), add this to the bottom of your page template (above scripts).
  • Hidden images are not only visually hidden.
  • The lightbox can be closed by use of the escape key.
  • Navigating through images in the lightbox can be done by using the arrow keys.
  • The focus cannot be placed on elements which are hidden from view by the lightbox.
<div class="image-gallery--wrapper multiple{% if type %} image-gallery--{{ type }}{% endif %}">
  <ul class="image-gallery js-lightbox">
    {% for item in items %}
      <li>
        <a href="{{ item.src }}" class="gallery-link"
          {% if item.caption %}
            aria-label="{{ item.caption }}"
            aria-describedby="image-gallery__open-gallery"
          {% else %}
            aria-labelledby="image-gallery__open-gallery"
          {% endif %}
          >
          {% include '@image' with {
            "caption": item.caption,
            "src": item.src,
            "alt_text": item.alt,
            "ratio": '8:5'
          } %}
        </a>
      </li>
    {% endfor %}
    <li class="image-gallery__show-more">
      <div class="show-more__content">
        <span class="show-more__total">{{ items.length }}</span>
        <span>Show all photos <i class="icon-fullscreen-enter" aria-hidden="true"></i></span>
      </div>
    </li>
  </ul>
</div>

<!-- Add this to the bottom of your page template, above scripts -->
<!-- <span hidden id="image-gallery__open-gallery">open gallery</span> -->
<div class="image-gallery--wrapper multiple">
    <ul class="image-gallery js-lightbox">
        <li class="image-gallery__show-more">
            <div class="show-more__content">
                <span class="show-more__total"></span>
                <span>Show all photos <i class="icon-fullscreen-enter" aria-hidden="true"></i></span>
            </div>
        </li>
    </ul>
</div>

<!-- Add this to the bottom of your page template, above scripts -->
<!-- <span hidden id="image-gallery__open-gallery">open gallery</span> -->
{
  "src": "https://via.placeholder.com/800x500&text=8:5+(800x500)",
  "alt": "placeholder image alternative text",
  "caption": "image caption"
}
  • Content:
    // sass-lint:disable no-important
    .image-gallery {
      @include clearfix();
    
      position: relative;
      margin: 0;
      padding: 0;
      text-align: center;
    
      li {
        display: inline-block;
        width: calc((100% / 1) - #{$gutter-width});
        margin: calc(#{$gutter-width} / 2);
        float: left;
    
        // mobile first: only show two images
        &:not(.image-gallery__show-more):nth-of-type(n + 3),
        &:first-of-type:nth-last-of-type(-n+3) ~ .image-gallery__show-more {
          display: none;
        }
      }
    
      .image-gallery__show-more {
        display: flex;
        position: absolute;
        right: 0;
        bottom: 0;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        width: calc((100% / 1) - #{$gutter-width});
        margin: calc(#{$gutter-width} / 2);
        padding-bottom: calc(((100% / 1) - #{$gutter-width}) / 1.6);
        pointer-events: none;
      }
    
      .show-more__content {
        @include theme('color', 'color-zero', 'image-gallery-show-more-color');
        @include theme('background-color', 'color-tertiary--lighten-3', 'image-gallery-show-more-background-color');
        @include medium-text;
        @include semi-bold-text;
    
        position: absolute;
        top: 50%;
        padding: .4rem .75rem;
        transform: translate(0, -50%);
        line-height: 1;
        text-align: center;
    
        .show-more__total {
          display: none;
        }
    
        i {
          font-size: 1.3rem;
          vertical-align: middle;
        }
      }
    
      a.gallery-link {
        @include reset-link-background;
    
        display: block;
        width: 100%;
        margin: 0;
        padding: 0;
        font-weight: normal;
        text-decoration: none;
    
        &::after {
          content: none !important;
        }
    
        &:hover,
        &:focus {
          background-color: transparent;
        }
      }
    
      @include phablet {
        // two on each row
        // |____||____|
        li {
          width: calc((100% / 2) - #{$gutter-width} - .5px);
        }
    
        .image-gallery__show-more {
          width: calc((100% / 2) - #{$gutter-width});
          padding-bottom: calc(((100% / 2) - #{$gutter-width}) / 1.6 - .5px);
        }
      }
    
      @include tablet {
        // only show first four images
        li:not(.image-gallery__show-more):nth-of-type(-n + 4) {
          display: inline-block;
        }
    
        // hide 'show more' if there are no more than four images
        li:first-of-type:nth-last-of-type(-n+5) ~ .image-gallery__show-more {
          display: none;
        }
    
        // triple
        // first row: three
        // |__||__||__|
        // subtract .33px to deal with incorrect IE rounding
        li:first-of-type:nth-last-of-type(4),
        li:first-of-type:nth-last-of-type(4) ~ li {
          width: calc((100% / 3) - #{$gutter-width} - .33px);
        }
      }
    
      @include desktop {
        // quintuple
        // first row: two
        // second row: three
        // |____||____|
        // |__||__||__|
        // subtract .33px to deal with incorrect IE rounding
        li:first-of-type:nth-last-of-type(n + 6) ~ li:nth-of-type(n + 3) {
          width: calc((100% / 3) - #{$gutter-width} - .33px);
        }
    
        // only show first five images
        li:not(.image-gallery__show-more):nth-of-type(-n + 5) {
          display: inline-block;
        }
    
        // hide 'show more' if there are no more than five images
        li:first-of-type:nth-last-of-type(-n+6) ~ .image-gallery__show-more {
          display: none;
        }
    
        // subtract .33px to deal with incorrect IE rounding
        li:first-of-type:nth-last-of-type(n + 6) ~ .image-gallery__show-more {
          width: calc((100% / 3) - #{$gutter-width} - .33px);
          padding-bottom: calc(((100% / 3) - #{$gutter-width} - .33px) / 1.6);
        }
      }
    
      // Alternative styling
      &--secondary {
        // single image styling
        .image-gallery {
          text-align: left;
    
          figcaption {
            display: none;
          }
    
          .gallery-link {
            @include mobile {
              max-width: 270px;
            }
    
            @include phablet {
              width: calc(100% / 2 - 1vw);
            }
    
            @include desktop {
              width: calc((100% / 3 - #{$gutter-width} / 1.5) - .33px);
              max-width: 100%;
              margin: calc(#{$gutter-width} / 2) 0 0;
            }
    
            width: 100%;
            margin: 1vw 0 $gutter-width;
          }
        }
    
        // multiple image styling
        ul.image-gallery {
          margin: 0 -1vw;
    
          // overrule single image styling
          .gallery-link {
            width: 100%;
            margin: 0;
          }
    
          li {
            width: calc(100% - 2vw);
            margin: 1vw;
    
            .gallery-link {
              padding-bottom: 2.3em;
            }
    
            &:first-of-type:nth-last-of-type(-n+3) ~ .image-gallery__show-more {
              display: flex;
            }
    
            // only two images
            &:not(.image-gallery__show-more):nth-of-type(n + 2) {
              display: none;
            }
    
            // hide show more if there is only one image
            &:first-of-type:nth-last-of-type(-n+2) ~ .image-gallery__show-more {
              display: none;
            }
          }
    
          // Media Queries
          @include mobile {
            max-width: 13.5rem;
          }
    
          @include phablet {
            max-width: calc(13.5rem * 2 + 2vw);
    
            li {
              &,
              &:first-of-type:nth-last-of-type(4) ~ li,
              &:first-of-type:nth-last-of-type(4) {
                width: calc((100% / 2) - 2vw);
              }
    
              .gallery-link {
                padding: 0;
              }
            }
    
            // only show first two images
            li:not(.image-gallery__show-more):nth-of-type(-n + 2) {
              display: inline-block;
            }
    
            // hide 'show more' if there are no more than two images
            li:first-of-type:nth-last-of-type(-n+3) ~ .image-gallery__show-more {
              display: none;
            }
          }
    
          @include tablet {
            // overrule image gallery
            li:first-of-type:nth-last-of-type(-n+5) ~ .image-gallery__show-more,
            li:first-of-type:nth-last-of-type(-n+6) ~ .image-gallery__show-more {
              display: flex;
            }
    
            // hide 'show more' if there are no more than three images
            li:first-of-type:nth-last-of-type(-n+3) ~ .image-gallery__show-more {
              display: none;
            }
          }
    
          @include desktop {
            max-width: calc(100% + #{$gutter-width});
            margin: 0 calc((#{$gutter-width} / 2) * -1);
    
            li {
              &,
              &:first-of-type:nth-last-of-type(4),
              &:first-of-type:nth-last-of-type(4) ~ li {
                width: calc((100% / 3) - #{$gutter-width} - .33px);
                margin: calc(#{$gutter-width} / 2);
              }
            }
    
            // only show first three images
            li:not(.image-gallery__show-more):nth-of-type(-n + 3) {
              display: inline-block;
            }
    
            // hide 'show more' if there are no more than three images
            li:first-of-type:nth-last-of-type(-n+4) ~ .image-gallery__show-more {
              display: none;
            }
    
            // subtract .33px to deal with incorrect IE rounding
            li:first-of-type:nth-last-of-type(n + 4) ~ .image-gallery__show-more {
              width: calc((100% / 3) - #{$gutter-width} - .33px);
              padding-bottom: calc(((100% / 3) - #{$gutter-width} - .33px) / 1.6);
            }
          }
        }
      }
    }
    
  • URL: /components/raw/image-gallery/_image-gallery.scss
  • Filesystem Path: components/41-organisms/image-gallery/_image-gallery.scss
  • Size: 7 KB