In this article, I'll explain how to create a gallery of photos with text overlays using modern CSS and HTML, as seen in the Code Sandbox below.
Background
As I was building out a test photo gallery, app I realized that I wanted my main page to be a grid of square images. The pictures were supposed to be the star of the show, but I wanted the viewer to be able to see a description of each image when they hovered over it. To spice it up a little, I decided to add a "zoom in" effect on hover.
My goal was to use modern CSS and HTML only, if I could. I decided to start with CSS Grid as a base.
Step One - The Grid
The first step was building out the grid itself. To start with, I created a container div and a few tiles:
<div class="grid-container">
<div class="grid-tile"></div>
<div class="grid-tile"></div>
<div class="grid-tile"></div>
<div class="grid-tile"></div>
<div class="grid-tile"></div>
<div class="grid-tile"></div>
<div class="grid-tile"></div>
<div class="grid-tile"></div>
</div>
The grid-container
will hold all our grid-tiles
. The grid-tiles
will then hold the images and the overlays.
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
}
.grid-tile {
background-color: rgba(0, 0, 0, 0.1);
aspect-ratio: 1;
}
Here, we set the grid-container
to display as a grid with a 20px gap between its rows and columns. We also define the width of the columns of the grid in grid-template-columns
. With repeat(auto-fit, minmax(150px, 1fr))
, we declare that we want to create columns that are at least 150px and will otherwise evenly grow to fill the available width of the container (minmax(150px, 1fr)
). The auto-fit
keyword will only create as many columns as we need - if we only have two child elements, only two columns will be created.
For a great auto-fit / auto-fill breakdown, check out (Auto-Sizing Columns in CSS Grid)[https://css-tricks.com/auto-sizing-columns-css-grid-auto-fill-vs-auto-fit/]
The critical part of the grid-tile
style is aspect-ratio: 1
. This tells the browser that we want square tiles. aspect-ratio
accepts whole numbers or fractions - without units - in the format <width> / <height>
.
Note
aspect-ratio
is supported in the most recent version of all major browsers as of this writing, but support does not include Internet Explorer or Safari before version 15. You can replicate some of this withoutaspect-ratio
by settinggrid-template-columns: repeat(auto-fit, 150px);
andgrid-auto-rows: 150px;
in thegrid-container
class. You lose flexibility, but maintain the fixed aspect ratio.
With these rules in place we should see something like this:
Step Two - The Overlay
Now that we have the grid itself, we can add a text overlay.
We'll add the following code inside each of our grid-tile
divs:
<div class="tile-overlay tile-overlay-bottom">
<p class="tile-overlay-text">Hello, World!</p>
</div>
The a tile-overlay
div will contain an elements that we want to appear on hover over the tile. The tile-overlay-bottom
styles will ensure the element appears on the bottom of the tile. We'll style tile-overlay-text
for readability on a darker background.
We need the tile to have position: relative
so we can place its children absolutely, otherwise, the children will be positioned relative to the window itself, which isn't what we want.
.grid-tile {
background-color: rgba(0, 0, 0, 0.1);
aspect-ratio: 1;
position: relative;
}
.tile-overlay {
position: absolute;
background-color: rgba(0, 0, 0, 0.75);
}
.tile-overlay-bottom {
bottom: 0;
width: 100%;
}
.tile-overlay-text {
margin: 0;
color: white;
padding: 10px;
}
As a challenge to yourself, try to implement an overlay element that will appear in the top-right of each tile.
At the end of Step Two, you'll have something like this:
Step Three - Overlay Hover Effect
So far, we have a grid of tiles and we have an overlay. That's great, but the overlay is there all the time and I promised you hover effects.
To get us there, we'll need to add a couple lines to our CSS:
.tile-overlay {
position: absolute;
background-color: rgba(0, 0, 0, 0.75);
opacity: 0%;
}
.grid-tile:hover .tile-overlay {
opacity: 100%;
}
With these few lines of code, we're giving everything with the tile-overlay
class an opacity of 0%, which means the element will still take up its usual space, but will not be visible.
The next rule says, "Every time a viewer hover
s over a grid-tile
, I want the opacity
of the tile-overlay
s to go to 100% (be completely visible)."
And with that, we have one of our hover effects!
For a smoother
transition
between opacity 0% and opacity 100%, you can insert atransition
property into the.tile-overlay
rule:
transition: opacity 0.33s;
This will extend the length of the transition from 0% to 100% opacity from instant to a third of a second.
The end of Step Three should look like this:
Step Four - Adding Images
So far, we've built the cards and an overlay, but our photo gallery isn't any good if there aren't any photos! Start off by adding img
tags to your grid-tile
divs. When you're done, the grid-tile
html should look something like this:
<div class="grid-tile">
<img
class="tile-image"
src="https://source.unsplash.com/random/200x250"
/>
<div class="tile-overlay tile-overlay-bottom">
<p class="tile-overlay-text">Hello, World!</p>
</div>
</div>
If you check out your page now, you'll see that the images are overflowing the tiles, so we're going to need to turn to CSS for help.
.tile-image {
width: 100%;
height: 100%;
object-fit: cover;
}
This new CSS rule will constrain our images to the height and width of their parent - in this case, the grid-tile
. A value of cover
for the object-fit
property means that the image will maintain its aspect ratio, but anything outside of the parent element will be clipped. This keeps our images from being distorted, but means we lose some of the image content.
One last thing to note for this phase. You may have noticed that our tiles are no longer perfect squares, depending on your browser. There may be a small strip of gray background below the image in each tile. I believe this has something to do with the image distorting the grid, but I haven't been able to figure it out yet. If you know why this happens, please leave a comment!
We can fix that strip pretty easily by adding
overflow: hidden
to thegrid-tile
rule. This will also be important for the next step, so let's go ahead and do it now:
.grid-tile {
background-color: rgba(0, 0, 0, 0.1);
aspect-ratio: 1;
position: relative;
overflow: hidden;
}
By the end of Step Four, you'll have something like this:
Step Five - The Image Zoom Hover Effect
What we have is great so far, but let's jazz it up a little. We want it to look like the image is coming slightly closer to you when you hover over it.
First, we'll add some styles to the image to provide a baseline for what we'll do on hover:
.tile-image {
position: relative;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: 0.33s;
}
These styles give us a baseline for our transition on hover. They tell the browser that we're consciously placing our image exactly where it normally goes in the flow of the document. Note also the height and width. Those will change when we hover over the grid-tile
.
.grid-tile:hover .tile-image {
top: -5px;
left: -5px;
width: calc(100% + 10px);
height: calc(100% + 10px);
}
With this, when a visitor hovers over the grid-tile
, the image will grow 10px in width and height (the calc
statement makes sure of that!), at the same time, we're shifting the image 5 pixels up and to the left. This makes sure that the effect we're going for is centered in our tile. Without the position change, it would look like the image is just growing from the upper left.
You might ask, "But if the image is growing, why isn't the tile growing too, and changing the layout!?" This is where the overflow: hidden;
rule from the last step comes in. Since the overflow is set to hidden, any change in the image size will be clipped!
This leads us back to where we started out:
If you liked this, or have questions, let me know!
Top comments (0)