Check the gallery online: https://ziizium.github.io/my-webdev-notes/photo-gallery/
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.
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.
<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.
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.
* {
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.
.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.
All we need is a max-width
property with a value of 100
on the images.
.gallery__image {
max-width: 100%;
}
Refresh your browser and you'll then feel that the gallery is coming together.
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.
.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.
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.
@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
All code snippets from this point onwards should be inside the @supports(display: grid) {}
unless otherwise stated.
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.
.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:
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
.
.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 — grid-auto-flow
and its value dense
.
.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.
We can fix this with Flexbox. All we have to do is apply display: flex
to the <figure>
elements via the gallery__figure
selector.
.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.
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.
.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.
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.
.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.
.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
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.
.gallery__figure {
display: flex;
flex-direction: column;
max-width: initial;
}
Save and refresh your browser.
The next thing is to fix the stretched images.
The following snippet should be applied to the gallery__image
selector outside the @supports(display: grid){}
query.
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.
.gallery__image {
max-width: 100%;
object-fit: cover;
}
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?
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 value1 0 auto
, this is short forflex-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:
.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).
* {
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.
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.
And the Github repo:
ziizium / my-webdev-notes
Code snippets for series of articles on DEV about my experiments in web development
My WebDev Notes
This repositiory contains code snippets, and links for series of articles on DEV about my experiments in Web development.
List of articles
- My WebDev Notes: CSS Loaders published on the 25th February 2020
- My WebDev Notes: Filter table published on the 1st April 2020
- MyWebDev Notes: Center page elements with CSS Grid published on the 3rd of April 2020
- My WebDev Notes: Photo gallery with CSS Grid published on the 7th of April 2020
- My WebDev Notes: Fullscreen overlay navigation published on the 13th of April 2020
- My WebDev Notes: A simple and accessible accordion published on 28th of April 2020
- My WebDev Notes: How to create a tooltip with HTML and CSS published on 3rd February 2021
- How to create a modal published on 22nd June 2021
Have fun.
Top comments (0)