How to Make a Polaroid Photo Gallery in HTML and CSS

In this post, we are going to make a Polaroid Image Gallery of cute dogs in HTML and CSS using Flexbox.

Some important concepts we will learn while making this gallery:

  • HTML5 <figure> and <figcaption> elements
  • How to stack elements horizontally
  • How to give equal space between elements in a row using justify-content property
  • How to wrap elements inside flexbox without shrinking it using flex-shrink and flex-wrap properties

Let’s dive in.

The Blank Slate

Let’s get on the same page first.

This is what my project folder structure looks like:

Our stylesheet “styles.css” goes inside the “css” folder, the images of Dogs will go inside “images” folder and our HTML code resides in “gallery.html” file.

Here’s the initial code for our blank page:

<!DOCTYPE html>
<html>
  <head>
    <title>Polaroid Photo Gallery</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css">
  </head>
  <body>
    
  </body>
</html>

If you don’t understand the above code then you are probably coding in HTML for the first time. I would recommend starting here: How to Make a Burger in HTML — A Beginner Tutorial.

Dog Pictures: You will need some square images for this tutorial, download the four images that I am using from here: https://imgur.com/a/VWicZ

The <figure> element

Polaroid photos have a photo and some text below it. Let’s create a Polaroid photo by enclosing a <img> tag and a <p> tag inside a <div> in our gallery.html file:

<!DOCTYPE html>
<html>
  <head>
    <title>Polaroid Photo Gallery</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css">
  </head>
  <body>
    <div>
      <img src="images/dog-1.png">
      <p>
        Julie's Rabbit Ears
      </p>
    </div>
  </body>
</html>

If you open it in browser you will see the image with text below it.

This method of displaying an image is totally fine. However, HTML5 introduced two tags: <figure> and <figcaption>, and these tags does exactly what we are doing here - display an image and a caption of the image.

The

and

method is fine, but try to use the new HTML5 tags where possible. The new HTML5 tags like: section, aside, article, header, footer, figure, and others, give meaning to the HTML file. You can use <div> for all these but a div in itself doesn’t have any meaning.

Let’s use <figure> and <figcaption> for our polaroid image:

<!DOCTYPE html>
<html>
  <head>
    <title>Polaroid Photo Gallery</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css">
  </head>
  <body>
    <figure>
      <img src="images/dog-1.png">
      <figcaption>
        Julie's Rabbit Ears
      </figcaption>
    </figure>
  </body>
</html>

On refreshing your browser you will see the output remains same, but the image moved a little to right. That is because of the default margin that <figure> element has. We will deal with it later.

Now, we need to give some style to our image and its caption. The image looks quite big, and the caption text should be in the center. Let’s do this in our styles.css file:

figure{
  width: 200px;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
}

Save the file and refresh your browser to see the difference.

I want you to think about two things here:

  • why did we decide to give width to the figure element, and not the img element?
  • why we gave the img width:100%?

To answer the first question, we gave width to the figure element because that element defines a single unit — one Polaroid photo. The img and figcaption elements are part of that unit. When we need to give some finer details we will style those elements individually, but when we need to give some style to an entire polaroid photo we give style to the <figure> element.

That’s why instead of giving width: 200px to the img and figcaption elements individually, we gave that to the <figure> element.

Try to visualize HTML elements in groups, and your styling will improve automatically. This is something experienced developers get intuitively, and forget to teach to students.

Next, we gave the img width: 100% because by default the img element displays an image in its original dimensions. By giving it width of 100% we said it to be as wide as it’s parent, not more than that, so it will be 200px wide.

Flexbox Layout

Let’s include one more Polaroid image:

<!DOCTYPE html>
<html>
  <head>
    <title>Polaroid Photo Gallery</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css">
  </head>
  <body>
    <figure>
      <img src="images/dog-1.png">
      <figcaption>
        Julie's Rabbit Ears
      </figcaption>
    </figure>
    <figure>
      <img src="images/dog-2.png">
      <figcaption>The Innocent Look</figcaption>
    </figure>
  </body>
