How To Build Responsive Gallery With Foundation and Modals

How To Build Responsive Gallery With Foundation and Modals

Table of Contents

Beautiful and fully responsive gallery can make your website stand out. And, if you use some front-end framework, you can get this done in a few minutes. This is what this tutorial will be about. We will use Foundation framework along with a few custom styles and JavaScript to create responsive gallery with reveal modals. In addition, you will have a chance to practice some features of ES6 such as arrow functions. Enjoy this tutorial on building responsive gallery with Foundation!

Live demo on CodePen.

Source code on GitHub.

Introduction

Welcome in another simple tutorial! The goal of this tutorial is building responsive gallery with custom modals. To achieve this, we will use Foundation framework and its grid system based on flexbox. If you have never used this framework, then this tutorial will be a gentle introduction. I wanted to make this responsive gallery tutorial as technology-neutral and portable as possible. As a result, we will not need to use many external resources. In a fact, we will need only one.

This resource will be Foundation and its flexbox grid system. In this tutorial, I used version hosted on CDN. If you want to use complete Foundation framework, you can download it from Foundation website. However, this is not necessary nor required. Also, there is one thing I want to mention before we get started this responsive gallery tutorial. We will use two plugins, autoprefixer and babel. Autoprefixer adds CSS vendor prefixes. Babel compiles ES6 syntax.

HTML

The first part of this responsive gallery tutorial is rests in creating html document. Since the content of head section will depend on your own project, we will include only the necessary tags. These tags will be title, stylesheet links, and meta tags for charset and viewport. As I mentioned above, we will use only one external CSS resource, Foundation framework. Another, and also the last, CSS resource will be main stylesheet.

HTML:

<head>
 <meta charset="utf-8">

 <title>Responsive Gallery with Foundation</title>

 <meta name="viewport" content="width=device-width, initial-scale=1">

 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.2.4/foundation-flex.min.css">

 <link rel="stylesheet" href="styles/main.css">
</head>

Preparing the content for responsive gallery

The body of html document will consist of two pats. First will be gallery wrapper with individual images provided by Unsplash. These images are royalty-free. So, if you like some of them, you can use it. However, I would still recommend to give credit to a photographer. In this responsive gallery tutorial, we will mention every photographer in alt attributes of images. Every image will be wrapped inside anchor tag. Every tag will have data-toggle attribute with value of “galleryModal”. We will use this attribute later in JavaScript to find these tags.

HTML:

<!-- Gallery wrapper -->
<div class="gallery-wrapper row expanded">
 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/zZzKLzKP24o/600x600" alt="Photo by Caleb George" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/3YfSwCfJ6Do/600x600" alt="Photo by Garth Pratt" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/8Pd8yCjjKIQ/600x600" alt="Photo by Maxime Le Conte des Floris" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/2Bjq3A7rGn4/600x600" alt="Photo by Martin Jernberg" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/EDqP5r_QwrE/600x600" alt="Photo by Alec Weir" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/IEqPWQvnR1E/600x600" alt="Photo by Jaclyn Clark" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/4KgtxwMeLYw/600x600" alt="Photo by Andrew Collins" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/GnU2altkUSQ/600x600" alt="Photo by Clem Onojeghuo" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/Yfyt6q8gjaQ/600x600" alt="Photo by Clem Onojeghuo" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/8soQzdz9XXA/600x600" alt="Photo by Clem Onojeghuo" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/myFsTTkub9E/600x600" alt="Photo by Scott Webb" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/3kmDKoHY7as/600x600" alt="Photo by Matheus Ferrero" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/ZMRMFULofus/600x600" alt="Photo by Joel Filipe" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/n3FM6tfn9XQ/600x600" alt="Photo by Aaron Barnaby" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/SdEDmcrJOE4/600x600" alt="Photo by Geetanjal Khanna" />
  </a>
 </div>

 <div class="medium-6 large-3 xlarge-2">
  <a href="#" data-toggle="galleryModal">
   <img src="https://source.unsplash.com/6hC831t-ihM/600x600" alt="Photo by Anton Romanov" />
  </a>
 </div>
</div><!-- .end Gallery wrapper -->

Preparing the structure for reveal overlay and dialog

