Create a CSS only image gallery

Sometimes all you need is a simple web page. You might be creating a prototype or throwing together a landing page to gauge interest for a new product. Setting up environments and builds can take up unnecessary time when you can just use any text editor to write a simple HTML & CSS web page and run it straight in the browser! However, if that page needs a gallery you usually end up finding a JavaScript library to make it work.

But you don’t need JavaScript to create a library. Well, you probably do if you want some complex logic, but if you just need a way to show people a larger version of a thumbnail you can do it with CSS alone. It’s pretty straight forward, I’ll show you.

Start with the structure

Let’s start with the HTML. The structure is important here as we are going to be using radio inputs to determine the selected image. Therefore, the input, thumbnail and large image all need to be in a container together and in a specific order. Look at the sample HTML code below:

<section class="gallery">
  <div class="gallery__item">
    <input
      type="radio"
      id="img-1"
      checked
      name="gallery"
      class="gallery__selector"
    />
    <img
      class="gallery__img"
      src="https://i.picsum.photos/id/1015/600/400.jpg"
      alt=""
    />
    <label for="img-1" class="gallery__thumb">
      <img src="https://i.picsum.photos/id/1015/150/100.jpg" alt="" />
    </label>
  </div>
</section>

Make sure the ID on the input matches the for on the <label> element. The large image should be after the <input/> tag and the thumbnail should go inside the <label>. Create multiple gallery__item divs, one for each of your images. Remember to make sure each has a unique ID but the same name attribute for the functionality to work.

Make it look good

Now for the styling. I will be using SCSS in the examples just to make it a little easier. Let’s start with some basic setup:

* {
  box-sizing: border-box;
}

$max-img-width: 600px;
$max-img-height: 400px;

img {
  max-width: 100%;
  vertical-align: top;
}

The gallery will be responsive but you will need a maximum width and height of your large image. There are also some additional properties for all the images. The vertical alignment will remove the space below the images and the maximum width will mean the images will scale down within a container.

Now to add some styling to the gallery container. It will be centred and capped at the larges width of the image. The padding will create a space for the large image to be positioned in and the percentage padding will allow the image to maintain the correct aspect ratio.

.gallery {
  display: flex;
  margin: 10px auto;
  max-width: $max-img-width;
  position: relative;
  padding-top: $max-img-height/$max-img-width * 100%;

  @media screen and (min-width: $max-img-width) {
    padding-top: $max-img-height;
  }
}

The image needs to be absolutely positioned within the padding space created by the container. It also means it will break out of the row of thumbnails. You can change the image transition and the thumbnail spacing to best match the rest of your page.

.gallery__img {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

.gallery__thumb {
  padding-top: 6px;
  margin: 6px;
  display: block;
}

The styling for the input is the most important part, and it’s what makes everything work. The input itself needs to be hidden so you don’t have a radio button floating around and then the images can be styled when the input is selected. In this case, the opacity is changed to make the large image visible and the thumbnail image gets a box-shadow so you can see that it’s selected.

.gallery__selector {
  position: absolute;
  opacity: 0;
  visibility: hidden;

  &:checked {
    + .gallery__img {
      opacity: 1;
    }
    ~ .gallery__thumb > img {
      box-shadow: 0 0 0 3px #0be2f6;
    }
  }
}

Final result

That’s it! Now you have a functional image gallery without needing any JavaScript. Here is an example of what the final result looks like:

Copyright © 2020