CSS Grid with Variable Number of "Auto" Rows, But One Row Should Take "1Fr"

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

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>

Defining columns for a variable number of items, all in one row, equally spaced

This configuration is suitable for Flexbox where all you need to do is

.wrapper {
display:flex
}
.wrapper img {
flex:1;
}

Using CSS grid you have to do

.wrapper {
display:grid;
grid-auto-columns:1fr;
grid-auto-flow:column;
}

You consider a column flow and you define the width of column to be equal to 1fr

.wrapper {
display:grid;
grid-auto-columns:1fr;
grid-auto-flow:column;
grid-gap:5px;
margin:10px;
}

.wrapper span {
height:50px;
background:red;
}
<div class="wrapper">
<span></span>
<span></span>
<span></span>
<span></span>
</div>

<div class="wrapper">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>

Can I make a CSS grid with dynamic number of rows or columns?

Okay, after reading the MDN reference, I found the answer! The key to dynamic rows (or columns) is the repeat property.

const COLORS = [  '#FE9',  '#9AF',  '#F9A',  "#AFA",  "#FA7"];
function addItem(container, template) { let color = COLORS[_.random(COLORS.length - 1)]; let num = _.random(10000); container.append(Mustache.render(template, { color, num }));}
$(() => { const tmpl = $('#item_template').html() const container = $('#app'); for(let i=0; i<5; i++) { addItem(container, tmpl); } $('#add_el').click(() => { addItem(container, tmpl); }) container.on('click', '.del_el', (e) => { $(e.target).closest('.item').remove(); });});
.container {  width: 100%;  display: grid;  grid-template-columns: repeat(4, 1fr);  grid-template-rows: repeat(auto-fill, 120px);  grid-row-gap: .5em;  grid-column-gap: 1em;}
.container .item {}
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><div id="app" class="container"></div>
<button id="add_el">Add element</button>
<template id="item_template"> <div class="item" style="background: {{color}}"> <p>{{ num }}</p> <p> <button class="del_el">Delete</button> </p> </div></template>

Can I use css-grid to display an unknown number of items, in left-to-right reading order, over two rows?

A CSS only solution where you need to write a bit of CSS to cover many cases. The below code covers up to 12 elements and the CSS isn't that big. You can keep adding more to cover more cases.

.box {
display: grid;
grid-auto-flow:columns;
grid-auto-columns:1fr;
counter-reset: divs;
margin: 5px;
border:1px solid;
}

.box div {
border:1px solid red;
}

.box div:before {
counter-increment: divs;
content: counter(divs);
}

.box div:nth-child(1):nth-last-child(2) ~ *,
.box div:nth-child(2):nth-last-child(3) ~ *,
.box div:nth-child(3):nth-last-child(4) ~ *,
.box div:nth-child(4):nth-last-child(5) ~ *,
.box div:nth-child(5):nth-last-child(6) ~ *,
.box div:nth-child(6):nth-last-child(7) ~ *
/*
.box div:nth-child(N):nth-last-child(N+1) ~ *
*/{
grid-row:2;
}
<div class="box">
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>

Dynamic CSS grid with template-columns: auto and full-width row

Your header rule sets row to 1, and this limits your grid layout. You need at least 3 rows in order to have one for the header, and two for the full-width element.

  1. Start by removing the header rule completely.

  2. Then add grid-template-rows to the wrapper.

  3. Change your wrapper to use repeat(), this makes the horizontal span work as expected:

.wrapper {
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(4, minmax(0, 1fr));
background-color: #fff;
color: #444;
}

  1. In order to make full-width span 2 rows you need at least 3 rows in total. Plus you need to declare your grid-row span like this:
.wrapper {
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(4, minmax(0, 1fr));
background-color: #fff;
color: #444;
grid-template-rows: repeat(3, minmax(0, 1fr));
}

