Gallery with Sass, jQuery and Magnific Popup

November 22, 2016 JavaScript Styling

Sometimes you have to create a gallery but the images a client provides you aren't exactly the same size. They're high quality, yes, but then when you create a thumbnail off of them, they always end up being different sizes. It gets tricky when you have to position them.

I was working on a WordPress project the other day and noticed that the image gallery always perfectly centers its image previews. After decostructing the code, I came up with my own gallery example.

WordPress Media Library

As you can see from above, WordPress handles the centering of each image within a perfect square beautifully. We're going to try to achieve the same with our code.

Let's get to the code

The HTML

Just like anything else front-end, we're going to start with the HTML. Here's what the ending template will look like. <div class="gallery-item"> will be repeated with every image. It's the individual template.

<div class="image-gallery">
  <div class="image-gallery-items">
    <div class="gallery-item">
      <div>
        <a href="#link-to-large-image" class="thumbnail">
          <span>
            <img src="#link-to-thumbnail-image" alt="" />
          </span>
        </a>
      </div>
    </div>
  </div>
</div>

Pretty straight-forward template, but just in case, we can break it down a little further. The first two div tags will serve as presentation. The main div.image-gallery will be a simple white box. The child div.image-gallery-items is just a wrapper. You can do what you want with it (I just add a clearfix hack).

Immediately following that, is the individual templates for each image. There's a lot more going on with them. So rather me going through each line, here's the same code with comments on each element that matters

<div class="image-gallery">
  <div class="image-gallery-items">

    <!--
      individual item.
      We're going to repeat this over and over for each image that
      shows each preview.
    -->
    <div class="gallery-item">

      <!-- serves as the relative position point for the thumbnail, so we can center it -->
      <div>

        <!-- this links to the large image -->
        <a href="#link-to-large-image" class="thumbnail">

          <!-- we're going to use this span to center the thumbnail image -->
          <span>

            <!-- the thumbnail image should be substantially smaller -->
            <img src="#link-to-thumbnail-image" alt="" />

          </span>
        </a>        

      </div> <!-- class-less div -->
    </div> <!-- .gallery-item -->

  </div> <!-- .image-gallery-items -->
</div> <!-- .image-gallery -->

The Sass/CSS