</html>

Output:

Oops! The next image is displayed below the first, we want it to be beside it, not below it.

Why did the next image got displayed on next line?

Because <figure> element, just like <div>, is a block element. In HTML, block elements by default don’t allow other elements to display beside it. Pretty arrogant.

There are a number of ways to deal with this arrogance of block elements. A very famous one is “floating”. If we give the <figure> element float: left style then it will allow other elements to display beside it. One more option is to display the <figure> element as inline-block: display: inline-block.

But, we won’t use any of these methods.

Why?

Because these are old methods and introduce other difficulties in the layout. Luckily CSS3 introduced Flexbox layout that can easily deal with this situation.

To use Flexbox layout, we will have to wrap our <figure> elements into an element, we will use a <div> to do that:

<!DOCTYPE html>
<html>
  <head>
    <title>Polaroid Photo Gallery</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css">
  </head>
  <body>
    <div class="gallery">
      <figure>
        <img src="images/dog-1.png">
        <figcaption>
          Julie's Rabbit Ears
        </figcaption>
      </figure>
      <figure>
        <img src="images/dog-2.png">
        <figcaption>The Innocent Look</figcaption>
      </figure>
    </div>
  </body>
</html>

Once our elements are wrapped in a container, we will make that element a flex container by giving it display: flex style:

.gallery{
  display: flex;
}
figure{
  width: 200px;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
}

Output:

Now the images align in the same line.

When we give display: flex to an element the element becomes a “Flex Container”, and that element’s child elements become “Flex Items”. The default behavior or flex-items is that they display in a row.

Which property defines this behavior? flex-direction. The default value of this property is “row” that’s why our figures got aligned in a row. Try to change it to “column” like this: flex-direction: column on the gallery div and see the difference.

Centering in Flexbox layout

The gallery div covers entire width by default. Let’s give it some width and use margin: auto to center it horizontally.

.gallery{
  display: flex;
  width: 900px;
  margin: auto;
}
figure{
  width: 200px;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
}

Okay, the gallery div is centered, but the figure elements are not centered. First, let’s get rid of the default margin on figure element by setting it to 0.

.gallery{
  display: flex;
  width: 900px;
  margin: auto;
  border: 1px dashed gray;
}
figure{
  width: 200px;
  margin: 0;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
}

Output:

I gave a dashed border to the gallery div to see clearly what is happening.

Centering flex-items using “justify-content” property

Since we gave our gallery div display: flex, it has become a flex container and the figure elements have become flex-items. We can use “justify-content” property on the flex-container to align the flex-items inside it.

Remember how we use text-align: center on an element to align its text in the center? This is kind of similar concept, the difference is, the “justify-content” has some other awesome features too that we will discuss shortly.

First, let’s center our figures using justify-content: center on the gallery div:

.gallery{
  display: flex;
  width: 900px;
  margin: auto;
  border: 1px dashed gray;
  justify-content: center;
}
figure{
  width: 200px;
  margin: 0;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
}

Output:

Okay, our figure elements are centered, but they stick together.

You might be thinking, let’s give some margin to the figure element, right?

You are totally right, but we have a more powerful justify-content property here. We saw how to center using justify-content: center, now let’s see what justify-content: space-between can do.

If you set justify-content: space-between on a flex-container, the flex-items inside it are automatically placed in such a way that the space between them are equal. We want exactly this. Let’s do it:

.gallery{
  display: flex;
  width: 900px;
  margin: auto;
  border: 1px dashed gray;
  justify-content: space-between;
}
figure{
  width: 200px;
  margin: 0;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
}

Output:

Since we have only two figure elements inside the gallery div, to have equal space between them one will stick to the left side and other will stick to the right side. Let’s include one more picture and it will make more sense:

