Equal Height of Elements Inside Grid Item with CSS Grid Layout

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 of elements inside grid item with CSS grid layout

Is it even possible?

tldr; Yes.

Codepen Demo #1

Codepen Demo # 2 (Uses SASS and is configurable)


The difficulty here is that each article is a grid within itself and therefore any one article has no knowledge about another. Because of this, there is no way for a component of one article like a header to adjust according to the height of a header in another article.

There is actually a way to pull this off with css grids & without changing any markup!

We could 'flatten' the structure with CSS such that all the components of all the articles are part of just one CSS grid - the article container.

This is possible without even changing the current markup by setting the articles with display: contents

display: contents (caniuse)

From Caniuse:

display: contents causes an element's children to appear as if they
were direct children of the element's parent, ignoring the element
itself. This can be useful when a wrapper element should be ignored
when using CSS grid or similar layout techniques.

So if we set the articles with display: contents

.container article {
display: contents;
}

Now all of the headers, sections and footer become (direct) grid items (of the container - which has display:grid) which we can arrange using the grid-template-areas property.

.container {
display: grid;
grid-column-gap: 1em; /* horizontal gap between articles */
grid-template-columns: repeat(3, 1fr);

grid-template-areas: "header1 header2 header3"
"section1 section2 section3"
"footer1 footer2 footer3"
"header4 header5 header6"
"section4 section5 section6"
"footer4 footer5 footer6"
}

Since each header/section/footer take up exactly one cell - this forces them to take up the same vertical height. So e.g. header1,header2 and header3 will all have the same height regardless of their content.

Now set the grid-area properties on each of the cells.

article:nth-child(1) header {
grid-area: header1;
}
article:nth-child(2) header {
grid-area: header2;
}
article:nth-child(3) header {
grid-area: header3;
}
article:nth-child(4) header {
grid-area: header4;
}
article:nth-child(1) section {
grid-area: section1;
}
...
article:nth-child(4) section {
grid-area: section4;
}
article:nth-child(1) footer {
grid-area: footer1;
}
...
article:nth-child(4) footer {
grid-area: footer4;
}

Finally, set a vertical gap between each row of articles (starting from the second row of articles):

article:nth-child(n + 4) header {
margin-top: 1em;
}

Demo:

