Originally posted on my blog
CSS grid is awesome, we can't say it enough. It helps a lot in making our website responsive. Sometimes we even don't need media queries to make our layout fully responsive. In this post, I will lead you on how to build a responsive image gallery with CSS grid (only) and some JavaSript (to make it reactive), and the images will be fetched from the Unsplash API. So you'll need to create an account here to get your API key.
HTML
The HTML is relatively simple as you can see.
<main>
<div class="image--container"></div>
<div class="image--selection"></div>
</main>
We have two div elements, the first with image--container
class will be the viewer and the second div
tag will enable us to select an image from the gallery. And by the way img tags will be created by JavaScript.
CSS
For the CSS, we will first make some resets to remove spaces and import Open Sans
from google fonts.
@import url('https://fonts.googleapis.com/css?family=Open+Sans:700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #444;
}
main {
padding: 0 0.5rem;
height: 100vh;
}
Then, change the background-color
for the body
tag and set the height of the main
tag to fit the viewport height.
.image--gallery {
width: 100%;
height: 32rem;
display: block;
margin: auto;
border: 5px solid #222;
object-fit: cover;
}
.image--selection {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
justify-content: center;
align-items: center;
}
.image__item {
width: 100%;
height: 8rem;
display: block;
margin: auto;
margin-bottom: 0.5rem;
border: 5px solid #222;
object-fit: cover;
cursor: pointer;
}
To make the image viewer looking nice, we have to use .image--gallery
to expand the width to 100% to have a responsive image and the most important set the object-fit
property to cover
. It will just ensure that the image fit its container element.
Then, for the image gallery, we use .image--selection
to have a grid system. After displaying it in grid, we have to set grid-template-columns
to repeat(auto-fit, minmax(9rem, 1fr))
.
The repeat()
function takes two arguments: the number of times to repeat the value and the value itself. The auto-fit
value allows us to wrap our columns into rows when we lack spaces to display every elements in the same column in our viewport. And the minmax()
function will set the minimum size of the element to 9rem
and the maximum size to 1fr
.
justify-content
and align-items
help us centering the grid elements.
Finally, the .image__item
class serves to resize each image in the gallery.
rem
is a relative unit of the font size(16px).
fr
is just a fractional unit.
.loader {
text-align: center;
margin-top: 20%;
color: #fff;
font-family: 'Open Sans', sans-serif;
font-weight: 700;
}
.animate-entrance {
animation: BounceIn 0.8s;
}
@keyframes BounceIn {
0% {
transform: scale(0.8);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
As you can see here, we use .loader
class to style the alignment, font, margin and color of the text to display if we have no images.
Then, .animate-entrance
class helps us making some animations on the image viewer. For that, we use the animation property and the BounceIn
value defined below with @keyframes
. It will just bounce in with a fade in effect and scale a little bit. And the animation will last 0.8 seconds.
JavaScript
We start the javaScript part by selecting some elements.
const image_gallery = document.querySelector('.image--container');
const image_container = document.querySelector('.image--selection');
const loading = '<h1 class="loader">Loading...</h1>';
The first image_gallery
is the viewer element, and the second image_container
is the image gallery. Both elements are selected by their class names with the querySelector
method.
The last one loading
will be used if we have no images to show.
const showImages = () => {
// Show loading text if no data
if (image_container.children.length === 0)
image_container.innerHTML = loading;
fetch(
`https://api.unsplash.com/photos?client_id=${apiKey}`
)
.then(res => {
res.json().then(images => {
// Call the function to create the gallery
createImageGallery(images);
});
})
.catch(e => {
console.log(e);
});
};
The showImages()
function will display images fetched from the unsplash API. First, we need to check if the image_container
have a child element or not, in other words if the image gallery is created in the DOM or not and display Loading...
while fetching data from unsplash with the fetch API. Then, if we get images pass it to the createImageGallery()
function as an argument to display images otherwise catch the error.
For the apiKey
in the url, you need to create an account here and replace it with your API key.
const createImageGallery = images => {
let output = '';
// Show the first image on the viewer
image_gallery.innerHTML = `<img src="${images[0].urls.regular}" class="animate-entrance image--gallery" alt="${images[0].alt_description}">`;
setTimeout(() => {
image_gallery.children[0].classList.remove('animate-entrance');
}, 500);
// show unselected images
images.forEach(({ urls, alt_description }) => {
output += `<img src="${urls.regular}" alt="${alt_description}" class="image__item" />`;
});
image_container.innerHTML = output;
};
To create the viewer in the DOM, we use the property innerHTML to append the element to image_gallery
. Then, we remove animate-entrance
class after 0.5 seconds to be able to animate the viewer again.
And we loop through images
(data fetched from unsplash) and just pull out urls
and alt_description
to create the img tag and append it to image_container
to create the image gallery's selection on the DOM.
const changeImage = e => {
// Get the viewer image element
const image = image_gallery.children[0];
if (e.target.src) {
// change the image
image.classList.add('animate-entrance');
image.src = e.target.src;
setTimeout(() => {
image.classList.remove('animate-entrance');
}, 800);
}
};
// Event listeners
document.addEventListener('DOMContentLoaded', showImages);
image_container.addEventListener('click', changeImage);
To change the image on the viewer, we need to get the child element(img tag) of image_gallery
. Then, check if the event provided as an argument contains an src attribute of not. And then change the viewer image with the selected one and add the .animate-entrance
class to the img tag. Then, we remove animate-entrance
class after 0.5 seconds to be able to animate the viewer again.
And to make it happen, we need to listen to two events. The first DOMContentLoaded
will fire the showImages()
function to display all images when the initial HTML document will be completely loaded.
The second listen for the click event on the image_container
element and change the viewer image with the function changeImage()
.
You can check it live here
That's all folks!!!