CSS Grid with variable number of "auto" rows, but one row should take "1fr"
Asked Answered
P

2

18

I'm fiddling around with a CSS grid-based frontend and the following behaviour is required over and over and over again in different parts of the frontend:

  1. A grid with a variable number of rows.
  2. Every row should have a variable size (auto will do).
  3. The last row should always take up all the remaining space.

So in the case I happen to need five rows, this does the trick:

.myGridForFiveRows
{
    display: grid;
    grid-template-rows: auto auto auto auto 1fr;
}

However, I'd really like a stylesheet that yields the correct behaviour for any given number of rows. I thought maybe I could somehow use repeat() to do this?

https://developer.mozilla.org/en-US/docs/Web/CSS/repeat

.myGrid
{
    display: grid;
    grid-template-rows: repeat(auto-fit, auto) 1fr;
}

I've been playing around with variants of repeat(auto-fit, auto) and repeat(auto-fill, auto), which unfortunately aren't legit CSS, while repeat(4, auto) or repeat(auto-fill, 30px) are.

Any idea? It's not something I can't circumvent, but it just so happens that "display n properly sized rows, then let the last element take up all the remaining space" is basically the default behaviour for all elements in my spec...

Paella answered 19/1, 2018 at 10:47 Comment(2)
What is the purpose of the last row? Perhaps a wrapper for the content and a "footer row" and solve the rest with flexbox or Grid?Goodtempered
Hey Paulie, the purpose of the last row is to contain a certain HTML element just like every other row. My employer just wants the last element to artificially blow up till it fills the rest of the page. I guess I could go down the route of specifying that if it's the last element of the grid, it should flex grow or whatever. I was just curious if you could tell the grid to do the sizing (which I find vastly prettier) as opposed to the content :)Paella
T
10

Considering your three requirements:

  1. A grid with a variable number of rows.
  2. Every row should have a variable size (auto will do).
  3. The last row should always take up all the remaining space.

Flexbox is well-suited for the job. In fact, it may be the perfect fit (depending on your other requirements). I've provided a code sample below.

But if Grid Layout is what you want, then I think you're going to be disappointed. I don't believe Level 1 can do the job.

The closest you can get would be:

grid-template-rows: repeat(auto-fit, minmax(auto, 1px)) 1fr;

But it won't work because the current grid spec doesn't support this syntax.

repeat(auto-fit, auto) 1fr

This is the code you tried. It's not valid because auto and fr cannot be used with auto-fit.

7.2.2.1. Syntax of repeat()

Automatic repetitions (auto-fill or auto-fit) cannot be combined with intrinsic or flexible sizes.

  • An intrinsic sizing function is min-content, max-content, auto, fit-content().

  • A flexible sizing function is <flex> (fr).

You can get around the auto limitation with something like this:

repeat(auto-fit, minmax(auto, 1px)) 1fr

minmax(min,max)

Defines a size range greater than or equal to min and less than or equal to max.

If max < min, then max is ignored and minmax(min,max) is treated as min.

As a maximum, a <flex> value sets the track’s flex factor; it is invalid as a minimum.

That works to properly auto-size your rows, whether the container has the default auto height or a height / min-height defined. demo

But it still doesn't solve the last row problem, since the 1fr remains invalid, and causes the entire rule to fail. demo

Something like this would be valid:

repeat(auto-fit, minmax(auto, 1px)) 10em

But the auto-fit doesn't work as you expect: the 10em is applied to the second row. demo

And the rule doesn't work as expected if the container has a height or min-height defined. demo


Even with CSS Grid Layout now widely available, Flexbox is still the better choice in some cases.

This covers all your requirements with clean and simple code:

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

section:last-child {
  flex-grow: 1;
}

section {
  /* flex-basis: auto <-- this is a default setting */
  margin-bottom: 10px;
  background-color: lightgreen;
}

body {
  margin: 0;
}
<article>
  <section>text text text</section>
  <section>text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text
    text text text text text text text text text text </section>
  <section>text text text text text text text text text text text text text text text text text text text text text text text text text text text</section>
  <section>text text text text text text text text text text text text text text text text text text text text text text text text text text text</section>
  <section>text text text text text text text text text text text text text text text text text text text text text text text text text text text</section>
</article>
Tarantula answered 19/1, 2018 at 14:57 Comment(8)
Also from §7.2.2.1: "The <auto-repeat> variant can repeat automatically to fill a space, but requires definite track sizes so that the number of repetitions can be calculated." This seems to be a limitation of grid layout.Rajiv
@BoltClock, yes I saw that, but I figured the 1fr could be factored in after the auto-repeat function had done its job. So I just left that out of my answer since I wasn't sure about the process.Tarantula
Based on the OP's requirements (the 3 bullet points in the Q), flexbox should be fine.Tarantula
I agree. Part of the craft is choosing the right tool for the job.Rajiv
@BoltClock, I can understand why 1fr may not work with auto-fill, since empty tracks don't collapse. So there's no free space left over. But with auto-fit empty tracks do collapse, and the last occupied track can have space to expand. More here if you're interested: https://mcmap.net/q/447245/-what-is-the-difference-between-auto-fill-and-auto-fitTarantula
You're probably more familiar with grid layout than I am, but the only explanation I can think of is that the grid container doesn't know anything about its grid items in advance at the time it lays out its grid areas, so it doesn't know how many tracks it actually needs to create, and that is the reason for the limitation. (I was considering posting an answer along these lines, but I wanted to defer to you first.)Rajiv
Hey folks, thanks for all the input. I will try to talk Michael_B's solution through with colleagues. Unfortunately there are quite a few things that the grid does for us (I left that out to produce a minimal example, sorry ...), but his solution might still be workable. Thanks again for everything!Paella
I really just need to find time to sit down and build with flexbox and grid... just haven't had the chance to yet.Rajiv
B
-1

This a little bit of a hack, but you could enter a very large number for the repeat that you would never cross over. For example, you could do this for the grid-template-rows:

grid-template-rows: repeat(100000000, auto) 1fr;

I had the same issue, and this worked for me.

Brigantine answered 7/4, 2022 at 22:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.