Grow your CSS skills. Land your dream job.

Seamless Responsive Photo Grid

Published by Chris Coyier

Let's say you have a bunch of images you want to display, and the goal is to get them edge-to-edge on the browser window with no gaps. Just because you think that would be cool. They are of all different sizes. You don't care if they are resized, but they should maintain their aspect ratio. Like this:


Human sacrifice, dogs and cats living together... mass hysteria!

View Demo   Download Files

Ideally we keep it pretty chill on the markup, like:

<section id="photos">
  <img src="images/dog-1.jpg" alt="Little doggie">
  <img src="images/cat-1.jpg" alt="Little kittie">
  ...
</section>

Without any CSS at all, the images will line up in a row since they are essentially inline-block:


Rivers of white.

But that's not quite what we want. Your mind might go right to some JavaScript solution. Possibly the super cool Masonry project. The problem with JavaScript is that it relies on the window.resize event which (to me at least) always makes pages feel sluggish (even if you are hip and do the unbouncing thing).

We can get just where we want to with this with just CSS. What we need are vertical columns in which to place the images. That way the images will stack on top of each other, and the height issue is moot. We could wrap equal numbers of images in floated divs, but that's ghetto not very easy to keep balanced. The trick is going to be to use Masonry CSS, where the vertical columns are made through the CSS3 property column-count. Yeah, literally, the thing where you can set text in narrow columns automatically.

So as long the parent with the multiple columns is as wide as the browser window (default) and the column-gap is 0, we got it made in the shade. By setting the images width to 100%, they will take up exactly the width of one column.

Here's the CSS:

#photos {
   /* Prevent vertical gaps */
   line-height: 0;
   
   -webkit-column-count: 5;
   -webkit-column-gap:   0px;
   -moz-column-count:    5;
   -moz-column-gap:      0px;
   column-count:         5;
   column-gap:           0px;
   
}
#photos img {
  /* Just in case there are inline attributes */
  width: 100% !important;
  height: auto !important;
}

But what about when the browser window starts getting narrow? 5 columns might be great for a very large browser window but too many for a smaller browser window (5 images side-by-side might get too narrow). We can fix the problem super easily by having media queries test the browser width and adjust the number of columns accordingly.

@media (max-width: 1200px) {
  #photos {
  -moz-column-count:    4;
  -webkit-column-count: 4;
  column-count:         4;
  }
}
@media (max-width: 1000px) {
  #photos {
  -moz-column-count:    3;
  -webkit-column-count: 3;
  column-count:         3;
  }
}
@media (max-width: 800px) {
  #photos {
  -moz-column-count:    2;
  -webkit-column-count: 2;
  column-count:         2;
  }
}
@media (max-width: 400px) {
  #photos {
  -moz-column-count:    1;
  -webkit-column-count: 1;
  column-count:         1;
  }
}

Fannnnnncy.

What is cool about this technique to me is that since it's pure CSS, everything happens very smooth and fast. You can resize your browser all around and watch things resize and move very quickly.

What about, you know.

So as far as browser support, it's pretty good, except IE which will only have it in v10. Otherwise: Firefox 3.6+, Opera 11.1+, Safari 4+, Chrome 10+. Might even be a bit deeper than that.

You might just skip a fallback, because whatever it's not that bad it just has some white gaps. Or you might use the Modernizr/YepNope combo (Modernizr can test for it as csscolumns) to load up jQuery Masonry and see what you can do with that. Or you could test for it and roll your own JS which counts the images and splits them up into equal groups and wraps floated divs around them. Whatever floats your boat.

Super cute cats and dogs!!

Totally. They are from Placedog and Placekitten. For the demo, I just alternated between them and set random heights. PHP:

<?php for ($i=0; $i < 30; $i++) {

  if ($i % 2 == 1) {

	echo "<img src='http://placedog.com/300/";
	echo rand(200,400);
	echo "' alt=''>";
	
	} else {
	
	echo "<img src='http://placekitten.com/300/";
	echo rand(200,400);
	echo "' alt=''>";			
	
	}

} ?>

View Demo   Download Files