The second part of this responsive gallery tutorial related to html will be reveal overlay and reveal dialog (or modal). In addition, reveal dialog (modal) will also contain a number of components. We will need h2 heading for photo info and its author and img tag for preview image. Also, we will need a button to close the reveal dialog. Reveal dialog will be wrapped inside reveal overlay. I used plain div for reveal dialog. However, you can also use article element. Then, you can also use h1.

HTML:

<!-- Gallery overlay -->
<div class="js-gallery-overlay gallery-overlay reveal-overlay">
 <!-- Gallery modal -->
 <div class="js-gallery-reveal gallery-reveal reveal" id="galleryModal" data-reveal data-overlay="true">
  <h2 class="js-modal-author modal-author"></h2>

  <img class="js-modal-preview modal-preview" src="" alt="" />

  <button class="js-close-button close-button" data-close aria-label="Close modal" type="button">
   <span aria-hidden="true">&times;</span>
  </button>
 </div><!-- .end Gallery modal -->
</div><!-- .end Gallery overlay -->

Putting it all together

This is all we need for html to get this responsive gallery work. Well, almost. We will need to add link to include scripts that will enhance this responsive gallery with reveal dialog. That will be all. So, let’s now finish this part by putting together these three parts. Then, we can move on to Sass (or CSS) and add some styling to our responsive gallery.

Whole HTML:

<!doctype html>
<html>
<head>
 <meta charset="utf-8">
 
 <title>Responsive Gallery with Foundation</title>
 
 <meta name="viewport" content="width=device-width, initial-scale=1">
 
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.2.4/foundation-flex.min.css">
 
 <link rel="stylesheet" href="styles/main.css">
</head>
 
<body>
 <!-- Gallery wrapper -->
 <div class="gallery-wrapper row expanded">
  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/zZzKLzKP24o/600x600" alt="Photo by Caleb George" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/3YfSwCfJ6Do/600x600" alt="Photo by Garth Pratt" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/8Pd8yCjjKIQ/600x600" alt="Photo by Maxime Le Conte des Floris" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/2Bjq3A7rGn4/600x600" alt="Photo by Martin Jernberg" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/EDqP5r_QwrE/600x600" alt="Photo by Alec Weir" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/IEqPWQvnR1E/600x600" alt="Photo by Jaclyn Clark" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/4KgtxwMeLYw/600x600" alt="Photo by Andrew Collins" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/GnU2altkUSQ/600x600" alt="Photo by Clem Onojeghuo" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/Yfyt6q8gjaQ/600x600" alt="Photo by Clem Onojeghuo" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/8soQzdz9XXA/600x600" alt="Photo by Clem Onojeghuo" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/myFsTTkub9E/600x600" alt="Photo by Scott Webb" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/3kmDKoHY7as/600x600" alt="Photo by Matheus Ferrero" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/ZMRMFULofus/600x600" alt="Photo by Joel Filipe" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/n3FM6tfn9XQ/600x600" alt="Photo by Aaron Barnaby" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/SdEDmcrJOE4/600x600" alt="Photo by Geetanjal Khanna" />
   </a>
  </div>

  <div class="medium-6 large-3 xlarge-2">
   <a href="#" data-toggle="galleryModal">
    <img src="https://source.unsplash.com/6hC831t-ihM/600x600" alt="Photo by Anton Romanov" />
   </a>
  </div>
 </div><!-- .end Gallery wrapper -->

 <!-- Gallery overlay -->
 <div class="js-gallery-overlay gallery-overlay reveal-overlay">
  <!-- Gallery modal -->
  <div class="js-gallery-reveal gallery-reveal reveal" id="galleryModal" data-reveal data-overlay="true">
   <h2 class="js-modal-author modal-author"></h2>

   <img class="js-modal-preview modal-preview" src="" alt="" />

   <button class="js-close-button close-button" data-close aria-label="Close modal" type="button">
    <span aria-hidden="true">&times;</span>
   </button>
  </div><!-- .end Gallery modal -->
 </div><!-- .end Gallery overlay -->

 <!-- Scripts -->
 <script src="scripts/main.js"></script>
</body>
</html>

CSS and Sass

