Does CSS Grid have a flex-grow function?
Asked Answered
M

2

69

Is there an analog to flex-grow for the grid property ?

I'd like my grid areas to accomodate the content they receive but have some areas take more place than others like flex-grow for flex.

Practically, in the example below I'd like

  • the turquoise to be invisible because it accomodates its content.
  • The footer to be invisible as well because it has no content.
  • The middle section take the remaining of the page like flex-grow.

More practically, I'd like this code:

.ctnr {
  display: grid;
  min-height: 100vh;
  grid-template-areas:
    "header header"
    "nav main"
    "footer footer";
}

.test {
  background: black;
  height: 1rem;
}

header {
  grid-area: header;
  background: turquoise;
}

nav {
  grid-area: nav;
  background: grey;
}

main {
  grid-area: main;
}

footer {
  grid-area: footer;
  background: yellow
}
<div class="ctnr">
  <header>
    <div class="test"></div>
  </header>
  <nav></nav>
  <main></main>
  <footer></footer>
</div>

To act like this code:

.ctnr {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.panel {
  flex-grow: 1;
  display: flex;
}

header {
  flex-grow: 0;
  background: turquoise;
}

nav {
  min-width: 10rem;
  background: grey
}

footer {
  background: yellow
}
<div class="ctnr">
  <header>hey</header>
  <div class="panel">
    <nav></nav>
    <main></main>
  </div>
  <footer></footer>
</div>

Without the div.panel and without adding any additional tag.

The reason I would like to do this, is a legitimate one, that extra div element is annoying me.

Mcdaniels answered 24/8, 2017 at 20:25 Comment(0)
D
83

CSS Grid offers the fr unit, which functions similarly to flex-grow.

While flex-grow distributes free space in the container among flex items, the fr unit distributes free space in the container among rows / columns.

From the spec:

7.2.3. Flexible Lengths: the fr unit

A flexible length or <flex> is a dimension with the fr unit, which represents a fraction of the free space in the grid container.

(Note that flex-grow is applied to flex items, while fr lengths are applied to grid containers.)

So in your grid, you have three rows:

  1. The first row is the header. You want the content to set its height. So its height is set to auto.

  2. The last row is the footer. You want the content to set its height. So its height is set to auto.

  3. The middle row contains the nav and main elements. You want this row to occupy all remaining vertical space. So its height is set to 1fr.

.ctnr {
  display: grid;
  grid-template-rows: auto 1fr auto;  /* key rule */
  grid-template-columns: 1fr 1fr;
  height: 100vh;
  grid-template-areas: "header header" 
                         "nav main" 
                       "footer footer";
}

header {
  grid-area: header;
  background: turquoise;
}

nav {
  grid-area: nav;
  background: grey;
}

main {
  grid-area: main;
  background: orange;
}

footer {
  grid-area: footer;
  background: yellow;
}

body {
  margin: 0;
}
<div class="ctnr">
  <header>header</header>
  <nav>nav</nav>
  <main>main</main>
  <footer>footer</footer>
</div>

jsFiddle demo

Drinking answered 25/8, 2017 at 2:47 Comment(0)
R
1

Adding to the accepted answer, with my case: sometimes a <header> element, and always <main> and <footer> element would be present on the page, below each other; the <main> should always be stretched (i.e. flex-grow: 1 but for grid)

I added grid-template-rows: auto 1fr auto; to the parent. This works when all 3 are present but not when the <header> is missing. I fixed that by adding:

The fix:

grid-template-rows: auto 1fr auto; on the parent element
grid-rows-end: span 2 on the <main>

Explanation:

The <main> will always take 2 rows. The grid-template-rows is a pattern for an infinite number of rows, so row 1-3 are auto 1fr auto, 4-6 are the same, 7-9, ...
When there are 3 elements on the page, the <header> will take the first row with value auto, the <main> will take rows 2 and 3 with values 1fr auto. The <footer> will start on row 4, which is a new start of the pattern containing auto.
When there are only 2 elements (so no <header>), the <main> will take row 1 and 2, meaning auto 1fr, and the <footer> will take row 3 containing auto

With header:

html,
body {
  height: 100%;
  margin: 0;
}

body {
  display: grid;
  grid-template-rows: auto 1fr auto;
  height: 100%;
}

main {
  grid-row-end: span 2;
}

header,
footer {
  background-color: hotpink;
}
<header>header</header>
<main>main</main>
<footer>footer</footer>

Without header:

html,
body {
  height: 100%;
  margin: 0;
}

body {
  display: grid;
  grid-template-rows: auto 1fr auto;
  height: 100%;
}

main {
  grid-row-end: span 2;
}

header,
footer {
  background-color: hotpink;
}
<main>main</main>
<footer>footer</footer>
Riboflavin answered 9/11, 2023 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.