.full-width {
grid-column: 1 / -1;
grid-row: span 2 / span 2;
}

  1. If you want an empty node at the top left corner of the grid, we need to increase the amount of columns to 5. We also need an extra class that pushes the first grid child one column towards the right (the other children will be pushed along):
.wrapper {
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(5, minmax(0, 1fr));
background-color: #fff;
color: #444;
grid-template-rows: repeat(3, minmax(0, 1fr));
}

.wrapper:first-child {
grid-column-start: 2;
}

CSS grid with dynamic number of rows and adjustable width

Make only the red container resizable with an initial width equal to 50% and use flex-grow:1 on the green container

function addCell(color) {
let container = document.getElementById(color + "-container");

let cell = document.createElement("div");
cell.innerText = color;
container.appendChild(cell).className = "container-item";
}
#container {
border: 1px solid #ddd;
display: flex;
gap: 10px;
padding: 10px;
height: 50vh;
resize: vertical;
overflow: auto;
}

#green-container,
#red-container {
display: grid;
overflow: auto;
}
#red-container {
border: 1px solid red;
width: 50%;
resize: horizontal;
}
#green-container {
border: 1px solid green;
flex-grow: 1;
}

.container-item {
padding: 10px;
border: 1px solid black;
}
<button onclick="addCell('red')">add red cell</button>
<button onclick="addCell('green')">add green cell</button>

<div id="container">
<div id="red-container"></div>
<div id="green-container"></div>
</div>

First row a finite size, variable remaining rows equal portions of remaining space?

Since only the first row is definite, you don't need to use grid-template-rows to define all rows that may exist in the grid.

The first row can exist in the explicit grid (sized by grid-template-rows).

The remaining rows can exist in the implicit grid (sized by grid-auto-rows).

Instead of this:

.grid-container {
display: grid;
grid-template-rows: 20px repeat(auto-fill, 1fr);
}

Try this:

.grid-container {
display: grid;
grid-template-rows: 20px;
grid-auto-rows: 1fr; /* also try `auto` (which is the default) */
}

Also see: Equal height rows in CSS Grid Layout

Is it possible to auto-fill when aligning one item on the top right side with Flexbox or CSS Grid?

You could set grid-row-end to a ridiculously high number, so that the comment section will always cover enough rows for the left side. This works as long as you have no row-gap because if you do have row-gap, there would be lots of gaps created between all the rows created from the high grid-row-end value.

.container {
display: grid;
}
@media (min-width: 20rem) {
.container {
grid-template-columns: repeat(2, 1fr);
}
}

.row {
outline: 1px solid green;
grid-column-start: 1;
}

.comments {
outline: 1px solid red;
height: 15rem;
grid-column-start: 2;
grid-row-start: 1;
grid-row-end: 999;
}
<div class="container">
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="comments">Comments</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
</div>

How does grid-row: 1 / -1 actually work? (references needed)

grid-row:1/-1 means grid-row-start:1 and grid-row-end:-1 and if you check the specification you can read:

Numeric indexes in the grid-placement properties count from the edges of the explicit grid. Positive indexes count from the start side (starting from 1 for the start-most explicit line), while negative indexes count from the end side (starting from -1 for the end-most explicit line).

The trick is the explicit grid. In your case you didn't define any explicit rows and your elements will be placed automatically generating new rows we call the implicit grid

The grid-template-rows, grid-template-columns, and grid-template-areas properties define a fixed number of tracks that form the explicit grid. When grid items are positioned outside of these bounds, the grid container generates implicit grid tracks by adding implicit grid lines to the grid. These lines together with the explicit grid form the implicit grid.

and

...If these properties don’t define any explicit tracks the explicit grid still contains one grid line in each axis.

So grid-row:1/-1 will not consider the grid structure after placing all the elements but will consider the intial definition of the grid before placing any element and this grid contain 2 columns (defined by grid-template-columns: max-content 1fr) and 0 rows.

Related: How to span to the last column in an implicit grid?



Related Topics



Leave a reply



Submit