<!DOCTYPE html>
<html>
  <head>
    <title>Polaroid Photo Gallery</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css">
  </head>
  <body>
    <div class="gallery">
      <figure>
        <img src="images/dog-1.png">
        <figcaption>Julie's Rabbit Ears</figcaption>
      </figure>
      <figure>
        <img src="images/dog-2.png">
        <figcaption>The Innocent Look</figcaption>
      </figure>
      <figure>
        <img src="images/dog-3.png">
        <figcaption>Big Eyed Buggy</figcaption>
      </figure>
    </div>
  </body>
</html>

Output:

See how the spaces between the polaroids are equal automatically!

Styling the Polaroids

Okay, I admit it — our Polaroid photos are not looking like polaroids. Let’s make them look like polaroids.

Let’s remove the dashed border we gave to the gallery div, and give the figure elements a solid border:

.gallery{
  display: flex;
  width: 900px;
  margin: auto;
  justify-content: space-between;
}
figure{
  width: 200px;
  margin: 0;
  border: 1px solid #777;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
}

Output:

Looks better.

If you have seen polaroid images, you will notice that in a Polaroid the image and text do not touch the borders of the pic. So let’s give some padding to the figure element. And the text would also look good with little extra padding on top and bottom. Let’s do it:

.gallery{
  display: flex;
  width: 900px;
  margin: auto;
  justify-content: space-between;
}
figure{
  width: 200px;
  margin: 0;
  border: 1px solid #777;
  padding: 8px;
  box-sizing: border-box;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
  padding: 8px 4px;
}

Output:

Yay! Now our photos look like polaroids.

Notice we also gave box-sizing: border-box to the figure element, because we don’t want the padding to increase the size of the figure elements, instead we want its contents to shrink to make space for the padding.

Some developers prefer to give all the elements box-sizing: border-box in their CSS like this:

* {
  box-sizing: border-box;
}

This way we don’t have to write box-sizing: border-box every time we give padding or border to an element to prevent it from expanding.

I don’t do this though, just a personal choice.

Okay, now that our photos are looking like polaroids, let’s include the fourth image:

<!DOCTYPE html>
<html>
  <head>
    <title>Polaroid Photo Gallery</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css">
  </head>
  <body>
    <div class="gallery">
      <figure>
        <img src="images/dog-1.png">
        <figcaption>Julie's Rabbit Ears</figcaption>
      </figure>
      <figure>
        <img src="images/dog-2.png">
        <figcaption>The Innocent Look</figcaption>
      </figure>
      <figure>
        <img src="images/dog-3.png">
        <figcaption>Big Eyed Buggy</figcaption>
      </figure>
      <figure>
        <img src="images/dog-4.png">
        <figcaption>The Saint Doggo</figcaption>
      </figure>
    </div>
  </body>
</html>

Output:

Allow or Prevent flex-items to shrink using flex-shrink

Our first row is ready. Let’s include 5th image and see what happens:

<!DOCTYPE html>
<html>
  <head>
    <title>Polaroid Photo Gallery</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css">
  </head>
  <body>
    <div class="gallery">
      <figure>
        <img src="images/dog-1.png">
        <figcaption>Julie's Rabbit Ears</figcaption>
      </figure>
      <figure>
        <img src="images/dog-2.png">
        <figcaption>The Innocent Look</figcaption>
      </figure>
      <figure>
        <img src="images/dog-3.png">
        <figcaption>Big Eyed Buggy</figcaption>
      </figure>
      <figure>
        <img src="images/dog-4.png">
        <figcaption>The Saint Doggo</figcaption>
      </figure>
      <figure>
        <img src="images/dog-3.png">
        <figcaption>Big Eyed Buggy</figcaption>
      </figure>
    </div>
  </body>
</html>

Output:

Well, three things happened:

  • the 5th image didn’t automatically move to next row
  • the spaces between the figures are 0 now
  • the figure elements actually shrank a bit, look carefully

Let’s look at why the images shrunk. We gave figure elements a fixed width of 200px, right? Then how do they shrink?

This doesn’t happen normally, but since we are using flex box, and the figure elements are flex-items, they obey the rules of flexbox.

