Build an Animated Photo Wall With CSS

Today we’re going to embark on the adventure of building yet another awesome and fun CSS demo. This time we’ll create a big, seamless wall of photos. When the user hovers over an image, a transparent black overlay will fade it out as a text label fades in and the image zooms.

The result is pretty slick and I’ve also thrown in a bonus second version for those that make it all the way through the tutorial. Read on to get started!

2 Million+ Digital Assets, With Unlimited Downloads

Get unlimited downloads of 2 million+ design resources, themes, templates, photos, graphics and more. Envato Elements starts at $16 per month, and is the best creative subscription we've ever seen.

Explore Design Resources

Sneak Peek

If you’re interested in seeing where we’re going, you can have a quick look at the final result by clicking the link below.

Demo: Click Here to launch.

The HTML

The first thing that we need to do is create the general structure for our page and toss in the content. All we really need is a single container div and an unordered list.

<div class="container">
  <ul>
    <li></li>
    <li></li>
    <li></li>
  </ul>
</div>

Now it’s time to put something in the list items. We start with a paragraph because our ultimate goal is to have some text appear on hover. Next, we toss in an image.

<div class="container">
  <ul>
    <li><p></p><img src=""></li>
    <li><p></p><img src=""></li>
    <li><p></p><img src=""></li>
  </ul>
</div>

Now that we have a feel for the structure, let’s drop in some placeholder images and text (feel free to use whatever you like, just make sure your image dimensions are 320×213).

<div class="container">
  <ul>
     <li><p>big</p><img src="http://lorempixum.com/320/213/transport/1"></li>
     <li><p>small</p><img src="http://lorempixum.com/320/213/transport/2"></li>
     <li><p>fast</p><img src="http://lorempixum.com/320/213/transport/3"></li>
     <li><p>slow</p><img src="http://lorempixum.com/320/213/transport/4"></li>
     <li><p>awesome</p><img src="http://lorempixum.com/320/213/transport/5"></li>
     <li><p>convenient</p><img src="http://lorempixum.com/320/213/transport/6"></li>
     <li><p>necessary</p><img src="http://lorempixum.com/320/213/transport/7"></li>
     <li><p>fun</p><img src="http://lorempixum.com/320/213/transport/8"></li>
     <li><p>boring</p><img src="http://lorempixum.com/320/213/transport/9"></li>
     <li><p>crazy</p><img src="http://lorempixum.com/320/213/transport/10"></li>
     <li><p>boring</p><img src="http://lorempixum.com/320/213/transport/1"></li>
     <li><p>small</p><img src="http://lorempixum.com/320/213/transport/2"></li>
     <li><p>fast</p><img src="http://lorempixum.com/320/213/transport/3"></li>
     <li><p>slow</p><img src="http://lorempixum.com/320/213/transport/4"></li>
     <li><p>awesome</p><img src="http://lorempixum.com/320/213/transport/5"></li>
     <li><p>convenient</p><img src="http://lorempixum.com/320/213/transport/6"></li>
     <li><p>necessary</p><img src="http://lorempixum.com/320/213/transport/7"></li>
     <li><p>fun</p><img src="http://lorempixum.com/320/213/transport/8"></li>
     <li><p>boring</p><img src="http://lorempixum.com/320/213/transport/9"></li>
     <li><p>crazy</p><img src="http://lorempixum.com/320/213/transport/10"></li>
  </ul>
 </div>

As you can see, we’re using a ton of images, but it’s necessary if you want to pull off the photo wall effect.

Container Styles

Now that we’ve created a giant mess of content in our HTML, it’s time to start prettying it up with CSS. The first step in this process is to center the container. When all is said and done, our images will actually be 280px wide. Allowing for four across, this gives us a width of 1120px. Adding auto margins will center everything up nicely.

/*------CONTAINER------*/
.container {
  width: 1120px;
  margin: 0 auto 50px auto;
}

Media Queries

I’m not going to go full on responsive with this project, but it would be nice if it adjusted half way decently to different viewport sizes. This merely involves resetting our container width at different intervals, which effectively drops the layout from four columns down to three, two and finally one when the viewport is narrow enough.

/*------MEDIA QUERIES------*/
@media screen and (max-width: 1120px) {
  .container {width: 840px;}
}

@media screen and (max-width: 840px) {
  .container {width: 560px;}
}

@media screen and (max-width: 560px) {
  .container {width: 280px;}
}

List Styles

Next, let’s attack the basic list styles. We have three areas to cover: the unordered list, the list items and the list item hover state.

The ul selector simply needs the list style cleared so that we don’t have any bullet points or weird indenting troubles. The li elements should be floated left and given a position of relative, the latter of which will give us a starting point for the absolutely positioned text that we’ll be using later. Our list items also require both a height and a width, for a reason that we’ll see later. Finally, make sure you set the overflow to hidden.

We didn’t set up the images to be links (you can if you want) but I do want a little bit of cursor feedback on hover so I changed the cursor value to pointer (the little hand).