The biggest benefit of using a front-end framework, such as Foundation, is that you don’t have to style everything. In case of this responsive gallery, Foundation will take care about the majority of styling. However, we will hack it and add couple of overrides. Before we add these overrides, let’s also add one Sass variable and function to make our work easier. We will use that variable for breakpoint. This will be our first hack. What about that function? It will convert pixels to rems.

Sass:

// Function to convert px to rems
@function remy($value, $base: 16px) {
 @return ($value / $base) * 1rem;
}

// Variables
$breakpoint-xlarge: 1200px;

This is all for our Sass workflow. What if you are not familiar with Sass and you want to learn it? Then, check out this short 5-minute Sass tutorial I prepared for you. Our next step will be resetting the font-size of html and body elements. We will be using rem units and this will help us keep styles consistent. Quick side note: Foundation sets font-size of html to “100%”. And, what about body element? It leaves it untouched.

Sass:

/* Base resets */
html {
 font-size: 16px;
}

body {
 font-size: 100%;
}

Hacking the grid and adding new columns

Now, it is time to introduce our first hacks. We will create additional column for grid. This column will use class xlarge-* and will target resolution 1200px and higher ($breakpoint-xlarge variable). This means that we will have to create styles for all twelve columns for this breakpoint. Well, we can create just one column. This responsive gallery will use only xlarge-2 class. Therefore, we can create styles only for this class. However, I decided to create styles for all twelve columns in case you want to use different grid structure.

Sass:

/* Extending Foundation grid */
@media screen and (min-width: $breakpoint-xlarge) {
 .xlarge-1 {
  flex: 0 0 8.33333%;
  max-width: 8.33333%
 }
 .xlarge-2 {
  flex: 0 0 16.66667%;
  max-width: 16.66667%
 }
 .xlarge-3 {
  flex: 0 0 25%;
  max-width: 25%
 }
 .xlarge-4 {
  flex: 0 0 33.33333%;
  max-width: 33.33333%
 }
 .xlarge-5 {
  flex: 0 0 41.66667%;
  max-width: 41.66667%
 }
 .xlarge-6 {
  flex: 0 0 50%;
  max-width: 50%
 }
 .xlarge-7 {
  flex: 0 0 58.33333%;
  max-width: 58.33333%
 }
 .xlarge-8 {
  flex: 0 0 66.66667%;
  max-width: 66.66667%
 }
 .xlarge-9 {
  flex: 0 0 75%;
  max-width: 75%
 }
 .xlarge-10 {
  flex: 0 0 83.33333%;
  max-width: 83.33333%
 }
 .xlarge-11 {
  flex: 0 0 91.66667%;
  max-width: 91.66667%
 }
 .xlarge-12 {
  flex: 0 0 100%;
  max-width: 100%
 }
}

Hacking reveal overlay and dialog

Let’s move to the second, third and fourth hack. There are couple of things in Foundation I didn’t like. First, it shows vertical scrollbar in overlay. I think that it is necessary to do that unless the content doesn’t fit the overlay. Therefore, we will set the overflow property of reveal-overlay to “auto”. Next thing is that Foundation doesn’t vertically and horizontally center the reveal dialog. We will fix that by using a combo of position, top, left and transform properties.

Another thing that Foundation does is that it sets the min-height and height to “100vh” on small screens. This is not necessary. Also, it might look very bad if the image is also not full-height. So, we will reset this behavior by setting the min-height to “auto” and height to “1px”. The last two things related to reveal dialog are removing default border and adding very subtle box-shadow.

Sass:

/* Override for Foundation reveal overlay */
.reveal-overlay {
 overflow: auto;
}
/* Override for Foundation reveal (modal) */
.gallery-reveal {
 position: absolute;
 top: 50%;
 left: 50%;
 min-height: 1px;
 height: auto;
 border: 0;
 box-shadow: 0 remy(2px) remy(4px) rgba(0, 0, 0, .5);
 transform: translate(-50%, -50%);
}

Let’s now take a quick look at the content of our reveal dialog. There are two things we need to take about. The first one is styling h2 heading we used to show the author of photo. You can also use it for a short info about the photo. Finally, let’s make the change of button color on hover a bit smoother by adding transition.

Sass:

/* Custom styles for author */
.modal-author {
 margin-bottom: remy(8px);
 font-size: remy(16px);
}

.close-button {
 transition: color .25s cubic-bezier(.4, 0, 1, 1);
}