Comments

  1. Very cool. :)

  2. Paddy
    Permalink to comment#

    Hm… any way to fix the bottom though?
    ![Bottom](http://i.imgur.com/CDnfc.png)

  3. Rohit Mehta
    Permalink to comment#

    Very well Chris. I had posted in forums how to create such grid layout a couple of days back. Thanks for creating a tut on it :)

  4. Great article! There’s definitely a “Masonry” trend going around the web, especially for images galleries. But this is a pure CSS solution.

    Good work! And Thanks!

  5. Very nice article, thanks for the tip. Interesting concept for portfolio sites.

    -Matt

  6. Permalink to comment#

    too bad css columns don’t allow for “column-min-width” and “column-max-width.” that’d be “made in the shade” with no media queries.

  7. Nice article. Pretty nifty idea. This will certainly inspire my next image wall.

    Have to correct on compatibility though. At least in my Firefox 3.6.18 it does not work. Images are scaled 100% of the browser window. Seems as if the columns are not used.
    I will investigate later.

    Thx for the article

    Sascha

  8. Cool Chris, thanks for this tut. Was discussing this exact format with some development guys the other day, they wanted to minimize any jquery etc for a design they are converting to cms so if we still need to go down this route I will definitely show them this all CSS technique.

  9. What does the ‘rand(200, 400)’ do in the php? It doesn’t seem to be a height, but part of the image link.

    • original code:

      echo "<img src='http:⁄⁄placedog.com/300/";
      echo rand(200,400);
      echo "' alt=''>";

      prints out this:

      <img src='http://placedog.com/300/258" alt=''>"

      rand(200,400) is giving a random number between 200 and 400 and printing(echoing) it in the height portion of the url.

    • It is part of the URL. The service (placedog) takes arguments from the url and returns dog pictures with the dimensions given.

  10. Permalink to comment#

    Great stuff!

  11. Hey Chris! I wonder why you’ve stopped using rel="(CSS, HTML, PHP etc)"

    • I was just lazy, I fixed these.

    • Thanks! Although, then it wouldn’t be necessary, for example, to include ‘PHP:’ before the code and then PHP being written inside the ‘code-box’ again. I suggested using rel again because I thought then you’d not have to say ‘PHP:’ or ‘Here’s the CSS:’ before the code-box.

      And, if the above comment sounds rude, then please don’t mind it, I apologize in advance =)

  12. Hi Chris, nice article and a very funky technique.

    If I’m not mistaken, there’s a small typo inside your media queries.—shouldn’t #people be #photos?

  13. Wow this is so straight forward and useful. Cheers.

  14. Permalink to comment#

    Cool. And how nice would that be as plugin for WordPress: An Overview of every pic ever published, linked to its post… ;o)

  15. Permalink to comment#

    How about the idea of a wordpress template that would post thumbnailed images like this at full-screen as short cuts to blog posts? Yet one box would remain in a fixed location as a logo?

    • That’s actually a great idea. I can see this being very useful, especially for portfolio type WordPress sites. There could text that appears when an image is hovered to describe the post briefly as well. So many different things could be done with this.

    • Permalink to comment#

      I agree Darryl. My main idea on this is an easy place to mix pix messages (via phone), as well as other photos, designs, graphics and posts. I’ll see what I can do about making one. Not sure how I’m going to get the functions to loop yet keep a single box in place.

    • Permalink to comment#

      Great idea…got the gears turning on that one!

  16. adam
    Permalink to comment#

    Your solution is so close in IE7…great tutorial! Thanks.

  17. Thats really nice!

  18. pretty cool.
    i’ve been using this technique on one of my lifestyle websites:

    http://iliketheweekend.com/

    one thing to note is on the img style, if you add any positioning such as relative or whatever, safari has a weird bug which will break the css3 column layout.

  19. AWSUM!! Wall to wall kittens by power of pure CSS. The day just can’t get better! ;-) Good work.

  20. Neil Monroe
    Permalink to comment#

    Another idea for non-CSS fallback would be to use the attribute ‘align=”absmiddle”‘ on the img tags. This would line up the image centers horizontally.

  21. Nice CSS only Solution.

  22. Dhaval Pathak
    Permalink to comment#

    Nice. It’s useful CSS .

  23. Permalink to comment#

    Very Nice! I will have to give this one a try, much nicer (and cleaner) than one (not by you) I have used in the past. Thanks!

  24. Hey, Chris!

    That’s an excellent use for the “Masonry” style! And to see you use my column-count technique to do it is about the greatest confirmation that it was a good idea that I could have hoped for.

    Cheers and thanks!

    P.S.: +1 on the “make this a WordPress plugin” idea.

  25. Mario
    Permalink to comment#

    Great Article, gotta love the Ghost Busters Quote put in there.

  26. Nifty. It’s nice to see CSS techniques getting applied to solve problems that aren’t always obvious.

    I’m a big fan of keeping HTML markup super simple, and CSS3 allows us to do some fantastic stuff without cluttering the markup.

    Keep up the good work!

  27. Wow! I really love this site. You all offer some good information. I love your use of kittens too. I am a Tucson web designer, so this is all super useful for me. Keep up the good work. I’ll be checking in regularly to get more tips and hints. Thanks.

  28. Permalink to comment#

    That’s a really nice solution to an age-old problem. Very useful.

  29. Dan Wilt
    Permalink to comment#

    Instead of doing CSS3, couldn’t you have left them inline-block and done vertical-align top? And you could’ve removed the white space between them by setting font-size to 0?

  30. Ricky
    Permalink to comment#

    I don’t want to be beating a dead horse, but the download still has the mistake of the section id=’people’ instead of section id=’photos’.

    Great tut! I have been looking for this for a long time! And pure CSS!

  31. Permalink to comment#

    There’s a placedog.com? awesome!

  32. Hey Chris, love this idea.

    I’m trying to implement a version of this technique on my site but I think I’m running into a few issues with how webkit handles columns?

    I have 6 images / 6 columns at high resolutions and 6 images / 3 columns at lower resolutions. It behaves as expected in Firefox but not in Chrome or Safari. When you return to the higher resolutions it remains at 3 columns?

    If anyone could take a quick look it would be greatly appreciated – http://jsfiddle.net/Pa7mU/2/

    Thanks

    • The same happened to me. It’s webkit issue, it handles css3 stuff differently usually. I’m trying to find a solution to make it look better, but ineffectually yet… Have you solved this problem?

      As far as I saw, some kind of bug also appears, if you declare line-height, and works pretty fine without this. Sort of.

    • @Janos – I basically gave up trying to find a fix for this using just CSS and ended up using jQuery Masonry instead.

  33. Permalink to comment#

    Great stuff!

    I’m having a pesky problem with adding some captions though, and I’m wondering if anyone can help.

    Here’s the test page: http://souchi.com/pages/celebrity3 As you can see, the text that ends up at the bottom of the longest column won’t stay at the bottom of that column. It wants to wrap to the top of the next column.

    My html look like this:

    
    &ltdiv id="photos">
    
      &ltdiv>  
        &ltimg src="example.jpg" />
        &ltp>Test tile lorem ipsum. &lt/p>
     &lt/div>
    
    
    &lt/div>

    and my CSS looks like this:

    
    #photos {
        width: 925px;
        line-height: 0px;
       -webkit-column-count: 6;
       -webkit-column-gap:   1px;
       -moz-column-count:    6;
       -moz-column-gap:      1px;
       column-count:         6;
       column-gap:           1px;
    }
    #photos div {
      width: 150px !important;
      height: auto !important;
         margin-bottom: 10px;
    }
    #photos div p{
        line-height: 16px;
    }
    

    Anyone know the solution for getting that caption to stick with the image? Cheers.

  34. Permalink to comment#

    Hey Chris, thanks for the genius-ly simple code, took me hours and hours to make work on iphone though; i finally discovered the head must contain meta name=”viewport” content=”width=device-width,initial-scale=1.0″ otherwise the phone sticks think its resolution is in between 800px and 1000px (3 columns, 2 images wide). your code worked on http://iphonetester.com/ and i could re-size my laptop chrome screen and watch it downsize nicely but iphone it was broken… so I think you should add the all important viewport line to your download :)

  35. Permalink to comment#

    a question, can you make the images load across the rows instead of down the columns? In a five column screen i want the story to go left to right down through the rows…

Leave a Comment

Current day month ye@r *

*May or may not contain any actual "CSS" or "Tricks".