Multiline Grid with Elements of Same Height Using Flexbox

Multiline grid with elements of same height using flexbox

No, this is not possible with pure CSS/flexbox.

I'll cite the W3C spec:

When a flex container has multiple lines, the cross size of each line is the minimum size necessary to contain the flex items on the line (after aligment due to align-self), and the lines are aligned within the flex container with the align-content property. [...]

(From http://www.w3.org/TR/css3-flexbox/#flex-lines)

So, one item is only expanded to the maximum height of that line it's currently on.

Terminology of the above quote:

(From http://www.w3.org/TR/css3-flexbox/#box-model)

Equal height rows in CSS Grid Layout

Short Answer

If the goal is to create a grid with equal height rows, where the tallest cell in the grid sets the height for all rows, here's a quick and simple solution:

  • Set the container to grid-auto-rows: 1fr

How it works

Grid Layout provides a unit for establishing flexible lengths in a grid container. This is the fr unit. It is designed to distribute free space in the container and is somewhat analogous to the flex-grow property in flexbox.

If you set all rows in a grid container to 1fr, let's say like this:

grid-auto-rows: 1fr;

... then all rows will be equal height.

It doesn't really make sense off-the-bat because fr is supposed to distribute free space. And if several rows have content with different heights, then when the space is distributed, some rows would be proportionally smaller and taller.

Except, buried deep in the grid spec is this little nugget:

7.2.3. Flexible Lengths: the fr
unit

...

When the available space is infinite (which happens when the grid
container’s width or height is indefinite), flex-sized (fr) grid tracks are
sized to their contents while retaining their respective proportions.

The used size of each flex-sized grid track is computed by determining
the max-content size of each flex-sized grid track and dividing that
size by the respective flex factor to determine a “hypothetical 1fr
size”.

The maximum of those is used as the resolved 1fr length (the
flex fraction), which is then multiplied by each grid track’s flex
factor to determine its final size.

So, if I'm reading this correctly, when dealing with a dynamically-sized grid (e.g., the height is indefinite), grid tracks (rows, in this case) are sized to their contents.

The height of each row is determined by the tallest (max-content) grid item.

The maximum height of those rows becomes the length of 1fr.

That's how 1fr creates equal height rows in a grid container.


Why flexbox isn't an option

As noted in the question, equal height rows are not possible with flexbox.

Flex items can be equal height on the same row, but not across multiple rows.

This behavior is defined in the flexbox spec:

6. Flex Lines

In a multi-line flex container, the cross size of each line is the minimum size necessary to contain the flex items on the line.

In other words, when there are multiple lines in a row-based flex container, the height of each line (the "cross size") is the minimum height necessary to contain the flex items on the line.

Equal height rows in a flex container

The answer is NO.

The reason is provided in the flexbox specification:

6. Flex Lines

In a multi-line flex container, the cross size of each line is the minimum size necessary to contain the flex items on the line.

In other words, when there are multiple lines in a row-based flex container, the height of each line (the "cross size") is the minimum height necessary to contain the flex items on the line.

Equal height rows, however, are possible in CSS Grid Layout:

  • Equal height rows in CSS Grid Layout

Otherwise, consider a JavaScript alternative.

Managing CSS flex-box growth in multi-line to create a grid of equal blocks

This is totally possible, however, you have to know how many columns you'll have at a maximum. http://jsfiddle.net/kelunik/C2q8D/6/

Solution

By adding a few empty placeholders, your cards get equally sized. You just have to make sure, you're adding enough placeholders. It doesn't matter if there are too many, because they'll have a height of zero and are not visible.

CSS

section {
display: flex;
flex-flow: row wrap;
background-color: blue;
}

div {
background-color: red;
height: 3rem;
flex: 1 0 10rem;
}

div:nth-child(even) {
background-color: green;
}

div:empty {
margin: 0;
height: 0;
border: 0;
background: transparent;
}

HTML

<section>
<div>a</div>
<div>b</div>
<div>c</div>
<div>d</div>
<div>e</div>
<div>f</div>
<div>g</div>
<div>h</div>
<div>i</div>
<div>j</div>
<div>k</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</section>

CSS only solution to set MULTIPLE “same height” row sections on a responsive grid

Based on your own answer, where you grouped them by 4, you can do that with CSS Flexbox too.

To make them behave when there is less than 4, it might be possible to accomplish that using the nth-child selector, but it was simpler to use a last* class, so I went for the latter.

One might even be able to do this without the .group_of_4 wrapper, with some clever nth-child rules, but again, went for the simpler since it does not come with any obvious limitations

Fiddle demo

.items {  display: flex;  flex-direction: column;  max-width: 1200px;}
.items .group_of_4 { display: flex; flex-wrap: wrap; justify-content: space-between; /* updated */}
.items .group_of_4 ~ .group_of_4 { margin-top: 24px;}
.items .group_of_4 > div { width: calc(25% - 12px); /* updated */ box-sizing: border-box; padding: 12px;}

.item__heading { background-color: #d4d0f5; padding: 10px; text-align: center; border: 1px solid #bbbbbb; order: 1;}
.item__content { padding: 10px; border-left: 1px solid #bbbbbb; border-right: 1px solid #bbbbbb; order: 2;}
.item__features { padding: 10px; border-left: 1px solid #bbbbbb; border-right: 1px solid #bbbbbb; background-color: #f7cbb1; order: 3;}
.item__price { background-color: #e0f6d9; padding: 10px; text-align: center; border: 1px solid #bbbbbb; order: 4;}
/* one item in a group */.items .group_of_4 .last1 { margin-right: calc(75% 6px); /* updated */}/* two items in a group */.items .group_of_4 .last2 { margin-right: calc(50% + 6px); /* updated */}/* three items in a group */.items .group_of_4 .last3 { margin-right: calc(25% + 6px); /* updated */}
@media (max-width: 600px) { .items .group_of_4 > div:nth-child(8) ~ .item__heading { margin-top: 24px; order: 5; } .items .group_of_4 > div:nth-child(8) ~ .item__content { order: 6; } .items .group_of_4 > div:nth-child(8) ~ .item__features { order: 7; } .items .group_of_4 > div:nth-child(8) ~ .item__price { order: 8; } .items .group_of_4 > div { width: calc(50% - 12px); /* updated */ }
/* one item in a group */ /* three items in a group */ .items .group_of_4 .last1, .items .group_of_4 .last3 { margin-right: 50%; } /* two items in a group */ .items .group_of_4 .last2 { margin-right: 0%; } }
<div class="items">
<div class="group_of_4"> <div class="item__heading"> Item 1 </div> <div class="item__content"> Some content that is not that long </div> <div class="item__features"> <ul> <li>feature 1</li> </ul> </div> <div class="item__price"> £99.99 </div>
<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 as it spans many more lines than the rest of the other content sections on this row </div> <div class="item__features"> <ul> <li>feature 1</li> </ul> </div> <div class="item__price"> £69.99 </div>
<div class="item__heading"> Item 3 </div> <div class="item__content"> Some content that is not that long </div> <div class="item__features"> <ul> <li>feature 1</li> <li>feature 2</li> <li>feature 3</li> </ul> </div> <div class="item__price"> £69.99 </div>
<div class="item__heading"> Item 4 </div> <div class="item__content"> Some content that is not that long </div> <div class="item__features"> <ul> <li>feature 1</li> </ul> </div> <div class="item__price"> £109.99 </div> </div> <div class="group_of_4"> <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__features"> <ul> <li>feature 1</li> </ul> </div> <div class="item__price"> £29.99 </div>
<div class="item__heading last2"> Item 6 </div> <div class="item__content last2"> Some content that is not that long </div> <div class="item__features last2"> <ul> <li>feature 1</li> </ul> </div> <div class="item__price last2"> £99.99 </div>
</div></div>

How to vertically stretch only one row in a multi-line flexbox wrapped layout?

Have you considered adding one more div for the first two elements?

* {
font-size: 1.5rem;
margin: 0;
padding: 0;
}

.container {
background: #AAA;
height: 100vh;
display: flex;
flex-direction: column;
align-content: stretch;
}

.container1 {
display: flex;
flex: 1 0;
}

.first {
background: lightblue;
width: 30%;
}

.second {
background: lightyellow;
width: 70%;
}

.third {
background: lightgreen;
}
<body>
<div class="container">
<div class="container1">
<div class="first">First</div>
<div class="second">Second</div>
</div>
<div class="third">Third</div>
</div>
</body>

Equal height rows in flex-direction: column

The flex equal height columns feature – which is the result of align-items: stretch, a default setting of a flex container – applies only to flex items on the same row.

It doesn't work for items in a multi-line flex container. This behavior is defined in the spec:

6. Flex Lines

In a multi-line flex container (even one with only a single line), the
cross size of each line is the minimum size necessary to contain the
flex items on the line (after alignment due to align-self), and the
lines are aligned within the flex container with the align-content
property.

In other words, when there are multiple lines in a row-based flex container, the height of each line (the "cross size") is the "minimum size necessary to contain the flex items on the line".

In addition, because align-items: stretch works along the cross-axis, the equal height columns feature is useless in flex-direction: column, where the cross-axis is horizontal.

To achieve equal height columns/rows across multiple lines consider a Javascript solution.


However, without knowing much about your overall objective, here's a simple way to achieve equal height rows in your current layout:

Add duplicate content in both divs. In the .component.left div, use visibility: hidden.

Revised Fiddle



Related Topics



Leave a reply



Submit