skip to Main Content

We have a marketing page on our site that displays a grid of square product images with a heading over each. We want it to be 3 columns wide on wide screens, and 2 columns wide on narrow screens (mobile). Currently using css grid with a media query that changes the number of columns on narrow screens. If we have the heading and image together inside each grid cell, then long headings that wrap take up more vertical space, pushing the image lower in that cell relative to its neighbours on that row.

How can I make the images line up relative to each other, regardless of the height of the headings in that row?

There are 6 images, so they fit nicely in either 3 or 2 columns, so there’s no trouble filling the last row.

A codepen that demonstrates dummy layout.

.container {
  width: 60%;
  border: 1px solid black;
  margin: 0 auto;
  padding: 10px;
}

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}
/* Make the grid 2 columns on narrow screens */
@media (max-width: 900px) {
  .grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.grid-item .heading {
  font-size: 1em;
}
.grid-item a {
  display: block;
  aspect-ratio: 1/1;
}
.grid-item .fake-img {
  width: 100%;
  height: 100%;
  background-color: palegoldenrod;
}
<p>The grid contains items, each has a heading and an image (faked here with a coloured box).  Because the headings have variable length, some wrap and push the image down relative to the top of the item.  This makes images along one row become misaligned.  This would be easy to fix in a fixed layout by putting the headings in their own cell of the grid, but one would have to arrange the 3 headings in one row, then 3 images in the second row, and repeat.  If a media query for narrow screens tells the grid to have 2 columns instead of 3, the whole layout would break because it has 3 headings and images hardcoded into each row.
</p>
<p>There is a media query that makes the grid 2 columns wide on a page less than 900px wide, you can resize your browser window to achieve this.  I'm not sure how this will look in the Facebook Snippet viewer.</p>

<div class="container">
  <div class="grid">
    <div class="grid-item">
      <span class="heading">Heading</span>
      <a href="#"><div class="fake-img"></div></a>
    </div>
    <div class="grid-item">
      <span class="heading">Heading Heading Heading</span>
      <a href="#"><div class="fake-img"></div></a>
    </div>
    <div class="grid-item">
      <span class="heading">Heading</span>
      <a href="#"><div class="fake-img"></div></a>
    </div>
    <div class="grid-item">
      <span class="heading">Heading Heading Heading Heading Heading</span>
      <a href="#"><div class="fake-img"></div></a>
    </div>
    <div class="grid-item">
      <span class="heading">Heading</span>
      <a href="#"><div class="fake-img"></div></a>
    </div>
    <div class="grid-item">
      <span class="heading">Heading</span>
      <a href="#"><div class="fake-img"></div></a>
    </div>
  </div>
</div>

One approach may be to have a row of headings first, then a row of product images that match those headings, but this splits the headings from their images in a very hardcoded way. You also run into trouble when a media query changes the number of columns to 2, then your 3 headings on one row breaks. I know that with flexbox you can rearrange the order of items using CSS, so a media query could change the number of columns and rearrange the elements, but that whole approach seems ugly and high maintenance.

I could force the height of the headings with something like .heading { display: inline-block; height: 2em; but that won’t work for headings that wrap taller than that and is quite brittle.

I’ve searched around but this problem seems quite niche and I’ve not seen an attempted solution. Anyone got any bright ideas I could try?

2

Answers


  1. The answer lies in styling the .grid-item class itself. Note that since these grid items are in a normal CSS grid, each cell in a given row is the same height. That means that if you turn each grid-item into a flex box, pushing the squares to the bottom will naturally put them in line with each other. It would look like this:

    .grid-item {
      display: flex;
      flex-direction : column;
      justify-content: space-between; // or flex-end. Whichever you prefer
    }
    

    flex-end would push everything to the bottom of a row, making the squares line up naturally. space-between would maximize the space between the headings and images, which would again push the squares to the bottom, lining them up.

    Login or Signup to reply.
  2. This is actually a nice opporchancity to use subgrid. It’s quite new, but this is exactly what it was designed for:

    .container {
      width: 60%;
      border: 1px solid black;
      margin: 0 auto;
      padding: 10px;
    }
    
    .grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 20px;
      grid-auto-rows: auto;
    }
    
    .grid-item {
      display: grid;
      grid-row: span 2;
      grid-template-rows: subgrid;
    }
    
    /* Make the grid 2 columns on narrow screens */
    @media (max-width: 900px) {
      .grid {
        grid-template-columns: repeat(2, 1fr);
      }
    }
    
    .grid-item .heading {
      font-size: 1em;
    }
    .grid-item a {
      display: block;
      aspect-ratio: 1/1;
    }
    .grid-item .fake-img {
      width: 100%;
      height: 100%;
      background-color: palegoldenrod;
    }
    <div class="container">
      <div class="grid">
        <div class="grid-item">
          <span class="heading">Heading</span>
          <a href="#"><div class="fake-img"></div></a>
        </div>
        <div class="grid-item">
          <span class="heading">Heading Heading Heading</span>
          <a href="#"><div class="fake-img"></div></a>
        </div>
        <div class="grid-item">
          <span class="heading">Heading</span>
          <a href="#"><div class="fake-img"></div></a>
        </div>
        <div class="grid-item">
          <span class="heading">Heading Heading Heading Heading Heading</span>
          <a href="#"><div class="fake-img"></div></a>
        </div>
        <div class="grid-item">
          <span class="heading">Heading</span>
          <a href="#"><div class="fake-img"></div></a>
        </div>
        <div class="grid-item">
          <span class="heading">Heading</span>
          <a href="#"><div class="fake-img"></div></a>
        </div>
      </div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search