body {  width: 100%;  max-width: 1024px;  margin: auto;}
.container { display: grid; grid-column-gap: 1em; grid-template-columns: repeat(3, 1fr); grid-template-areas: "header1 header2 header3" "section1 section2 section3" "footer1 footer2 footer3" "header4 header5 header6" "section4 section5 section6" "footer4 footer5 footer6"}
.container article { display: contents;}
article header { background-color: #eeeeee;}
article section { background-color: #cccccc;}
article footer { background-color: #dddddd;}
article:nth-child(n + 4) header { margin-top: 1em;}
article:nth-child(1) header { grid-area: header1;}article:nth-child(2) header { grid-area: header2;}article:nth-child(3) header { grid-area: header3;}article:nth-child(4) header { grid-area: header4;}article:nth-child(1) section { grid-area: section1;}article:nth-child(2) section { grid-area: section2;}article:nth-child(3) section { grid-area: section3;}article:nth-child(4) section { grid-area: section4;}article:nth-child(1) footer { grid-area: footer1;}article:nth-child(2) footer { grid-area: footer2;}article:nth-child(3) footer { grid-area: footer3;}article:nth-child(4) footer { grid-area: footer4;}
<div class="container">    <article>        <header>            <h2>Header</h2>            <h2>Header</h2>        </header>        <section>            <p>Content</p>        </section>        <footer>            <p>Footer</p>        </footer>    </article>    <article>        <header>            <h2>Header</h2>        </header>        <section>            <p>Content</p>            <p>Content</p>            <p>Content</p>            <p>Content</p>            <p>Content</p>        </section>        <footer>            <p>Footer</p>            <p>Footer</p>        </footer>    </article>    <article>        <header>            <h2>Header</h2>        </header>        <section>            <p>Content</p>            <p>Content</p>            <p>Content</p>        </section>        <footer>            <p>Footer</p>        </footer>    </article>    <article>        <header>            <h2>Header</h2>        </header>        <section>            <p>Content</p>            <p>Content</p>            <p>Content</p>            <p>Content</p>        </section>        <footer>            <p>Footer</p>            <p>Footer</p>        </footer>    </article></div>

Making the content inside grid items equal height in CSS Grid

Those grid items are already the same height. They are both the height of the second row, which is 1fr, as defined by grid-template-rows on the grid container.

If you put a border around those two items (.companyInfoContainer & .contactInfoContainer), you'll see what I mean.

revised demo

It seems that you want the bordered children of the grid items (.companyInfoBox and .contactInfoBox) to consume all available height.

Since the grid items are not also grid containers, you can't use grid properties on the children.

An easy and efficient solution would be to make the grid items flex containers. Then you can apply flex properties to the children, which can make them consume free space.

.companyInfoContainer {
display: flex;
flex-direction: column;
}

.companyInfoBox {
flex: 1;
}

.contactInfoContainer {
display: flex;
flex-direction: column;
}

.contactInfoBox {
flex: 1
}

revised demo

More details: Descendant element not responding to grid properties

Set grid items to be same height

You can add more rows like this:

grid-template: ".   two   ." 1fr
"one two three" 1fr
"one four three" 1fr
". four ." 1fr /
1fr 1fr 1fr;

With the appropriate grid-area on children.

Here is the snippet (I have also added a height: 90%; on children to make them fit the cells height but keeping the margin):

#cards {
padding: 0 7vw;
margin-top: 3vh;
display: grid;
grid-template: ". two ." 1fr
"one two three" 1fr
"one four three" 1fr
". four ." 1fr /
1fr 1fr 1fr;
align-items: center;
justify-content: center;
}

.card:nth-child(1){
grid-area: one;
}
.card:nth-child(2){
grid-area: two;
}
.card:nth-child(3){
grid-area: three;
}
.card:nth-child(4){
grid-area: four;
}

.card {
display: block;
border: solid 5px #000;
box-shadow: 0 20px 30px -15px hsl(215, 49%, 82%);
border-radius: 8px;
margin: 5%;
overflow: hidden;
text-align: center;
height: 90%;
}
<div id="cards">
<div class="card">
<div class="card-content">
<h2 class="card-title">Item 1</h2>
</div>
</div>
<div class="card">
<div class="card-content">
<h2 class="card-title">Item 2</h2>
</div>
</div>
<div class="card">
<div class="card-content">
<h2 class="card-title">Item 3</h2>
<p>Hello world</p>
</div>
</div>
<div class="card">
<div class="card-content">
<h2 class="card-title">Item 4</h2>
<p>I built this two line paragraph oh yes I really want it to be two lines or yes finally.</p>
</div>
</div>
</div>

Equal height children of grid items

Unless you can use display: subgrid or display: contents (more info below), the only way to achieve this behavior with CSS alone would be to put all children of the various grid items in a single grid container. In other words, make them all siblings.

The problem is that an element in one container has no idea what its cousin in another container is doing. They have no direct connection, which prevents them from working together. Siblings, on the other hand, have no problem working together.

Note that "flattening" HTML documents for the purposes of CSS is not recommended as it damages semantics and accessibility.

More information:

  • Positioning content of grid items in primary container (subgrid feature)
  • What is an alternative to css subgrid?

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.

In CSS Grid how to remove column 1's matching height of column 2?

You can't stop the columns being equal height but you can align the content of the columns so it does not achieve the default 100% height.

Note that the row will still have the same height but this has the visual appearance you seem to be after.

.container {
display: grid;
grid-template-columns: minmax(0, 1fr) 300px;
column-gap:32px;
}
.col1 {
background:red;
padding:32px;
align-self:flex-start;
}
.col2 {
background:orange;
padding:32px;
align-self:flex-start;
}
<div class="container">
<div class="col1">Bacon ipsum dolor amet boudin andouille pig beef, prosciutto tongue ball tip cow ham. Ground round salami tenderloin, biltong tail pastrami pork shoulder pork loin. Picanha cow ribeye meatloaf tri-tip pork chop burgdoggen salami beef chuck alcatra swine ground round. Tail doner tri-tip flank brisket prosciutto chislic capicola meatloaf picanha swine. Shankle capicola venison beef boudin, strip steak alcatra bacon sirloin cupim spare ribs short ribs kielbasa pork loin ground round. Leberkas short loin boudin meatloaf.</div>
<div class="col2">Kielbasa pastrami tenderloin, turkey short loin pork loin swine fatback flank leberkas prosciutto hamburger t-bone drumstick. Jowl picanha ham, t-bone filet mignon short ribs turducken leberkas. Turducken ham hock alcatra, shoulder tail sirloin strip steak hamburger picanha jerky tenderloin spare ribs tri-tip. Tenderloin prosciutto picanha, capicola kevin pig biltong t-bone pork chop boudin porchetta bacon salami chicken fatback. Ham hock pancetta tail tenderloin jerky ground round chislic frankfurter shank picanha pork belly strip steak pork chop. Short loin andouille biltong corned beef pig pork chop pork bacon tri-tip jerky.

Filet mignon meatloaf drumstick hamburger ham hock landjaeger tri-tip ribeye swine. Ham shankle tongue, kielbasa swine burgdoggen tenderloin beef ribs buffalo meatball hamburger leberkas picanha t-bone. Beef ribs ball tip ham pork loin capicola filet mignon. Hamburger sausage shoulder meatball pork chop tail spare ribs, fatback burgdoggen drumstick short loin swine pork loin.

Kielbasa boudin cow beef beef ribs tongue pork chop frankfurter sausage burgdoggen. Flank landjaeger leberkas spare ribs alcatra, swine corned beef boudin shoulder pig prosciutto pancetta pork chop. Pork chop turducken andouille filet mignon alcatra porchetta cupim tri-tip cow tongue beef meatball doner. Beef ribs ham hock chuck shank doner.

</div>
</div>


Related Topics



Leave a reply



Submit