Putting it all together

This is everything we need for our responsive gallery in case of Sass. Again, let’s put all the styles we created above together.

Sass:

// Function to convert px to rems
@function remy($value, $base: 16px) {
 @return ($value / $base) * 1rem;
}

// Variables
$breakpoint-xlarge: 1200px;

/* Base resets */
html {
 font-size: 16px;
}

body {
 font-size: 100%;
}

/* Extending Foundation grid */
@media screen and (min-width: $breakpoint-xlarge) {
 .xlarge-1 {
  flex: 0 0 8.33333%;
  max-width: 8.33333%
 }
 .xlarge-2 {
  flex: 0 0 16.66667%;
  max-width: 16.66667%
 }
 .xlarge-3 {
  flex: 0 0 25%;
  max-width: 25%
 }
 .xlarge-4 {
  flex: 0 0 33.33333%;
  max-width: 33.33333%
 }
 .xlarge-5 {
  flex: 0 0 41.66667%;
  max-width: 41.66667%
 }
 .xlarge-6 {
  flex: 0 0 50%;
  max-width: 50%
 }
 .xlarge-7 {
  flex: 0 0 58.33333%;
  max-width: 58.33333%
 }
 .xlarge-8 {
  flex: 0 0 66.66667%;
  max-width: 66.66667%
 }
 .xlarge-9 {
  flex: 0 0 75%;
  max-width: 75%
 }
 .xlarge-10 {
  flex: 0 0 83.33333%;
  max-width: 83.33333%
 }
 .xlarge-11 {
  flex: 0 0 91.66667%;
  max-width: 91.66667%
 }
 .xlarge-12 {
  flex: 0 0 100%;
  max-width: 100%
 }
}

/* Override for Foundation reveal overlay */
.reveal-overlay {
 overflow: auto;
}

/* Override for Foundation reveal (modal) */
.gallery-reveal {
 position: absolute;
 top: 50%;
 left: 50%;
 min-height: 1px;
 height: auto;
 border: 0;
 box-shadow: 0 remy(2px) remy(4px) rgba(0, 0, 0, .5);
 transform: translate(-50%, -50%);
}

/* Custom styles for author */
.modal-author {
 margin-bottom: remy(8px);
 font-size: remy(16px);
}

.close-button {
 transition: color .25s cubic-bezier(.4, 0, 1, 1);
}

JavaScript

This is the third and last part of this responsive gallery tutorial. In this part, we will add a bit of JavaScript to handle the reveal overlay and dialog. Also, we will write some simple scripts for showing the clicked image in a preview inside the reveal dialog. The first step is getting the html elements we will work with and storing them inside variables. Let’s put all these variables inside one object literal. This will help us keep the code tidy. I used const because they will not change.

JavaScript (ES6):

(() => {
 const galleryVariables = {
  galleryAuthor: document.querySelector('.js-modal-author'),
  galleryButton: document.querySelector('.js-close-button'),
  galleryLinks: document.querySelectorAll('a[data-toggle="galleryModal"]'),
  galleryModal: document.querySelector('.js-gallery-reveal'),
  galleryOverlay: document.querySelector('.js-gallery-overlay'),
  galleryPreview: document.querySelector('.js-modal-preview'),
  galleryPreviewAuthor: document.querySelector('.js-modal-author')
 }
})();

Adding custom fade in and fade out functions

If we want to show or hide the reveal, we can use a bit of JavaScript to change its CSS display property. However, this doesn’t look very good. We can make this interaction much smoother. So, let’s create two functions, fadeIn and fadeOut. These functions will use setInterval and CSS opacity and display property to show the reveal dialog.

JavaScript (ES6):

// Fade in function
const fadeIn = (element) => {
 // Create variable with initial opacity
 let elementOpacity = 0.1;

 // Create timer for fadeIn function
 let timer = setInterval(() => {
  if (elementOpacity >= 1) {
   // Reset interval
   clearInterval(timer);
  }

  // Set element opacity to elementOpacity
  element.style.opacity = elementOpacity;

  // Increase value of elementOpacity
  elementOpacity += elementOpacity * 0.1;

  // Show element
  element.style.display = 'block';
 }, 15);
}