We'll get to the main chunk of code in a bit. FIrst we need to style the main div.image-gallery to display our gallery and add some color to our document's body so we can start making it pretty (and yes, it's pink).

// This is just so we can see the white box
body {
  background: #ff71a8;
}

// The wrapper we're going to use to present out
// example. It's just a white box.
.image-gallery {
  width: 100%;
  height: auto;
  max-width: 700px;
  background: #fff;
  overflow: auto;
  padding: 5px;
  margin: 50px auto 0;
  -webkit-box-shadow: 0 3px 10px #ec3f3f;
  box-shadow: 0 3px 10px #ec3f3f;
}

Although this is just a white box, its centered and spaced out with slight margins. It's also responsive and it maxes out at a width of 700 pixels. Lets combine the HTML and preliminary Sass and a few images to the gallery to check out what we've got going on:

See the Pen Gallery Example - Sass 1 by Adrian Ortega (@adrian-ortega) on CodePen.

As you can see, the images stack at the moment. So we can make them go side by side by simply adding a float: left;

.gallery-item {
  float: left;
}

However, all that's going to do is "butt" up each image next to each other and since they're all different heights, it's not going to look like a grid at all. It would look something like this:

Float left makes it look funny

So to fix that, we're going to add a relative positioning, size and then absolutely position the image within the div.gallery-item. That looks like this:

.gallery-item {
  position: relative;
  float: left;
  width: 25%;

  img {
    position: absolute;
    top: 0;
    left: 0;
  }
}

But wait... that causes issues!!!

But wait, that causes issues

This would be easily fixed if we were using a flex type grid. However, I'm oldschool, so let's call the help of the micro clearfix hack and create a mixin for it (we are using Sass after all). Then we can start adding some styling to our immediate child div by using the maintain ratio trick outlined in this article.

The mixin we will add:

// Micro clearfix hack
@mixin clearfix {
  &:before,
  &:after {
    content: " ";
    display: table;
  }
  &:after {
    clear: both;
  }  
}

The added styles to our gallery item:

.gallery-item {
  position: relative;
  float: left;
  width: 25%;

  // child div inside each item
  > div {
    position: relative;

    // we'll add the maintain ratio trick to a 
    // pseudo selector, you'll see why later
    &:before {
      content: "";
      display: block;
      padding-top: 100%;
    }
  }

  // the thumbnail image
  img {
    position: absolute;
    top: 0;
    left: 0;
  }
}

This is what we end up with:

See the Pen Gallery Example - Sass 2.3 by Adrian Ortega (@adrian-ortega) on CodePen.

Yeah, still not what we want but we're making progress and progress is good. We can stop each of the images from colliding by adding a overflow: hidden and forcing each edge of the a.thumbnail element to stretch to each edge with an absolute position.

.thumbnail {
    overflow: hidden;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}

We get something like this.

Add an overflow hidden

Looking good so far! Now that we have something that resembles a grid, we can start fine tuning it. First we'll start by separating the blocks a bit by adding some padding to the div.gallery-item.

.gallery-item {
  position: relative;
  float: left;
  width: 25%;
  padding: 6px;
}

Spaced out

That's a little better. Although this looks like we're done, we're not. At the moment the thumbnail images will take up the size of the div.gallery-item and although it looks like they fit well, they're not centered within the box. You'll notice that the second image (the skyscrapers pixel-art) is slightly shorter than the rest. This is because it has a different aspect ratio. The first thing we're going to attempt is centering the image within the square container.

In our original template, the div.thumbnail also comes with a span that wraps the img tag. We're going to use this span's top left corner as a center point within the original block. Here's what that looks like in code:

.thumbnail {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  overflow: hidden;

  span {
    // Here we tell the span element to position itself
    // to the top left corner of the .thumbnail element
    position: absolute;
    top: 0;
    left: 0;

    // and force its size to fit that of it's parent
    width: 100%;
    height: 100%;

    // Then, here's where the magic happens, we move the
    // top-left corner of the span element down to the 
    // center of the containing .thumbnail element
    -webkit-transform: translate(50%, 50%);
    transform: translate(50%, 50%);
  }
}

After the above changes, you'll notice this happens (I outlined the .thumbnail temporarily so you can see its bounding box)

We center the .thumbnail span

Now that we have the dead center of the .thumbnail we can transform the image to center itself within it.

img {
  position: absolute;
  top: 0;
  left: 0;

  // We force the height of the image to stretch
  // to th eheight of the span. I've set the max
  // width of the image here to `inherit` because
  // some frameworks (like Foundation or Bootstrap)
  // set it to 100%
  max-height: 100%;
  max-width: inherit;

  // Just like the span inside .thumbnail, we're going
  // to ask the img tag to transform itself by moving
  // in the oposite direction we asked the span to.
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
}

And we're back to normal. Notice that the image is now centered and not positioned to the top left of the parent. But wait, there's still some whitespace! This can be fixed by adding a small amount of scale (transform: translare(-50%, -50%) scale(1.03);) to the transform, but for this example, I'm happy with the results.

The JavaScript

To finish this off, I included the Magnific Popup jQuery plugin to act as our lightbox. Its super simple to use and comes with great documentation.

That being said, going through the examples on the website, I came up with a simple IIFE to bind the onclick event of our a.thumbnails as well as add some flair.

Keep in mind that you will need to include jQuery as well as the Magnific Popup plugin before this is run.

(function($) {
  $('a.thumbnail').magnificPopup({
    type: 'image',
    closeOnContentClick: true,
    closeBtnInside: false,
    gallery: { enabled: true },
    fixedContentPos: true,
    mainClass: 'mfp-no-margins mfp-with-zoom',
    image: {
      verticalFit: true
    },
    zoom: {
      enabled: true,
      duration: 300
    }
  });
})(jQuery);

The finished product.

In this finished gallery example, I added a bit more stylistic changes (like a scrolling gradient background and hover states) to the gallery. You'll also notice that theres a few more mixins in there as well. Take a look and see if you can spot them all.

See the Pen Gallery Example by Adrian Ortega (@adrian-ortega) on CodePen.