Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS only solution to set "same height" row sections on a responsive grid

Wanted: a CSS only solution to enable equal height grid "sections" on a per row basis, that is also responsive.

This diagram hopefully explains the requirement better than the words in this post will:

helpful diagram

The "item grid" should be responsive - in that it can show a different number of cards per row based on viewport width. And within a given row, the equivalent sections should have the same height on a "per row" basis.

In the below HTML & CSS - the item cards are split in to the rows that we need (at the two example break points desktop & mobile) but the content section heights are variable (yuck):

.items {
  max-width: 1200px;
}

.item {
  width: 25%;
  box-sizing: border-box;
  display: inline-block;
  vertical-align: top;
  padding: 0 12px;
  margin: 24px -4px 24px 0;
}

@media (max-width: 600px) {
  .item {
    width: 50%;
  }
}

.item__heading {
  background-color: #d4d0f5;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
}

.item__content {
  padding: 10px;
  border-left: 1px solid #bbbbbb;
  border-right: 1px solid #bbbbbb;
}

.item__price {
  background-color: #e0f6d9;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
}
<div class="items">

  <div class="item">
    <div class="item__heading">
      Item 1
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__price">
      £99.99
    </div>
  </div>


  <div class="item">
    <div class="item__heading">
      Item 2
    </div>
    <div class="item__content">
      Some content that is longer than other items on the same row and sets the height of this section
    </div>
    <div class="item__price">
      £69.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 3
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__price">
      £69.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 4
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__price">
      £109.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 5
    </div>
    <div class="item__content">
      Some content that is a medium kind of length blah blah
    </div>
    <div class="item__price">
      £29.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 6
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__price">
      £99.99
    </div>
  </div>

    
</div>

The following codepen is a JavaScript based solution that achieves the desired outcome - but is what I am trying to avoid: https://codepen.io/rusta/pen/KmbVKd

Limitations

  • The number of items to be displayed in the grid list can be any number from 1-150
  • The size of the item content to be displayed will be genuinely variable (so picking a "sensible" min-height is not an option)

I was hoping that the new CSS Grid system would help me achieve the above, but having played with it for a while it seems to need a bit more structure than I had hoped it would, and the responsive aspect seemed rather challenging. But maybe there is a CSS Grid based answer out there somewhere

Further note: I say a CSS only solution, by which I mean a non-JS solution. If the HTML blocks need to change (order/nesting/class names) to support a non-JS solution that is absolutely fine

In this trivial example - we are only focusing on the "content" section for having "matching heights" - as we can assume the heading and price sections will naturally be the same height. It would be nice to enable "equivalency" across any matching grid section (header/content/price/other) but that can be for another day...

like image 396
Russell Winborn Avatar asked May 22 '17 14:05

Russell Winborn


1 Answers

By giving the .items display: flex; flex-wrap: wrap; your item will become flex items and flow from left to right and wrap when there is no more space.

Then you give the .item display: flex; flex-direction: column;, which will make them a flex container as well, and by using column direction, its children will flow vertically, like block elements.

Finally you give the .item__content flex: 1;, which will make it take any remaining space, vertically, hence every row's item will have equal height.

Updated codepen

Stack snippet

.items {
  display: flex;
  flex-wrap: wrap;
  max-width: 1200px;
}

.item {
  display: flex;
  flex-direction: column;
  width: 25%;
  box-sizing: border-box;
  vertical-align: top;
  padding: 0 12px;
  margin: 24px -4px 24px 0;
}

@media (max-width: 600px) {
  .item {
    width: 50%;
  }
}

.item__heading {
  background-color: #d4d0f5;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
}

.item__content {
  flex: 1 1 auto;                   /* IE need 1 1 auto */
  padding: 10px;
  border-left: 1px solid #bbbbbb;
  border-right: 1px solid #bbbbbb;
}

.item__price {
  background-color: #e0f6d9;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
}
<div class="items">

  <div class="item">
    <div class="item__heading">
      Item 1
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__price">
      £99.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 2
    </div>
    <div class="item__content">
      Some content that is longer than other items on the same row and sets the height of this section
    </div>
    <div class="item__price">
      £69.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 3
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__price">
      £69.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 4
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__price">
      £109.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 5
    </div>
    <div class="item__content">
      Some content that is a medium kind of length blah blah
    </div>
    <div class="item__price">
      £29.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 6
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__price">
      £99.99
    </div>
  </div>

</div>
like image 115
Asons Avatar answered Sep 21 '22 18:09

Asons