// Fade out function
const fadeOut = (element) => {
 // Create variable with initial opacity
 let elementOpacity = 1;

 // Create timer for fadeOut function
 let timer = setInterval(() => {
  if (elementOpacity <= 0.1) {
   // Reset interval
   clearInterval(timer);

   // Hide element
   element.style.display = 'none';
  }

  // Set element opacity to elementOpacity
  element.style.opacity = elementOpacity;

  // Decrease value of elementOpacity
  elementOpacity -= elementOpacity * 0.1;
 }, 15);
}

Showing reveal overlay and reveal dialog

Next, we will use for loop to loop through all the anchor tags with images. Then, we will use addEventListener to take extract the data in alt and src attributes and from clicked image. This is done by using getAttribute() method and textContent property. We will then take this data and use setAttribute() method to update the img element inside the reveal dialog. After that, we will check if all these information are in place. If it’s true, we will hide overflow of body element, set display property of reveal dialog to “block” and use fadeIn function to show the reveal overlay. As a result, we will smoothly reveal the reveal dialog with proper preview image.

JavaScript (ES6):

// Open reveal dialog by clicking on images
for (let x = 0, y = galleryVariables.galleryLinks.length; x < y; x++) {
 // Add event listener for image containers
 galleryVariables.galleryLinks[x].addEventListener('click', (e) => {
  e.preventDefault();

  // Get clicked image
  let image = e.target;

  // Get src of the image
  let imageSrc = image.getAttribute('src');

  // Get alt of the image
  let imageAlt = image.getAttribute('alt');

  // Set alt attribute of preview image to clicked image
  galleryVariables.galleryPreview.setAttribute('alt', imageAlt);

  // Set src attribute of preview image to clicked image
  galleryVariables.galleryPreview.setAttribute('src', imageSrc);

  // Set preview headline to alt of clicked photo
  galleryVariables.galleryPreviewAuthor.textContent = imageAlt;

  // Avoid openning modal unless its content is loaded
  if (galleryVariables.galleryPreview.getAttribute('src').length > 0 && galleryVariables.galleryPreview.getAttribute('alt').length > 0 && galleryVariables.galleryPreviewAuthor.textContent.length > 0) {
   // Add Foundation class to hide overflow
   document.body.classList.add('is-reveal-open');

   // Show modal dialog
   galleryVariables.galleryModal.style.display = 'block';

   // Show modal overlay (fade it in)
   fadeIn(galleryVariables.galleryOverlay);
  }
 });
}

Closing reveal overlay and reveal dialog

Okay, we have a way to open the reveal dialog. The last thing we need is some function to close it. Let’s create new event listener and bind it to the close button inside the reveal dialog. To do this, we will first use our fadeOut function and wait for a moment. Otherwise, the effect of closing reveal dialog wouldn’t be as smooth as we want. So, let’s use setTimeout method and use display property to hide the reveal dialog. In addition, we will use setAttribute() method and textContent property to reset the preview image and info text. Finally, we will rest overflow of body element.

JavaScript (ES6):

// Close reveal dialog
galleryVariables.galleryButton.addEventListener('click', (e) => {
 e.preventDefault();

 // Hide modal overlay (fade it out)
 fadeOut(galleryVariables.galleryOverlay);

 // Wait before overlay fades out
 setTimeout(() => {
  // Hide modal dialog
  galleryVariables.galleryModal.style.display = 'none';

  // Reset alt attribute of preview image
  galleryVariables.galleryPreview.setAttribute('alt', '');

  // Reset src attribute of preview image
  galleryVariables.galleryPreview.setAttribute('src', '');

  // Reset text for preview headline
  galleryVariables.galleryPreviewAuthor.textContent = '';

  // Remove Foundation class to hide overflow
  document.body.classList.remove('is-reveal-open');
 }, 250);
});

Putting it all together

This is the JavaScript code we need to make this responsive gallery work. Now, let’s put all the previous JavaScript parts together. Then, you will see the whole code for our responsive gallery at once.

JavaScript (ES6):

