HTML & CSS
Article

Using Modern CSS to Build a Responsive Image Grid

By George Martsoukos

Building custom responsive layouts is always exciting. This article examines a technique that we can use to take full control of the distance between grid columns. To demonstrate this, I’ll use the example of a responsive image gallery.

Building a Responsive Layout

To begin with, let’s assume on large screens our gallery should look something like this:

Required layout for large screens

On smaller screens (i.e. <50em), it should have the following appearance:

Required layout for small screens

The markup is simple:

<div>
  <a href="path-to-the-image">
    <figure>
      <img src="path-to-the-image" alt="">
    </figure>
  </a>

  <!-- other anchors here ... -->

</div>

As you probably already know, we can take advantage of different layout methods for generating the desired result. Before I examine two of those possible methods, let’s take note of our initial requirements (see previous visualizations):

  • I want a 2-column layout on medium screens and smaller (i.e. <50em), and a 4-column layout on large screens (50em or above).
  • The distance between columns should be 8px.

Using inline-block

I’ll first use the display:inline-block method to build our gallery. Consider the following CSS:

div {
  font-size: 0;
}

a {
  font-size: 16px; 
  display: inline-block;
  margin-bottom: 8px;
  width: calc(50% - 4px);
  margin-right: 8px;
}

a:nth-of-type(2n) {
  margin-right: 0;
}

@media screen and (min-width: 50em) {
  a {
    width: calc(25% - 6px);
  }

  a:nth-of-type(2n) {
    margin-right: 8px;
  }

  a:nth-of-type(4n) {
    margin-right: 0;
  }
}

Here’s an explanation of what I’ve done:

By default, there’s a space between inline-block elements. One way to override this is to set the font size of the parent element to zero. This might also require that we reset the font size of any child elements (not necessary in our case).

On small screens I have a 2-column layout and I specify 8px of space between columns.

The width of our columns is calculated as follows:

  • Total space between columns per row = 1 * 8px => 8px. The derived value is 8px and not 16px because I remove the right margin for every second column on small screens.
  • The width of each column = calc(50% – 4px). The value 4px derived by calculating: Total space between columns per row / Number of columns per row (8px / 2 => 4px).

2 column layout

On large screens I have a 4-column layout and I specify 8px space between columns. So, the width of our columns is calculated as follows:

  • Total space between columns per row = 3 * 8px => 24px. Again, the derived value is 24px and not 32px because I remove the right margin for every fourth column.
  • For column widths, I’m using calc(25% – 6px). The value 6px derived by making this simple calculation: Total space between columns per row / Number of columns per row (24px / 4 => 6px).

4 column layout

See the CodePen demo for the inline-block method here

Using Flexbox

The solution above works pretty well, but there are some disadvantages. To demonstrate one disadvantage, let’s assume that each image contains a caption as well.

Consider this slightly updated markup:

<div>
  <a href="path-to-the-image">
    <figure>
      <img src="path-to-the-image" alt="">
      <figcaption>Some text here</figcaption>
    </figure>
  </a>

  <!-- other anchors here ... -->

</div>

Here’s the new version of our gallery on large screens:

Large screen layout with captions

View the CodePen demo using inline-block with captions

The result isn’t so attractive because the grid items have different heights. We can fix this with flexbox, a modern layout method, which will allow us to overcome many common layout problems (e.g. default gap between inline-block elements). In order to activate this method, I simply have to update the CSS of the parent element (the flex container):

div {
  display: flex;
  flex-wrap: wrap;
}

Happily enough, if we now preview the gallery, we’ll get the expected result (i.e. equal height columns) across all screens. Here’s how it looks on large screens:

Large screen layout with captions, improved

View the CodePen demo using flexbox with improved captions

At this point, there’s one last thing I have to clarify. Flexbox provides the justify-content property that aligns flex items along the main axis of the current line of the flex container. However, note that this property doesn’t define a value that will allow us to build the desired gallery layout. For example, the space-between value results in this layout:

Grid with space-between value

While the space-around property results in this one:

Grid with space-around value

In both cases, the last two items are distributed awkwardly on the final row. The CSS for this method would look like this:

div {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between; /* or space-around */
} 

a {
  display: inline-block;
  margin-bottom: 8px;
  width: calc(50% - 4px);
}

@media screen and (min-width: 50em) {
  a {
    width: calc(25% - 6px);
  }
}

In this case I didn’t assign a margin-right property to the flex items. This happens because, depending on the value of the justify-content property, the browser takes care of distributing the flex items across their container.

View the CodePen demo using the justify-content property

Conclusion

This post has covered a couple of techniques for controlling the distance between grid columns in a responsive image grid. While the inline-block method is sufficient, flexbox makes it much easier and more convenient, especially when combined with the calc() function.

If you have another solution to this problem, feel free to mention it in the comments.

  • http://georgemarts.com/ George Martsoukos

    Try to remove this rule to see the difference. First, all links have margin-right: 8px. Then, on small screens we remove this margin for every second link. On large screens though, we remove this margin for every fourth link. But then, we have to reset the margin-right property (override the a:nth-of-type(2n) { margin-right: 0;} rule) for every second link. Remember that’s because we use a mobile-first approach.

    • http://n-or.de michael

      … and then you put ‘margin-right: 0px’ on every 4n a. Understand it now. Thanks. ;)

  • http://georgemarts.com/ George Martsoukos

    Can you please explain why this option doesn’t work for this demo? Also, which unit do you suggest for media queries?

  • Paul O’B

    Note that the inline-block example can be fixed with a simple vertical-align:top. When you make an element inline-block its default vertical-alignment is ‘baseline’ which is why you see the blocks misaligned. Just add vertical-align:top and they will line up nicely.

    • http://georgemarts.com/ George Martsoukos

      Very nice, thanks! The only drawback is that they don’t have equal heights.

      • Paul O’B

        Yes of course but in your demo that won’t notice as there are no backgrounds to match so it won’t really matter.

        If equal height backgrounds are needed then flexbox would be the choice perhaps with a fallback to inline-block (with modernizr or similar perhaps).

        • http://georgemarts.com/ George Martsoukos

          Indeed. I was just thinking the same thing! As long as there’s no need for setting backgrounds to the target elements, the display inline-block method would work pretty well.

  • LouisLazaris

    There are pros and cons. They’ve been discussed in various posts around the web:

    http://blog.cloudfour.com/the-ems-have-it-proportional-media-queries-ftw/
    http://stackoverflow.com/questions/22228568/switching-to-em-based-media-queries
    http://www.amberweinberg.com/using-ems-for-media-queries-for-proportional-layouts/

    But like George mentioned, this is just a simple demo.

    Ultimately, if you use ems, then you just need to be fully aware of your site’s typographic scale, and what effect (if any) it will have if you change the root font size. I like to use pixels myself, but for a demo like this, I don’t really see the difference. After all, the ems are just mapping to a pixel value anyways.

  • David Hucklesby

    A sure-fire way to eliminate the gaps between inline-blocks is to minify your HTML.

    • http://georgemarts.com/ George Martsoukos

      Indeed, this is another popular solution.

  • donkey

    if you overflow:hidden the figcaption with a fixed height and overflow:auto on hover, is it a bad practice?

    • http://georgemarts.com/ George Martsoukos

      What do you want to create? This seems like a slideToggle effect. Keep in mind that you cannot animate the overflow property.

  • http://www.tyssendesign.com.au John Faulds

    No it’s not.

  • Brian Hughes

    No further input on the fact that ‘In both cases, the last two items are distributed awkwardly on the final row.’?

  • Shawn Keys

    I am still learning so please bear with me. Whenever I try to put these codes into my site the header, footer shrinks or my site layout otherwise gets distorted and doesn’t fit. I can guess I will need to edit the css to the dimensions of my site but am not sure what specific line(s) where that is. My header and footer is 785px wide. I have the same issue for all of these kinds of css tutorials. Everything is easy until I copy and paste. Then everything’s out of whack. Professional web designers – try not to beat me up too badly. We all have to start somewhere.

  • http://dammediachat.com/ Nguyễn Tri Ân

    Only good with square image, But I want use image that don’t equal size.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Front-end, once a week, for free.