I have a CSS grid with several columns and many rows (I'm building a timetable view). The rows and columns are defined on the grid element itself, and then on the elements within the grid I set their column (always only one column) and their rows (might be more than one row).
An example is as follows:
.grid {
display: grid;
grid-template-rows: [row-a] 1fr [row-b] 1fr [row-c] 1fr [row-d] 1fr;
grid-template-columns: [col] 1fr;
flex-grow: 1;
}
.entry-one {
grid-column: col;
grid-row: row-a/row-d;
background-color: red;
}
.entry-two {
grid-column: col;
grid-row: row-b;
background-color: green;
}
<div class='grid'>
<div class='entry-one'>
Foobar
</div>
<div class='entry-two'>
Barfoo
</div>
</div>
Now, what I would like to have is that the elements resize themselves and flow nicely, such that they fit next to each other. I can mock this using width and margin on the elements:
.grid {
display: grid;
grid-template-rows: [row-a] 1fr [row-b] 1fr [row-c] 1fr [row-d] 1fr;
grid-template-columns: [col] 1fr;
flex-grow: 1;
}
.entry-one {
grid-column: col;
grid-row: row-a/row-d;
background-color: red;
width: 50%; /* ADDED */
}
.entry-two {
grid-column: col;
grid-row: row-b;
background-color: green;
width: 50%; /* ADDED */
margin-left: 50%; /* ADDED */
}
<div class='grid'>
<div class='entry-one'>
Foobar
</div>
<div class='entry-two'>
Barfoo
</div>
</div>
However this is not optimal, especially as the elements are inserted dynamically. Is there a way to have the elements size & align themselves automatically using CSS? I've tried to use display: flex on the entries, but that did not result in what I want (or maybe I forgot to add another rule).
Thank you for any ideas, and have a nice day!
I made this to see if that is what you are looking for
.grid{
display: flex;
grid-template-rows: [row-a] 1fr [row-b] 1fr [row-c] 1fr [row-d] 1fr;
grid-template-columns: [col] 1fr;
flex-grow: 1;
}
I just changed your display to flex and delete your margin-left: 50%; on the entry two, hope it is what you are looking for
The markup below works as expected on firefox (paragraph is 980px), but not on chrome (paragraph is 979.94px). Does anyone know how to prevent this from happening?
codepen example: https://codepen.io/isaacsgraphic/pen/jOVrrMO
body {
width: 980px;
background: #99c;
margin: 0 auto;
}
.row {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-column-gap: 20px;
background: white;
}
.content-wrap {
grid-column: span 12;
width: 100%
}
<div class="row">
<div class="content-wrap">
<p>Example paragraph which is 979.94px, not 980 like the row it's within</p>
</div>
</div>
here's a longer example which shows how this problem breaks my column layout on chrome: https://codepen.io/isaacsgraphic/pen/yLVJGqp
It seems that this is a rounding error in chrome. It is possible to recreate the problem with any layout which creates a recurring number sub-pixel fraction for column widths (like 3 or 12 column layouts with 980px), whereas numbers of columns which divide the width easily like 5 or 10 work without issues. The same issues can be caused by grid-column-gap numbers which don't divide nicely.
The simplest fix is to make sure the layout pixel width divides neatly into 12 (6 and 3 work too actually, since chrome can handle one or two decimal places)
body {
width: 984px;
background: #99c;
margin: 0 auto;
}
.row {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-column-gap: 18px;
background: white;
}
.content-wrap {
grid-column: span 12;
width: 100%
}
<div class="row">
<div class="content-wrap">
<p>Example paragraph which is fits the full width of the row it's within</p>
</div>
</div>
Maybe it is because of the different fonts different browsers use?
I used a CSS Grid Layout to display data in a table-like manner with the last column spanning multiple rows. While this basically works now , I absolutely cannot get the vertical alignment right. See the following snippet:
div {
border: 1px solid black;
padding: 3px;
}
.table {
display: grid;
grid-template-columns: min-content min-content min-content;
white-space: nowrap;
grid-auto-rows: auto;
grid-gap: 3px;
width: min-content;
grid-auto-flow: row dense;
}
.col1 {
grid-column-start: 1;
height: min-content;
}
.col2 {
grid-column-start: 2;
height: min-content;
}
.col3 {
grid-column-start: 3;
grid-row-end: span 2;
}
<div class="table">
<div class="col1">A1</div>
<div class="col2">B1</div>
<div class="col1">A2</div>
<div class="col2">B2</div>
<div class="col3">this<br>is<br>the first<br>very<br>very<br>long<br>cell</div>
<div class="col1">A3</div>
<div class="col2">B3</div>
<div class="col1">A4</div>
<div class="col2">B4</div>
<div class="col3">second cell</div>
</div>
I want the left two columns to always the minimum height and be aligned with the top of each multi-row cell. In this simplified example, that means that A2 and B2 should move up and be shown right under A1 and B1.
I also tried adding a dummy <div> before each col3 and make it take all extra height. But no matter what I tried, the height is always distributed equally among all rows.
Is there a way to make the cells top-aligned with the multi-row cell?
I was hoping to use CSS Grid to reverse the apparent order of two side-by-side divs, where one of the divs grows arbitrarily (I don't want to use floats).
I've created a plunkr here: http://plnkr.co/edit/6WZBnHbwhD7Sjx2ovCO7?p=preview
#container {
grid-template-columns: 240px 1fr;
display: grid;
}
.a {
background: yellow;
}
.b {
background: blue;
color: white;
}
#container>.a {
grid-column: 1;
}
#container>.b {
grid-column: 2;
}
#container.reverse>.a {
grid-column: 2;
}
#container.reverse>.b {
grid-column: 1;
}
<div id="container" class="reverse" style="width: 800px;">
<div class="a">A</div>
<div class="b">B</div>
</div>
The crux of it is that when I have the .reverse class applied (so that you should see B | A), B is offset to a new line so it looks more like:
| A
B
If I invert the document ordering of .a with .b, this goes back to normal (but of course, if I drop the .reverse class, I get the same problem).
Why is this, and how can I address?
As the Grid auto-placement algorithm lays out items in the container, it uses next available empty cells (source).
In your source code the A element comes before the B element:
<div id="container" class="reverse" style="width: 800px;">
<div class="a">A</div>
<div class="b">B</div>
</div>
Therefore, the grid container first places A, then uses the next available space to place B.
By default, the auto-placement algorithm looks linearly through the grid without backtracking; if it has to skip some empty spaces to place a larger item, it will not return to fill those spaces. To change this behavior, specify the dense keyword in grid-auto-flow.
http://www.w3.org/TR/css3-grid-layout/#common-uses-auto-placement
grid-auto-flow: dense
One solution to this problem (as you have noted) is to override the default grid-auto-flow: row with grid-auto-flow: dense.
With grid-auto-flow: dense, the Grid auto-placement algorithm will look to back-fill unoccupied cells with items that fit.
#container {
display: grid;
grid-template-columns: 240px 1fr;
grid-auto-flow: dense; /* NEW */
}
7.7. Automatic Placement: the grid-auto-flow
property
Grid items that aren’t explicitly placed are automatically placed into
an unoccupied space in the grid container by the auto-placement
algorithm.
grid-auto-flow controls how the auto-placement algorithm works,
specifying exactly how auto-placed items get flowed into the grid.
dense
If specified, the auto-placement algorithm uses a “dense” packing
algorithm, which attempts to fill in holes earlier in the grid if
smaller items come up later. This may cause items to appear
out-of-order, when doing so would fill in holes left by larger items.
#container {
display: grid;
grid-template-columns: 240px 1fr;
grid-auto-flow: dense; /* NEW */
}
.a {
background: yellow;
}
.b {
background: blue;
color: white;
}
#container>.a {
grid-column: 1;
}
#container>.b {
grid-column: 2;
}
#container.reverse>.a {
grid-column: 2;
}
#container.reverse>.b {
grid-row: 1;
grid-column: 1;
}
<div id="container" class="reverse" style="width: 800px;">
<div class="a">A</div>
<div class="b">B</div>
</div>
grid-row: 1
Another solution would be to simply define the row for the second item.
#container>.b {
grid-column: 2;
grid-row: 1; /* NEW */
}
#container {
display: grid;
grid-template-columns: 240px 1fr;
}
.a {
background: yellow;
}
.b {
background: blue;
color: white;
}
#container>.a {
grid-column: 1;
}
#container>.b {
grid-column: 2;
grid-row: 1; /* NEW */
}
#container.reverse>.a {
grid-column: 2;
}
#container.reverse>.b {
grid-row: 1;
grid-column: 1;
}
<div id="container" class="reverse" style="width: 800px;">
<div class="a">A</div>
<div class="b">B</div>
</div>
The simplest way is to add order: 1 to element B or order: -1 to element A in .reverse
It's also correct CSS rather than hack-y
I'm not sure how to reverse more grid items. But if you have 2 grid items in your grid, you can simply position 2nd grid item using below code.
#container > .b {
grid-column-start: 1;
grid-row-start: 1;
}
I had this same issue just now. I tried auto-row-dense and then set the direction of the container parent to rtl. It worked.
Just this, on the plunker link, seemed to do the trick.
.reverse{
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-auto-flow: dense;
direction: rtl;
}
You can use direction property to reverse a grid x-axis order.
Nested elements will be reversed too so you have to make sure to add additional styles to fix this behavior.
<div class="grid">
<div class="grid-item"><div>
</div>
<style>
.grid { direction : rtl; }
.grid-item { direction : ltr; }
</style>
Edit: this may work but could cause accessibilty issues.
Round peg in square hole
Remember even if you're using fancy 'new' grid features the older flex layout will still work. You can combine them, nest them and sometime you have to admit that certain problems like this may just be better solved with good old
flex-direction: row-reverse
But I know some people will want to downvote me for that so here's another way with grid.
Use named template regions
You can use named template regions and reverse them in the definition.
#container
{
grid-template-areas: a b;
grid-template-rows: 240px 1fr;
display: grid;
}
#container.reverse
{
// note the order is flipped for both these properties
grid-template-areas: b a;
grid-template-rows: 1fr 240px;
}
.a {
grid-area: a;
background: yellow;
}
.b {
grid-area: b;
background: blue;
color: white;
}
Here's an more complex example that uses that technique with media queries
I found out: I need to apply grid-auto-flow: dense; on the container:
#container {
grid-template-columns: 240px 1fr;
display: grid;
grid-auto-flow: dense;
}
According to MDN, this algorithm attempts to fill in holes earlier in the grid.
I want to mention a solution which is also relevant to this question in some cases. When having a multi-row layout, and you want a reversed look of how you grid fills up.
You can play with grid-start combined with some :nth-child & :last-child selectors to achieve a reverse auto flow.
Reversed grid-auto-flow: column
.container{
display: grid;
width: 10rem;
gap: 0.5rem;
grid-template-rows: repeat(2, 1fr);
grid-auto-flow: column; /* => vertical grid*/
}
/* REMOVE THIS TO SEE THE DIFFERENCE */
.pixel:nth-child(odd):last-child { /* reversed auto-flow: column */
grid-row-start: 2;
}
.pixel{
width: 2rem;
height: 2rem;
background: red;
border: 1px solid black
}
<div class="container">
<!-- ADD/REMOVE SOME PIXELS to see the result -->
<div class="pixel"></div>
<div class="pixel"></div>
<div class="pixel"></div>
<div class="pixel"></div>
<div class="pixel"></div>
<div class="pixel"></div>
<div class="pixel"></div>
</div>
Reversed: horizontal & vertical
.container{
display: grid;
width: 10rem;
gap: 0.5rem;
grid-template-rows: repeat(2, 1fr);
grid-auto-flow: column;
direction: rtl; /* reversed horizontal */
}
/* REMOVE THIS TO SEE THE DIFFERENCE */
.pixel:nth-child(odd):last-child { /* reversed vertical */
grid-row-start: 2;
}
.pixel{
width: 2rem;
height: 2rem;
background: red;
border: 1px solid black
}
<div class="container">
<!-- ADD/REMOVE SOME PIXELS to see the result -->
<div class="pixel">1</div>
<div class="pixel">2</div>
<div class="pixel">3</div>
<div class="pixel">4</div>
<div class="pixel">5</div>
<div class="pixel">6</div>
<div class="pixel">7</div>
</div>
I found out: I need to apply grid-auto-flow: dense; on the container:
I've been playing around with CSS Grid recently and have noticed something that I can't see to find the answer to. Let's say I split my page out to have 2 columns, and then a row below it, with another column (which spans both columns). On mobile, I'd like them to stack one on top of the other and then go back to layout described above after a certain breakpoint. Here is the markup:
HTML
<div class="grid">
<div class="upper">
<div class="a">A</div>
<div class="b">B</div>
</div>
<div class="lower">
<div class="c">C</div>
</div>
</div>
SCSS
.upper, .lower {
display: grid;
}
.upper {
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto;
background-color:grey;
grid-gap:10px;
#media only screen and (max-width:800px) {
grid-template-columns: 1fr;
grid-template-rows: auto;
}
}
.lower {
grid-template-columns: 1fr;
grid-template-rows:auto;
background-color: green;
grid-gap:10px;
}
I've noticed that on mobile, even though I've defined grid-gap for both of my grid sections, on mobile when the columns stack, the grid-gap is not maintained. So in the fiddle below, when you make the window smaller, you can see that when the columns, stack one on top of the other, the gap between B and C is non existent. Here is the fiddle:
Fiddle
Hope I'm making sense!
EDIT: Bear in mind I'm only testing this in Firefox and Chrome (which support grid).
The grid-gap rule doesn't work between B and C because it doesn't apply.
This rule creates gutters between rows and columns inside a grid container.
But you are declaring grid-gap on .upper and .lower, two siblings in a block container. Their parent (.grid) is not a grid container because it doesn't have display: grid or inline-grid.
Therefore, grid-gap: 10px on .upper is creating a 10px gutter between A and B...
and grid-gap: 10px on .lower is creating a 10px gutter between.... nothing (.lower has only one grid item. grid-gap creates gutters between multiple grid items).
fiddle demo 1
For grid-gap to work among the .upper and .lower siblings you need to apply it to their parent, which must be a grid container.
fiddle demo 2
.grid {
display: grid; /* NEW */
grid-gap: 25px; /* NEW */
}
.upper, .lower {
display: grid;
}
.upper {
grid-template-columns: 1fr 1fr;
grid-gap: 25px;
}
.lower {
grid-template-columns: 1fr;
grid-template-rows: auto;
grid-gap: 10px; /* does nothing unless there are multiple grid items */
}
#media ( max-width:800px ) {
.upper {
grid-template-columns: 1fr;
grid-template-rows: auto;
}
}
.upper > * { border: 1px dashed red; }
.lower > * { border: 1px dashed blue; }
<div class="grid">
<div class="upper">
<div class="a">A</div>
<div class="b">B</div>
</div>
<div class="lower">
<div class="c">C</div>
</div>
</div>