My WebDev Notes: Photo gallery with CSS Grid
Tags: html, css
Published: 2020-04-07 14:00:35

---
title: My WebDev Notes: Photo gallery with CSS Grid
published: true
description: A detailed experiment on how to create a photo gallery with CSS Grid
tags: html, css
series: My WebDev Notes
---

> <b>Check the gallery online: https://ziizium.github.io/my-webdev-notes/photo-gallery/</b>

##__Introduction__

Almost all websites on the Internet have one form of image or another. The images can be in different extensions (`png`, `jpg`, `jpeg` e.t.c) and if you have a section on your website dedicated to images or to show your photography skills you'll definitely want to present them in a beautiful layout.

This experiment will show you how to achieve that or at least give you a foundation that you can build upon.

The image below is the final layout of this experiment.

![An image gallery with CSS Grid](https://dev-to-uploads.s3.amazonaws.com/i/3a6yp1i6owtk2tak58l8.jpg)

From the post title, you should know that the layout is possible thanks to CSS Grid, but CSS Grid is not supported in some browsers, therefore, it's best if we start with a _progressive enhancement_ mindset. 

##__Progressive enhancement__

Progressive enhancement is a development approach that involves writing code that's guaranteed to run in most browsers then adding code that will work in modern browsers. Therefore, all users get the baseline experience and will only get the added functionality if their browser supports it.

The HTML for the gallery is given below. It's made up of `<figure>` elements which in turn contains the images in `<img>` tags then `<figcaption>` element for each image. 

```html
<div class="gallery">
    <figure class="gallery__figure large-image">
        <img class="gallery__image" src="img/usher.jpg" alt="A pciture of Usher Raymond">
        <figcaption class="gallery__caption">Usher Raymond</figcaption>
    </figure>

    <figure class="gallery__figure">
        <img class="gallery__image" src="img/full_moon.jpg" alt="A picture of the full moon">
        <figcaption class="gallery__caption">Full moon</figcaption>
    </figure>

    <figure class="gallery__figure large-image">
        <img class="gallery__image" src="img/trees.jpg" alt="A picture of trees">
        <figcaption class="gallery__caption">Trees</figcaption>
    </figure>

    <figure class="gallery__figure">
        <img class="gallery__image" src="img/bird.jpg" alt="A pictire of white birds" />
        <figcaption class="gallery__caption">Bird</figcaption>
    </figure>

    <figure class="gallery__figure large-image">
        <img class="gallery__image" src="img/manatees.jpg" alt="A picture of manatees">
        <figcaption class="gallery__caption">Manatees</figcaption>
    </figure>
    
    <figure class="gallery__figure">
        <img class="gallery__image" src="img/sunset.jpg" alt="A pictire of sunset" />
        <figcaption class="gallery__caption">Sunset</figcaption>
    </figure>

    <figure class="gallery__figure large-image">
        <img class="gallery__image" src="img/cat.jpg" alt="A pictire of a white cat" />
        <figcaption class="gallery__caption">Cat</figcaption>
    </figure>

    <figure class="gallery__figure">
        <img class="gallery__image" src="img/horse_unicorn.jpg" alt="A pictire of an horse unicorn" />
        <figcaption class="gallery__caption">Horse Unicorn</figcaption>
    </figure>
</div>
```

You can [download freely usable images from Unsplash](https://unsplash.com).

The CSS is the most most important part of this experiment therefore, we'll dissect it together. The code snippet below resets the margin and padding for all elements and sets the `box-sizing` property to `border-box`.

The background color of the `<body>` element is also changed. The `font-family` is set to `Trebuchet MS` with back up _font families_ and we add a little bit of padding.

```css
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    background-color: #807d6c;
    font-family: "Trebuchet Ms",Verdana, Georgia, sans-serif;
    padding: 0.7em;
}
```

When you reload your browser, you'll notice the changes and if you are using high-resolution images they will be in their default size occupying the entire viewport.

The following snippet will get us underway. We set the `display` property of the `gallery__figure` to `inline-block`. This will cause the images to appear on a single line and they'll fall to another line if there is no longer space. The images are also constrained to a maximum width of `300px` so that we can have enough images on a single line at a larger viewport.

```css
.gallery__figure {
    margin: 0;
    display: inline-block;
    max-width: 300px;
}
```

When you reload your browser, the images will overlap with each other because we have not constrained their width to their parent container.

![A crashed layout](https://dev-to-uploads.s3.amazonaws.com/i/e87dtkulm42xx81vkjy2.png)<figcaption>A crashed layout in Firefox web browser</figcaption>

All we need is a `max-width` property with a value of `100` on the images.

```css
.gallery__image {
    max-width: 100%;
}
```

Refresh your browser and you'll then feel that the gallery is coming together.

![An image gallery with inline-block layout](https://dev-to-uploads.s3.amazonaws.com/i/ixqqi5udax1z2hv1pq8p.png)<figcaption>Gallery coming together</figcaption>

What's left is the styling for the `figcaption`. In our HTML file, we have a class name of `gallery__caption` attached to all the `figcaption` elements, we'll make use of this class name in our CSS.

```css
.gallery__caption {
    padding: 0.3em 0.7em;
    background-color: rgba(0,0,0,0.5);
    color: #ffffff;
}
```

The output is much better when you reload your browser.

![An image gallery with styled captions](https://dev-to-uploads.s3.amazonaws.com/i/8vocccjusvnzicnxkuiv.png)<figcaption>Image gallery</figcaption>

This will be the baseline experience for all browsers that do not support CSS Grid. If you have access to Internet Explorer the output will be the same.

In order to exclude browsers that do not support the CSS Grid, we'll make use of a feature query using the `@supports` rule. All we have to do is test if the browser supports the CSS Grid before applying the properties of CSS Grid.

```css
@supports(display: grid) { /* if browser supports grid */
    /* code for browser that support CSS Grid */
}
```

Now we can rearrange the images using CSS Grid.

##__Image layouts with CSS Grid__

<b>All code snippets from this point onwards should be inside the `@supports(display: grid) {}` unless otherwise stated.</b> 

The `auto-fill` works in conjunction with the `repeat()` function. The `repeat()` function can accept the following as its first argument:

* An integer value _or_
* The `auto-fit` keyword _or_
* The `auto-fill` keyword

The second parameter to the `repeat()` function is the `track-list` which is the size of each column. Let's look at a basic example before discussing how the `auto-fill` works.

The following snippet will create four columns in the browser because we specify `2` as the first argument and `1fr 2fr` as the second argument. This will result in `1fr 2fr 1fr 2fr` giving a total of four columns.

```css
.selector {
    display: grid;
    grid-template-columns: repeat(2, 1fr 2fr);
}
```

If we don't want to explicitly specify the width of the column (like we just did) that's when we use the `auto-fill` keyword but with another function namely `minmax()`.

The name of the `minmax()` function should give away its behavior, it accepts two comma-separated parameters. The first parameter is the lowest possible width a column can occupy. The second parameter is the highest possible width a column can occupy.

The question you might ask is: Why did we use the `minmax()` function when we can specify the width of the columns and leave the `auto-fill` to do the rest?.

The thing is when you use an `auto-fill` as the first parameter of the `repeat()` function you'll have to make use of the `minmax()` function to specify the minimum width and the maximum width that the browser will auto-fill on the web page.

I did not make that up, it's [what the specification states about auto-fill](https://www.w3.org/TR/css-grid-1/#auto-repeat):

> When auto-fill is given as the repetition number, if the grid container has a definite size or max size in the relevant axis, then the number of repetitions is the largest possible positive integer that does not cause the grid to overflow its grid container (treating each track as its max track sizing function if that is definite or as its minimum track sizing function otherwise, and taking gap into account); if any number of repetitions would overflow, then 1 repetition. Otherwise, if the grid container has a definite min size in the relevant axis, the number of repetitions is the smallest possible positive integer that fulfills that minimum requirement. Otherwise, the specified track list repeats only once.

Therefore, the following snippet is telling the browser to auto-fill the images in the grid container with the minimum width of each column set to `200px` and maximum width set to `1fr`.

```css
.selector {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
```

This is what we'll use to arrange the images in the grid. Asides that, we'll specify a `grid-gap` value to give the images room to breath. We'll also specify automatic rows as single fractional units (`1fr`) using `grid-auto-rows` and new property that we have not talked about &mdash; `grid-auto-flow` and its value `dense`.

```css
.gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-auto-rows: 1fr;
    grid-gap: 1em;
    grid-auto-flow: dense;
}
```

The `grid-auto-*` families of properties mean we are leaving some heavy lifting of the page layout to the browser. The browser will try its possible best but it's going to need our help at some point.

You'll realize in our code that we've not specified a `grid-template-rows` that will allow us to position the grid items in any row of our chosen, that's why we've used the `grid-auto-rows` that'll tell the browser to create implicit grid tracks of `1fr` to accommodate the images if the need arises for it.

The `grid-auto-flow` property is used on grid items that have not been explicitly placed on the grid (like we just did) and it accepts properties like `row`, `column` and `dense`. We are interested in the `dense` value because I made use of it in our previous code snippet.

The specification states:

>  If specified, the auto-placement algorithm uses a “dense” packing algorithm, which attempts to fill in holes earlier in the grid if smaller items come up later. This may cause items to appear out-of-order, when doing so would fill in holes left by larger items.

In simpler terms, the `dense` property is telling the algorithm to attempt to fill gaps in the grid, even if it means changing the display order of some grid items.

For now, you might not notice the effect of this property but I will show you its effect later, for now, let's proceed with the layout.

When you turn on the browsers _Developer Tools_ and inspect the grid items, you will realize that the images are not stretched to fill the grid area leaving behind some unused height.

Take a look at the image below, take note of the highlighted image and note the empty space under the _figcaption_.

![Unused height in the grid item](https://dev-to-uploads.s3.amazonaws.com/i/cedoipjsd97g3f0bwvgy.PNG)<figcaption>Unused height in the Grid</figcaption>

We can fix this with Flexbox. All we have to do is apply `display: flex` to the `<figure>` elements via the `gallery__figure` selector.

```css
.gallery__figure {
    display: flex;
}
```

When you refresh your browser, you'll realize the images now occupy the entire grid area and there are no empty spaces under the _figcaptions_ but we have another problem which is evident from the image.

![Figcaption not aligned properly](https://dev-to-uploads.s3.amazonaws.com/i/c9fb8y6kagwyy13aiuwz.PNG)<figcaption>Image captions not aligned properly</figcaption>

The `figcaption` elements are now beside the images, one quick fix for this is change the `flex-direction` to `column`.

Update your `gallery__figure` selector to match the snippet below.

```css
.gallery__figure {
    display: flex;
    flex-direction: column;
}
```

Refresh your browser and you will realize the unused height is back again but the _figcaption_ now align perfectly.

![Unused height in a grid area](https://dev-to-uploads.s3.amazonaws.com/i/9gdwd9906u0l6qk2pyaz.PNG)<figcaption>Unused height in a grid</figcaption>

The fix is simple, we just have to make each specific image stretch to fill its grid item using the `flex-grow` property. This property is available as the _first property_ of the `flex` shorthand property.

Add the following to your code and spaces should disappear.

```css
.gallery__image {
    flex: 1;
}
```

We are almost done. In our HTML we have some image with a _class attribute_ of `large-image`. We have to make these images _span  2 rows_ and _2 columns_ to make them really stand out.

```css
.gallery .large-image {
    grid-row: span 2;
    grid-column: span 2;
}
```

Save your code and refresh your browser. It's evident there are issues with the layout. They include:

* The images are stretched
* Each image width is small

![Distorted image gallery](https://dev-to-uploads.s3.amazonaws.com/i/1bdoipawmq5pv8pod53l.png)<figcaption>Image gallery</figcaption>

Let's fix the image width. 

Prior to the code in the `@supports(display: grid){}` all `figure` elements via the `gallery__figure` selector have a maximum width of `300px`, we need to change this. Update the `gallery__figure` selector to match the following.

```css
.gallery__figure {
    display: flex;
    flex-direction: column;
    max-width: initial;
}
```

Save and refresh your browser.

![An almost finished image gallery](https://dev-to-uploads.s3.amazonaws.com/i/q47p3bd1yhkgmygewg18.jpg)<figcaption>Image gallery</figcaption>

The next thing is to fix the stretched images.

<hr>

<b>The following snippet should be applied to the `gallery__image` selector outside the `@supports(display: grid){}` query.</b>

All we have to do is make the image _fit_ inside its parent container using the `object-fit` property with a value of `cover`.

Update the `gallery__image` selector outside the `supports(display: grid){}` to match the following.

```css
.gallery__image {
    max-width: 100%;
    object-fit: cover;
}
```

<hr>

Save and refresh your browser, is everything all right?. If you are using Chrome (at the time of writing), I am pretty sure everything about the layout is not fine.

The image below is a view of the layout at a reduced zoom level of `50%`. The entire layout is out of order. Did we do anything wrong? No. Do we have a bug in our code? I don't think so. Then what is the problem?

![Image gallery with a reduced zoom level in Google Chrome](https://dev-to-uploads.s3.amazonaws.com/i/tw5sd4qenld9iohdk6xq.PNG)<figcaption>Image gallery in Chrome</figcaption>

It's the way Chrome calculates the height of images in a flex container and it's classified as a _flexbug_. There are two fixes that I know of that will make Chrome comply. They are:

* Adding a width of `100` to the images
* Or using `flex` shorthand with the value `1 0 auto`, this is short for `flex-grow: 1`, `flex-shrink: 0;`, `flex-basis: auto`

Either will work, but let's use `width: 100` to keep things simple. Update the `gallery__image` selector __inside__ the `@supports(display: grid){}` to match the following:

```css
.gallery__image {
    flex: 1;
    width: 100%;    /* Fix a bug in Chrome*/
    /* flex: 1 0 auto; Another fix for the bug in Chrome */
}
```

Save and refresh your browser. The layout should work as expected in Chrome.

Here is the entire CSS code (with comments).

```css
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    background-color: #807d6c;
    font-family: "Trebuchet Ms",Verdana, Georgia, sans-serif;
    padding: 0.7em;
}

.gallery__figure {
    margin: 0;
    display: inline-block;
    max-width: 300px;
}

.gallery__image {
    max-width: 100%;
    object-fit: cover;
}

.gallery__caption {
    padding: 0.3em 0.7em;
    background-color: rgba(0,0,0,0.5);
    color: #ffffff;
}

@supports(display: grid) { /* if browser supports grid */

	/**
	 * The dense keuword used in the grid-auto-flow property
	 * is telling the algorithm to attempt to fill gaps
	 * in the grid, even if it means changing the display
	 * order of some grid items
	 *
	 * The autofill used with the repeat() function will
	 * allow the browser to place as many tracks onto
	 * the grid as it can fit, with-out violating the restrictions
	 * set by the specified size (the minmax() value).
	 */
	.gallery {
	    display: grid;
	    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
	    grid-auto-rows: 1fr;
	    grid-gap: 1em;
	    grid-auto-flow: dense;
	}

	.gallery__figure {
	    display: flex;
	    flex-direction: column;
	    max-width: initial;
	}

	.gallery__image {
	    flex: 1;
	    width: 100%; /* Fix a bug in Chrome*/
	    /*flex: 1 0 auto; Another fix for the bug in Chrome */
	}

	.gallery .large-image {
	    grid-row: span 2;
	    grid-column: span 2;
	}

}
```

One more thing!, Remember I said I would show you the effect of `grid-auto-flow: dense`? Now it's time.

Comment the `grid-auto-flow: dense` in the `gallery` selector declaration and refresh your browser. If you are on a large viewport, you'll notice no difference. Reduce your browser viewport you'll see lots of empty spaces inside the grid container because the _grid placement algorithm_ is doing its best to arrange the images at that viewport which resulted in the empty spaces.

![Absence of grid-auto-flow: dense](https://dev-to-uploads.s3.amazonaws.com/i/q6mbw6zqmrem9tg7oywr.png)<figcaption>Abscence of grid-auto-flow: dense;</figcaption>

The `grid-auto-flow: dense` is telling the browser to fill the gaps in the grid even if it means changing the display order of some grid items. Now undo the comment and refresh your browser at the reduced viewport, everything should work as expected.

![Effect of grid-auto-flow: dense](https://dev-to-uploads.s3.amazonaws.com/i/ddpt7n0ngakd0eprm01w.png)<figcaption>Effect of grid-auto-flow: dense;</figcaption>

And the Github repo:

{% github ziizium/my-webdev-notes %}

_Have fun_.