/*------LIST------*/
.container ul {
  list-style-type: none;
}

.container li {
  float: left;
  position: relative;
  width: 280px;
  height: 187px;
  overflow: hidden;
}

.container li:hover {
  cursor: pointer;
}

Paragraph Styles

Before we jump into styling the images, let’s start by getting our text all straightened out. There’s a lot to do here so browse through it quickly and I’ll explain what’s going on after.

/*------PARAGRAPH------*/
.container li p {
  color: transparent;
  background: transparent;
  font: 200 30px/187px 'Arvo', Helvetica, Arial, sans-serif;
  text-align: center;
  text-transform: uppercase;
  
  position: absolute;
  top: 0;
  left: 0;
  width: 280px;
  height: 187px; 


  -webkit-transition: all 1s ease;
  -moz-transition: all 1s ease;
  -o-transition: all 1s ease;
  -ms-transition: all 1s ease;
  transition: all 1s ease;
}

.container li:hover p {
  color: white;
  background: #000; /*fallback for old browsers*/
  background: rgba(0,0,0,0.7);
}

Paragraph Appearance

The first section targets the list item paragraphs. I’ve divided this up into three distinct chunks for viewing convenience. The first chunk governs the general look of the text. I used Arvo for the font, aligned the text to the center and transformed it to uppercase.

The odd part that you may have noticed is that the text color and background color are both set to transparent. This allows us to hide the paragraphs by default and later we can fade them in on hover.

Paragraph Position

The next chunk of code sets the position of our paragraphs to absolute. If we don’t do this, the text will flow alongside or above our images rather than occupying the same space, which is what we want. This removes the text from the normal flow and our relative positioning that we set earlier on the list items gives the position values a boundary that they won’t cross.

I also set the height and width of the paragraph. This is another thing that seems weird, but the effect that I want to achieve requires a black overlay to appear on top of the image on hover. The text background provides and convenient and easy way to accomplish this.

Paragraph Transition

The third chunk of code sets up our transition. This is pretty straightforward, I set it to transition all properties in a second with an ease timing function.

Paragraph Hover

The final piece of this puzzle is setting the paragraph hover styles. When the user hovers, we want the text to become white and the background to darken. Through the use of rgba, we can achieve a transparent black look. Just keep in mind that older versions of IE require the fallback black background color because they don’t support rgba.

Image Styles

Our goal with the images is to create a zooming in effect so that when the user hovers, the image will get bigger while the text and overlay fades in. The trick is that we don’t want this to effect the layout at all, which is why we set a specific height and width on our list items with hidden overflow. Now we can enlarge the size of the image and have it stay within the bounds of the original size, thereby simulating a zoom. A neat trick!

To accomplish this, set the images to the list item size (280×176) and apply a transition. Then, when the user hovers, return the images to their actual physical size: 320×213.

/*------IMAGES------*/
.container img {
  width: 280px;
  height: 187px;
  
  -webkit-transition: all 1s ease;
  -moz-transition: all 1s ease;
  -o-transition: all 1s ease;
  -ms-transition: all 1s ease;
  transition: all 1s ease;
}

.container li:hover img {
  width: 320px;
  height: 213px;
}

See It In Action

With that, we’re all finished! Have a look at the live demo by clicking the link below.

Demo: Click Here to launch.

screenshot

Alternate Version

If you checked out the demo above, you may have noticed that there are two versions. The first is called “fade” and that’s the one we just set up. The second is called “flip” and has a different effect applied to it to give you a feel for the wide variety of results that you can achieve with this setup.

For the most part, the flip CSS is the same. The main differences are that I’ve removed the transition from the paragraph and changed the fade effect to a CSS transform that collapses the image’s width and expands it in the opposite direction. The result is a lazy man’s flip effect.

/*------PARAGRAPH------*/
.container li p {
  color: transparent;
  background: transparent;
  font: 200 30px/187px 'Arvo', Helvetica, Arial, sans-serif;
  text-align: center;
  text-transform: uppercase;
  
  position: absolute;
  top: 0;
  left: 0;
  width: 280px;
  height: 187px; 
}

.container li:hover p {
  color: white;
  background: rgba(0,0,0,0.7);
  z-index: 1;
}

/*------IMAGES------*/
.container img {
  width: 280px;
  height: 187px;
  -webkit-transition: all 0.7s ease;
  -moz-transition: all 0.7s ease;
  -o-transition: all 0.7s ease;
  -ms-transition: all 0.7s ease;
  transition: all 0.7s ease;
}

.container li:hover img {
  -webkit-transform: scale(-1, 1);
  -moz-transform: scale(-1, 1);
  -o-transform: scale(-1, 1);
  z-index: -3;
}

Conclusion

I hope you have as much fun building this little experiment as I did. It should provide you with some good practice for things like pulling off a zoom or a flip with CSS.

If you enjoyed the tutorial, leave a comment below and let us know. Also be sure to add how you would change or improve it!