Flex-items by default have “flex-shrink” property set to “1”. That’s why they shrink. A value “0” will say not to shrink the flex-item. Let’s give flex-shrink: 0 to our figure element and see what happens:

.gallery{
  display: flex;
  width: 900px;
  margin: auto;
  justify-content: space-between;
  border: 2px dashed gray;
}
figure{
  width: 200px;
  margin: 0;
  border: 1px solid #777;
  padding: 8px;
  flex-shrink: 0;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
  padding: 8px 4px;
}
* {
  box-sizing: border-box;
}

Output:

I’ve included a 2px dashed border for you to see that if we set “flex-shrink: 0” the images go beyond the width of our gallery div. We don’t want that. So we will keep the default value of “flex-shrink”.

I included this concept here because you should know why the images shrink automatically, and how to prevent that from happening if needed.

Wrapping flex-items with flex-wrap property

We will remove the dashed border and the flex-shrink: 0, but we still have one problem. Why the 5th image tries to fit in first row instead of moving to next row even when there was not enough space for it in the first row?

That is because of the default value of “flex-wrap” property, which is “nowrap”. When a flex-item’s “flex-wrap” is set to “nowrap” and you include more items, one of the two things happen: if flex-shrink of flex-items is set to 1 the items will shrink to make space for new items, or if flex-shrink is set to 0 then the new items will overflow. In both cases, the new items will be displayed in the same row.

Now, to wrap the flex-items, just like we do in our word process, set flex-wrap: wrap on the flex-container:

.gallery{
  display: flex;
  width: 900px;
  margin: auto;
  justify-content: space-between;
  flex-wrap: wrap;
  border: 2px dashed gray;
}
figure{
  width: 200px;
  margin: 0;
  border: 1px solid #777;
  padding: 8px;
  box-sizing: border-box;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
  padding: 8px 4px;
}

Output:

Fantastic, our 5th element moved to the new row.

Since we removed all margins from figure element the figures in both row will stick to each other. We will add some margin, but first, let’s add rest of the 3 pictures of our Polaroid gallery:

<!DOCTYPE html>
<html>
  <head>
    <title>Polaroid Photo Gallery</title>
    <link rel="stylesheet" type="text/css" href="css/styles.css">
  </head>
  <body>
    <div class="gallery">
      <figure>
        <img src="images/dog-1.png">
        <figcaption>Julie's Rabbit Ears</figcaption>
      </figure>
      <figure>
        <img src="images/dog-2.png">
        <figcaption>The Innocent Look</figcaption>
      </figure>
      <figure>
        <img src="images/dog-3.png">
        <figcaption>Big Eyed Buggy</figcaption>
      </figure>
      <figure>
        <img src="images/dog-4.png">
        <figcaption>The Saint Doggo</figcaption>
      </figure>
      <figure>
        <img src="images/dog-3.png">
        <figcaption>Big Eyed Buggy</figcaption>
      </figure>
      <figure>
        <img src="images/dog-2.png">
        <figcaption>The Innocent Look</figcaption>
      </figure>
      <figure>
        <img src="images/dog-4.png">
        <figcaption>The Saint Doggo</figcaption>
      </figure>
      <figure>
        <img src="images/dog-1.png">
        <figcaption>Julie's Rabbit Ears</figcaption>
      </figure>
    </div>
  </body>
</html>

Output:

Great, now let’s give some final styling like margin and background-color:

body{
  background-color: #dedede;
}
.gallery{
  display: flex;
  width: 900px;
  margin: auto;
  justify-content: space-between;
  flex-wrap: wrap;
}
figure{
  width: 200px;
  margin: 8px 0;
  border: 1px solid #777;
  padding: 8px;
  box-sizing: border-box;
  background-color: #fff;
}
figure img{
  width: 100%;
}
figure figcaption{
  text-align: center;
  padding: 8px 4px;
}

Output:

Our Polaroid Photo Gallery is ready!