(() => {
 'use strict';

 // Create object literal with project variables
 const galleryVariables = {
  galleryAuthor: document.querySelector('.js-modal-author'),
  galleryButton: document.querySelector('.js-close-button'),
  galleryLinks: document.querySelectorAll('a[data-toggle="galleryModal"]'),
  galleryModal: document.querySelector('.js-gallery-reveal'),
  galleryOverlay: document.querySelector('.js-gallery-overlay'),
  galleryPreview: document.querySelector('.js-modal-preview'),
  galleryPreviewAuthor: document.querySelector('.js-modal-author')
 }

 // Fade in function
 const fadeIn = (element) => {
  // Create variable with initial opacity
  let elementOpacity = 0.1;

  // Create timer for fadeIn function
  let timer = setInterval(() => {
   if (elementOpacity >= 1) {
    // Reset interval
    clearInterval(timer);
   }

   // Set element opacity to elementOpacity
   element.style.opacity = elementOpacity;

   // Increase value of elementOpacity
   elementOpacity += elementOpacity * 0.1;

   // Show element
   element.style.display = 'block';
  }, 15);
 }

 // Fade out function
 const fadeOut = (element) => {
  // Create variable with initial opacity
  let elementOpacity = 1;

  // Create timer for fadeOut function
  let timer = setInterval(() => {
   if (elementOpacity <= 0.1) {
    // Reset interval
    clearInterval(timer);

    // Hide element
    element.style.display = 'none';
   }

   // Set element opacity to elementOpacity
   element.style.opacity = elementOpacity;

   // Decrease value of elementOpacity
   elementOpacity -= elementOpacity * 0.1;
  }, 15);
 }

 // Open reveal dialog by clicking on images
 for (let x = 0, y = galleryVariables.galleryLinks.length; x < y; x++) {
  // Add event listener for image containers
  galleryVariables.galleryLinks[x].addEventListener('click', (e) => {
   e.preventDefault();

   // Get clicked image
   let image = e.target;

   // Get src of the image
   let imageSrc = image.getAttribute('src');

   // Get alt of the image
   let imageAlt = image.getAttribute('alt');

   // Set alt attribute of preview image to clicked image
   galleryVariables.galleryPreview.setAttribute('alt', imageAlt);

   // Set src attribute of preview image to clicked image
   galleryVariables.galleryPreview.setAttribute('src', imageSrc);

   // Set preview headline to alt of clicked photo
   galleryVariables.galleryPreviewAuthor.textContent = imageAlt;

   // Avoid openning modal unless its content is loaded
   if (galleryVariables.galleryPreview.getAttribute('src').length > 0 && galleryVariables.galleryPreview.getAttribute('alt').length > 0 && galleryVariables.galleryPreviewAuthor.textContent.length > 0) {
    // Add Foundation class to hide overflow
    document.body.classList.add('is-reveal-open');

    // Show modal dialog
    galleryVariables.galleryModal.style.display = 'block';

    // Show modal overlay (fade it in)
    fadeIn(galleryVariables.galleryOverlay);
   }
  });
 }

 // Close reveal dialog
 galleryVariables.galleryButton.addEventListener('click', (e) => {
  e.preventDefault();

  // Hide modal overlay (fade it out)
  fadeOut(galleryVariables.galleryOverlay);

  // Wait before overlay fades out
  setTimeout(() => {
   // Hide modal dialog
   galleryVariables.galleryModal.style.display = 'none';

   // Reset alt attribute of preview image
   galleryVariables.galleryPreview.setAttribute('alt', '');

   // Reset src attribute of preview image
   galleryVariables.galleryPreview.setAttribute('src', '');

   // Reset text for preview headline
   galleryVariables.galleryPreviewAuthor.textContent = '';

   // Remove Foundation class to hide overflow
   document.body.classList.remove('is-reveal-open');
  }, 250);
 });
});

Closing thoughts on building responsive gallery with Foundation

Hey, congrats! You’ve just finished this tutorial and created responsive gallery with custom reveal dialog. Chances are that you’ve also learned a bit about Foundation framework and ES6. So, now you can take what you’ve learned and implement it. And, what if you want to take this responsive gallery on another level? You can take a look at this React tutorial and follow this tutorial using React! Have a great day and see you soon.

If you liked this article, please subscribe so you don't miss any future post.

If you'd like to support me and this blog, you can become a patron, or you can buy me a coffee 🙂

By Alex Devero

I'm Founder/CEO of DEVERO Corporation. Entrepreneur, designer, developer. My mission and MTP is to accelerate the development of humankind through technology.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.