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:
Related
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
I am trying out some very basic grids using display: grid. I have a simple grid with one row, divided into 6 columns. In the HTML I have a div containing the grid, then 6 nested divs containing the 6 items, which should display along one row in 6 columns.
However, instead, they stack on top of each other - why?
This is what it looks like when run:
.gridnav {
display: grid;
/*Gap between columns cells*/
grid-column-gap: 15px;
grid-row-gap: 2px;
/*Padding on left and right edges*/
padding: 0px 10px 0px;
/*Centres items vertically and horizontally*/
align-items: center;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-areas: '1 2 3 4 5 6';
}
.navitem1 { grid-area: 1; }
.navitem2 { grid-area: 2; }
.navitem3 { grid-area: 3; }
.navitem4 { grid-area: 4; }
.navitem5 { grid-area: 5; }
.navitem6 { grid-area: 6; }
<div class="gridnav">
<div class="navitem1">1</div>
<div class="navitem2">2</div>
<div class="navitem3">3</div>
<div class="navitem4">4</div>
<div class="navitem5">5</div>
<div class="navitem6">6</div>
</div>
Any ideas most welcome, thanks
I have a simple grid with one row, divided into 6 columns.
Actually, you have the reverse. You have a grid with one column, divided into 6 rows.
The grid-area property is a shorthand property. It breaks down like this:
grid-row-start
grid-column-start
grid-row-end
grid-column-end
Therefore, your code:
.navitem1 { grid-area: 1; }
.navitem2 { grid-area: 2; }
.navitem3 { grid-area: 3; }
.navitem4 { grid-area: 4; }
.navitem5 { grid-area: 5; }
.navitem6 { grid-area: 6; }
Is equivalent to this:
.navitem1 {
grid-row-start: 1;
grid-column-start: auto;
grid-row-end: auto;
grid-column-end: auto;
}
.navitem2 {
grid-row-start: 2;
grid-column-start: auto;
grid-row-end: auto;
grid-column-end: auto;
}
...
So here's what's really happening:
This rule in the container is doing exactly what you expect.
grid-template-areas: '1 2 3 4 5 6'
But then the grid-area rules are overriding grid-template-areas. Above is what it looks like in Chrome dev tools.
As you can see, the items are originally lined up in one row and six columns (as you expect). But in the end, all items line up on six rows in a one column (because of the grid-area overrides).
However, if you use a non-integer value, like this:
.navitem1 { grid-area: a; }
It would translate to this:
.navitem1 {
grid-row-start: a;
grid-column-start: a;
grid-row-end: a;
grid-column-end: a;
}
... which would work for your purposes, as the values of grid-template-areas and grid-area are fully aligned.
Spec reference for the grid-area property.
It seems that grid template areas can't be named integers. Changing them to a, b, c, etc. makes it work.
.gridnav {
display: grid;
/*Gap between columns cells*/
grid-column-gap: 15px;
grid-row-gap: 2px;
/*Padding on left and right edges*/
padding: 0px 10px 0px;
/*Centres items vertically and horizontally*/
align-items: center;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-areas: 'a b c d e f';
}
.navitem1 { grid-area: a; }
.navitem2 { grid-area: b; }
.navitem3 { grid-area: c; }
.navitem4 { grid-area: d; }
.navitem5 { grid-area: e; }
.navitem6 { grid-area: f; }
<div class="gridnav">
<div class="navitem1">1</div>
<div class="navitem2">2</div>
<div class="navitem3">3</div>
<div class="navitem4">4</div>
<div class="navitem5">5</div>
<div class="navitem6">6</div>
</div>
The solution can be achieved without writing these many lines of code. You don't need to use grid-template-area until and unless you want to switch the div positions. Let me take you to the solution real quick.
.gridnav {
width: 90%;
margin: 0 auto;
display: grid;
grid-template-rows: 1fr; /* Optional */
grid-template-columns: repeat(6,1fr);
grid-column-gap: 15px;
grid-row-gap: 2px;
align-items: center;
}
.gridnav div {
border: 2px solid red;
text-align: center;
}
First thing, define some width to the parent container. In your case it is div with class .gridnav. Since you already defined this section as a grid, I would define the rows and columns first. Since you don't need many rows but just one, it is okay not to define row.
For columns, you can have 1fr 1fr 1fr 1fr 1fr 1fr; but I would reduce code to grid-template-columns: repeat(6,1fr);
Further styling you can have as you want. Let me know if you have any further questions regarding this.
For more details you can always visit these two links:
https://mozilladevelopers.github.io/playground/css-grid/02-first-grid/
https://css-tricks.com/snippets/css/complete-guide-grid/
Hope you will find your answer in this. You can see code working here : Hit that Run Code Snippet button
.gridnav {
width: 90%;
margin: 0 auto;
display: grid;
grid-template-rows: 1fr; /* Optional */
grid-template-columns: repeat(6,1fr);
grid-column-gap: 15px;
grid-row-gap: 2px;
align-items: center;
}
.gridnav div {
border: 2px solid red;
text-align: center;
}
<div class="gridnav">
<div class="navitem1">1</div>
<div class="navitem2">2</div>
<div class="navitem3">3</div>
<div class="navitem4">4</div>
<div class="navitem5">5</div>
<div class="navitem6">6</div>
</div>
I want to be able to reverse the order of columns (the 2 small to the left, the big one right). I've tried several solutions but didn't find one that works.
Here's the code:
.images-block-box{
display: grid;
grid-gap: 16px;
grid-template-columns: 708fr 340fr;
& > div:first-child{
grid-row: span 2;
}
&.reverse{
grid-template-columns: 340fr 708fr;
& > div:first-child{
order: 2; // doesn't work (I want to place the first item at the end of the 3)
}
}// reverse
}// images-block-box
Note that I really want to reverse the order of the columns themselves, not just their dimensions.
Simply adjust grid-column and conisder grid-auto-flow:dense; to allow the next elements to be placed before:
.grid {
display: grid;
grid-gap: 16px;
grid-template-columns: 2fr 1fr;
grid-auto-flow:dense;
margin:5px;
}
.grid div {
min-height: 50px;
border: 1px solid;
}
.grid div:first-child {
grid-row: span 2;
}
.grid.reverse {
grid-template-columns: 1fr 2fr;
}
.grid.reverse div:first-child {
grid-column:2;
}
<div class="grid">
<div></div>
<div></div>
<div></div>
</div>
<div class="grid reverse">
<div></div>
<div></div>
<div></div>
</div>
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.ref
Another option is to place the big box to the last column by using grid-column-end: -1 - see demo below:
.images-block-box {
display: grid;
grid-gap: 16px;
grid-template-columns: 708fr 340fr;
grid-template-rows: 100px 100px;
grid-auto-flow: column;
}
.images-block-box>div {
border: 1px solid;
}
.images-block-box>div:first-child {
grid-row: span 2;
}
.images-block-box.reverse {
grid-template-columns: 340fr 708fr;
}
.images-block-box.reverse>div:first-child {
grid-column-end: -1;
}
<div class="images-block-box">
<div></div>
<div></div>
<div></div>
</div>
<br/>
<div class="images-block-box reverse">
<div></div>
<div></div>
<div></div>
</div>
grid-column-end
<integer> && <custom-ident>?
Contributes the nth grid line to the grid
item’s placement. If a negative integer is given, it instead counts in
reverse, starting from the end edge of the explicit grid.
Since there are 2 answers that could be marked as accepted (thanks to #kukkuz and #Temani Afif) I'm posting here a sum up. The working techniques pointed out till now are:
grid-auto-flow: dense (container) + grid-column: 2 (first-child)
grid-auto-flow: column (container) + grid-column-end: -1 (first-child)
The rest of the code remains the same. Please take a look at the related answers.
Both are currently working well (at least in major/modern browsers).
Then Maybe You can use a different approach
.grid {
display: grid;
grid-template-columns: 5fr 2fr;
grid-template-rows: 1fr 1fr;
height: 500px;
grid-gap: 2rem;
}
.one {
background-color: red;
grid-row: 1 / 3;
}
.two {
background-color: green;
}
.three {
background-color: blue;
}
.reverse > .one {
grid-column: 2 / 3;
grid-row: 2 / 3;
}
.reverse > .three {
grid-column: 1 / 2;
grid-row: 1 / 3;
}
<h1>Without Reverse</h1>
<div class="grid">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
<h1>With Reverse</h1>
<div class="grid reverse">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
I have a card built with CSS Grid layout. There might be an image to the left, some text to the right top and maybe a button or a link at the right bottom.
In the code below, how can I make the green area take up as much space as possible and at the same time make the blue area take up as little space as possible?
The green should push the blue area down as far as possible.
https://jsfiddle.net/9nxpvs5m/
.grid {
display: grid;
grid-template-columns: 1fr 3fr;
grid-template-areas:
"one two"
"one three"
}
.one {
background: red;
grid-area: one;
padding: 50px 0;
}
.two {
background: green;
grid-area: two;
}
.three {
background: blue;
grid-area: three;
}
<div class="grid">
<div class="one">
One
</div>
<div class="two">
Two
</div>
<div class="three">
Three
</div>
</div>
Adding grid-template-rows: 1fr min-content; to your .grid will get you exactly what you're after :).
.grid {
display: grid;
grid-template-columns: 1fr 3fr;
grid-template-rows: 1fr min-content;
grid-template-areas:
"one two"
"one three"
}
.one {
background: red;
grid-area: one;
padding: 50px 0;
}
.two {
background: green;
grid-area: two;
}
.three {
background: blue;
grid-area: three;
}
<div class="grid">
<div class="one">
One
</div>
<div class="two">
Two
</div>
<div class="three">
Three
</div>
</div>
Jens edits: For better browser support this can be used instead: grid-template-rows: 1fr auto;, at least in this exact case.
A grid is a series of intersecting rows and columns.
You want the two items in the second column to automatically adjust their row height based on their content height.
That's not how a grid works. Such changes to the row height in the second column would also affect the first column.
If you must use CSS Grid, then what I would do is give the container, let's say, 12 rows, then have items span rows as necessary.
.grid {
display: grid;
grid-template-columns: 1fr 3fr;
grid-template-rows: repeat(12, 15px);
}
.one {
grid-row: 1 / -1;
background: red;
}
.two {
grid-row: span 10;
background: lightgreen;
}
.three {
grid-row: span 2;
background: aqua;
}
.grid > div {
display: flex;
align-items: center;
justify-content: center;
}
<div class="grid">
<div class="one">One</div>
<div class="two">Two</div>
<div class="three">Three</div>
</div>
Otherwise, you can try a flexbox solution.
.grid {
display: flex;
flex-flow: column wrap;
height: 200px;
}
.one {
flex: 0 0 100%;
width: 30%;
background: red;
}
.two {
flex: 1 0 1px;
width: 70%;
background: lightgreen;
}
.three {
background: aqua;
}
.grid>div {
display: flex;
align-items: center;
justify-content: center;
}
<div class="grid">
<div class="one">One</div>
<div class="two">Two</div>
<div class="three">Three</div>
</div>
When using grid, and you have grid template area used, and by chance you gave a particular area a width, you are left with a space grid does automatically.
In this situation, let grid-template-columns be either min-content or max-content, so that it adjusts its position automatically.
A possible approach might be grouping two and three together, and using flexbox:
.grid {
display: grid;
grid-template-columns: 1fr 3fr;
grid-template-areas: "one two"
}
.one {
background: red;
grid-area: one;
padding: 50px 0;
}
.wrap {
grid-area: two;
display: flex;
flex-direction: column;
}
.two {
background: green;
flex: 1;
}
.three {
background: blue;
}
<div class="grid">
<div class="one">
One
</div>
<div class="wrap">
<div class="two">
Two
</div>
<div class="three">
Three
</div>
</div>
</div>
Definitely not the most elegant solution and probably not best practice, but you could always add more lines of
"one two"
before the part where you have
"one three"
so it ends up looking like
.grid {
display: grid;
grid-template-columns: 1fr 3fr;
grid-template-areas:
"one two"
"one two"
"one two"
"one three"
}
Again, pretty sure this is just a work around and there's better solutions out there... But this does work, to be fair.
Just use width: 100% and height: 100% in the CSS class of the item you want to fill the grid. Join a max-width property and a max-height property if you don't want a grid item inside a grid container to grow more than some size.
On small screens i'm trying to get my .image div to slot in between .title and .text divs, something like:
title
image
text
.title and .text are wrapped in a container, this is so on a different screen size (medium up) I can do:
title | image
text | continuation of image element
I've thought about using a flex column layout for small screens, and changing the order of elements, but order doesn't seem to have an effect on a nested child element.
Here's the code for small:
HTML:
<div class="container">
<div class="content">
<div class="title"></div>
<div class="text"></div>
</div>
<div class="image"></div>
</div>
CSS:
.container {
display: flex;
flex-direction: column;
}
.image {
order: 2;
}
.title {
order: 1;
}
.text {
order: 3;
}
For reference my code for medium is (cascading upwards from small):
.container {
flex-direction: row;
}
.content {
flex-basis: 50%;
}
.image {
flex-basis: 50%;
}
You can ordering only sibling elements. Title and text aren't sibling with image. You need wrap title, text and image in general parent block
I have similar case, but for solution CSS grid is used, however there is also another support is recommended: CSS subgrid
For a simple design like even columns or rows it is not that hard to replicate, but if parent container contain more complex grid sizes, then CSS subgrid will be requirement for an only CSS solution.
Here is full demo: https://codepen.io/XCanG/pen/vYaNZPo?editors=1100
HTML I have in the demo have very similar structure with having parent, some container for 2 nested elements and 3rd element.
This is quote from my demo, except I rename class names according to your example:
.container {
display: grid;
grid-template-rows: repeat(3, 1fr);
}
.container .content {
display: grid;
grid-column: 1;
grid-row: 1 / span 3;
grid-template-rows: repeat(3, 1fr); /* Fallback */
grid-template-rows: subgrid; /* Limited support: https://caniuse.com/css-subgrid */
grid-template-columns: subgrid;
}
.container .title {
grid-colimn: 1;
grid-row: 1;
}
.container .text {
grid-column: 1;
grid-row: 3;
}
.container .image {
grid-column: 1;
grid-row: 2;
}
CSS make that nested element follow grid columns and rows, with subgrid support you will have exactly defined part of the grid (otherwise you will need to define fallback). And with grid-column and grid-row it is possible to sort elements.
Grid-template-areas will come in pretty handy in this type of layout as they are not siblings. And grid-template-areas have full browser support.
.container {
display: grid;
grid-template-areas: "title img"
"txt img";
}
.title{grid-area: title;}
.text{grid-area: txt;}
.image{grid-area: img;}
#media(max-width: 800px){
.container{
grid-template-areas: 'title'
'img'
'txt';
}
}
<div class="container">
<div class="content">
<div class="title">title</div>
<div class="text">text</div>
</div>
<div class="image">img</div>
</div>