CSS Grid with variable number of auto rows, but one row should take 1fr
Considering your three requirements:
- A grid with a variable number of rows.
- Every row should have a variable size (auto will do).
- 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
orauto-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.
Start by removing the
header
rule completely.Then add grid-template-rows to the wrapper.
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;
}
- 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;
}
- 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
How to Change The Bootstrap Default Font Family Using Font from Google
What Does The Shrink-To-Fit Viewport Meta Attribute Do
Gradient Colors in Internet Explorer
Flexbox: Align Between Bottom and Center
Put Search Icon Near Textbox Using Bootstrap
Django Doesn't Display Newline Character When Rendering Text from Database
How to Get a Flexbox to Have a Center Fixed and a Bottom Fixed Children Together
<A Href> Appends Link to End of Current Url
@Media Queries and Image Swapping
How to Use Custom Fonts in an HTML5 Canvas Element
How to Replace Radio Buttons with Images
Display Text on Mouseover for Image in HTML
Bootstrap 4: Navbar with Logo and 2 Rows
Extra Space When Centering Elements Using Flexbox
How to Translate HTML String to Real HTML Element by Ng-For in Angular 2