CSS Grid Item Placement - css

I was trying to learn how to change the order of columns on a very simple grid (grid-template-columns: 1fr 1fr). The answer was quite simple: add the following rule to the second item: grid-row-start: 1; /* grid-row: 1; shortcut also works */.
_________ _________
| A | B | --> | B | A |
--------- ---------
But I can't understand why this works. I'm trying to change the order of columns but I'm using row rules? The second item is already in the first row. What changed after adding that rule?
I tried to make sense of it. I reset the grid and added grid-column-start: 1; to the second item. I ended up with this grid:
_________
| A | | #B { grid-column-start: 1; }
---------
| B | |
---------
After that, I added grid-column-start: 2; to the first item and the result was:
_________
| | A | #A { grid-column-start: 2; }
--------- #B { grid-column-start: 1; }
| B | |
---------
Now, I have to tell the second item to start at the first row: grid-row-start: 1; and I finally got what I wanted.
_________
| B | A | #A { grid-column-start: 2; }
--------- #B { grid-column-start: 1; grid-row-start: 1; }
But the thing is, I can keep only grid-row-start: 1; and achieve the same result! Why?!
body {
background: lightgoldenrodyellow;
}
#content {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
h2 {
text-align: center;
}
.grid {
display: grid;
width: 480px;
height: 240px;
margin: 4rem auto;
grid-template-columns: 1fr 1fr;
gap: 5px;
font-size: 3rem;
}
.column {
border: black solid 1px;
}
.colorA {
background: turquoise;
text-align: center;
}
.colorB {
background: aliceblue;
text-align: center;
}
.column3 {
/* does nothing */
display: block;
}
.column4 {
grid-row-start: 1;
}
.column5 {
/* does nothing */
display: block;
}
.column6 {
grid-column-start: 1;
}
<div id="content">
<div>
<h2>Original</h2>
<div class="grid">
<div class="column colorA column1">A</div>
<div class="column colorB column2">B</div>
</div>
</div>
<div>
<h2>Using grid-row-start</h2>
<div class="grid">
<div class="column colorA column3">A</div>
<div class="column colorB column4">B</div>
</div>
</div>
<div>
<h2>Using grid-column-start</h2>
<div class="grid">
<div class="column colorA column5">A</div>
<div class="column colorB column6">B</div>
</div>
</div>
</div>

You need to follow the placement algorithm to understand this:
The item A will fall into this rule:
Position the remaining grid items.
The auto-placement cursor defines the current “insertion point” in the grid, specified as a pair of row and column grid lines. Initially the auto-placement cursor is set to the start-most row and column lines in the implicit grid.
then
If the item has an automatic grid position in both axes:
Increment the column position of the auto-placement cursor until either this item’s grid area does not overlap any occupied grid cells, or the cursor’s column position, plus the item’s column span, overflow the number of columns in the implicit grid, as determined earlier in this algorithm.
So basically, the B is placed at the first column/first row then our cursor will move along the same row trying to find an empty column to place the A and the second column is an empty one.
What you described is not correct because you assumed that the A will be placed before B which is not the case. We first place items having an explicit position then we place the remaining so if you set #B { grid-row-start: 1; } then B will be placed first.
B will consider the (2)
Process the items locked to a given row.
while A will consider (4) like explained previously
If you specify #B { grid-column-start: 1; } then both will fall under (4) and in such case A will be placed first (it's the first one in the DOM) and will logically get the first row/first column then B will be placed at first column but since the first row is filled by A then it will move to second row
If both have grid-column-start specified they are still considered inside (4) and we place A then B but here you need to make the difference between "sparse" and "dense". The first one will always increment the cursor (that's why you will have empty spaces) while the second one will always reset the cursor to make sure to fill as many areas as possible.
Similar questions:
CSS Grid : How Auto placement algorithm works
Using CSS GRID Why Im getting this gap?
How works CSS grid-autoflow?

Related

Implicit grid-area doesn't work as expected

given this html
<div class="parent">
<div class="first">1</div>
<div class="second">2</div>
<div class="third">3</div>
<div class="fourth">4</div>
</div>
and this css
.parent {
display: grid;
grid-template-areas:
'first second'
'third third'
'fourth fourth'
}
I expected the grid area "third" and "fourth" to implicitly have 1 column, which would render like so
I understand I can fix this by specifying grid-area, curious if theres another approach?
https://jsfiddle.net/qgdh2b8a/2/
This isn't a completely different approach, but you could use grid-column and not use grid-template-areas entirely. This solution also uses grid-template-columns.
.parent {
display: grid;
grid-gap: 3px;
/* Defines two columns */
grid-template-columns: 1fr 1fr;
}
.parent > div {
/* Visibility and styling */
background-color: black;
color: white;
padding: 1rem;
text-align: center;
font-family: sans-serif;
}
.third, .fourth {
/* Sets the column that the item should span */
grid-column: 1 / -1;
}
<div class="parent">
<div class="first">1</div>
<div class="second">2</div>
<div class="third">3</div>
<div class="fourth">4</div>
</div>
Here, the third and fourth classes have this style applied: grid-column: 1 / -1. The slash specifies the amount of columns that the element should span. 1 is the first column and -1 is the first from last (the last column).
Examples:
grid-column: 3 Sets the grid column to 3.
grid-column: 1 / 3 Sets the element to span columns 1 through 3.
grid-column: 1 / -2 Sets the element to span columns 1 through the 2nd last one.
You can use grid-column.
.first {
gird-column: 1 / 3; //like this
}
This tells the grid to start from the beginning of the first column, till the beginning of the third column.
The same can work for grid-rows.
You can read more here: grid-column

How to prevent CSS Flex / Grid layouts to be reversed on dir="RTL"

I have a simple div (grid layout but the same would apply to flexbox), which shows three different text elements.
The problem is that whenever my html's dir attribute is set to rtl, the order of the grid items changes, and I'd like to be able to prevent that change in that specific case (where content order is not based on writing direction, but for semantic value.
For example's sake, here's what my items look like:
let direction = 'rtl';
document.getElementById('toggle-direction').addEventListener('click', () => {
direction = direction === 'rtl' ? 'ltr' : 'rtl';
document.documentElement.setAttribute('dir', direction);
});
.container {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: repeat(3, 1fr);
border-radius: 5px;
padding: 10px;
}
.left-arrow {
grid-column: 1 / 2;
grid-row: 1 / 2;
margin-inline-end: auto;
}
.center-content {
grid-row: 1 / 2;
grid-column: 2 / 3;
text-align: center;
}
.right-arrow {
grid-row: 1 / 2;
grid-column: 3 / 4;
margin-inline-start: auto;
}
#toggle-direction {
grid-row: 2 / 3;
grid-column: 1 / 4;
}
<div class="container">
<span class="left-arrow"><</span>
<span class="center-content">Center content</span>
<span class="right-arrow">></span>
<button id="toggle-direction">Toggle direction</button>
</div>
PS: I could add direction: ltr; to the .container selector, but that would create an undesired styling, since I want to use inline-oriented styling.
Is my understanding you only want the arrows to keep their orientation.
So you should isolate and wrap the elements you want the direction to consistent in containers with consistent flow direction (and put everything you want to update outside that containers) like so :
let direction = 'rtl';
document.getElementById('toggle-direction').addEventListener('click', () => {
direction = direction === 'rtl' ? 'ltr' : 'rtl';
document.documentElement.setAttribute('dir', direction);
});
.container {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: repeat(3, 1fr);
}
.ltr {
direction: ltr;
}
.left-arrow {
grid-column: 1 / 2;
text-align: center;
}
.center-content {
grid-column: 2 / 3;
text-align: center;
}
.right-arrow {
grid-column: 3 / 4;
text-align: center;
}
#toggle-direction {
grid-column: 1 / 4;
}
<!-- inherit direction flow -->
<div class="container ltr">
<!-- ltr direction flow -->
<p class="left-arrow">Left arrow</p>
<p class="center-content">Center content</p>
<p class="right-arrow">Right arrow</p>
</div>
<div class="container">
<!-- inherit direction flow -->
<button id="toggle-direction">Toggle direction</button>
</div>
Edits done: wrapped the first row you had inside the container in another container (class="colum rtl") that always has a direction.
Small css updates to keep the view.
Alternatively you could place the arrows container inside the container with the button. But in order to have consistent rtl direction you will need to wrap them there in a container (with class="rtl") and you need a little more updates to your css to keep the aspect.
Alternatively if there is really no way for you to isolate all the components you need to have consistent direction you could for some specific cases create a class class="inherit-direction" (for example) and in your js, when you change the direction of the document, change the containers direction with that class as well (document.getElementsByClassName("inherit-direction") and iterate trough them).
But I would strongly advise to use this last option only as last measure and limit the number of uses as much as possible.

In CSS grid layout, do we count the span even starting on implicit grid lines?

This question is about whether CSS grid "span counting" -- whether it should start on implicit grid line. In CSS Definitive Guide, 4th Ed, p. 695, it is said that
box4 is where things really get interesting. It ends on the fifth row line, which is to say the second implicit grid line. It spans back three lines—and yet, it still starts on the same grid line as box3. This happens because spans have to start counting within the explicit grid. Once they start, they can continue on into the implicit grid (as happened with box2), but they cannot start counting within the implicit grid.
Is it true? Or has there been a spec change? Can "span counting" start within implicit grid?
It is a bit confusing as the code in the book used:
.box04 {grid-column: 4; grid-row: span 2 / 5;}
while the online code on Github.com used:
.box04 {grid-column: 4; grid-row: span 4 / 5;}
(so I do see it start counting at row grid line 5, and start counting 4 steps back starting at this implicit grid line 5, and the rule is "we shouldn't start counting at implicit grid lines". So has the rule changed or the rule doesn't mean it that way?).
and in order to have the result as on the book, it needs to be 4 / 5 instead of 2 / 5, and the text in the book said "spans back three lines" -- shouldn't it be span back 4 or 2 lines? If we can't count implicit grid, then really it should be 2 / 5, but if we can count implicit grid, then it should be 4 / 5. So was there a spec change? And the "three lines" probably is a typo? So if we need to span 4, then that probably means we count starting on either implicit or explicit grid line?
The code is a bit long but we can just look at box04:
html {
background: #DDD;
}
body {
padding: 2em;
margin: 0;
box-sizing: border-box;
background: white;
}
ul.grid {
padding: 0;
margin: 0;
}
.grid.boxed {
border: 1px solid black;
}
.grid.boxed.lines {
padding: 1px 0 0 1px;
}
.grid.small *[class^="box"] {
font-size: 1em;
font-weight: normal;
padding: 0.25em;
border-width: 0.167em;
}
*[class^="box"] {
border: 0.33em solid;
font: bold 2em Arvo, sans-serif;
display: flex;
align-items: center;
justify-content: center;
}
*[class^="box"][class*="01"] {
color: rgb(255, 0, 0);
background: rgba(255, 0, 0, 0.1);
}
*[class^="box"][class*="02"] {
color: rgb(255, 128, 0);
background: rgba(255, 128, 0, 0.15);
}
*[class^="box"][class*="03"] {
color: rgb(216, 168, 0);
background: rgba(216, 168, 0, 0.2);
}
*[class^="box"][class*="04"] {
color: rgb(0, 128, 0);
background: rgba(0, 128, 0, 0.1);
}
*[class^="box"][class*="05"] {
color: rgb(0, 0, 255);
background: rgba(0, 0, 255, 0.1);
}
*[class^="box"][class*="06"] {
color: rgb(128, 0, 128);
background: rgba(128, 0, 128, 0.1);
}
span[class*="gridline"] {
border: 1px dashed;
margin: -1px 0 0 -1px;
}
/* for print preview/production
body:hover {filter: saturate(0%);}
*/
#grid {
grid-auto-rows: 2em;
grid-auto-columns: 5em;
width: 35em;
}
#grid {
display: grid;
grid-template-rows: 2em 2em;
grid-template-columns: repeat(6, 4em);
}
.box01 {
grid-column: 1;
grid-row: 1 / 4;
}
.box02 {
grid-column: 2;
grid-row: 3 / span 2;
}
.box03 {
grid-column: 3;
grid-row: span 2 / 3;
}
.box04 {
grid-column: 4;
grid-row: span 4 / 5;
}
.box05 {
grid-column: 5;
grid-row: span 6 / 5;
}
.box06 {
grid-column: 6;
grid-row: -1 / span 3;
}
.box07 {
grid-column: 7;
grid-row: span 3 / -1;
}
span[class*="box"] {
z-index: 1;
}
span.explicit {
background: #DDD;
grid-area: 1 / 1 / 3 / 7;
}
<div class="grid gridlines" id="grid">
<span class="box01">1</span>
<span class="box02">2</span>
<span class="box03">3</span>
<span class="box04">4</span>
<span class="box05">5</span>
<span class="box06">6</span>
<span class="box07">7</span>
<span class="explicit"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
<span class="gridlines"></span>
</div>
Can "span counting" start within implicit grid?
No, we start considering the explicit grid and never the implicit grid. Focus on the word start which is the most important because the explicit grid is always the reference and the implicit grid is only the result of extra column/row created in order to contain everything.
As I explained here we cannot consider the implicit grid because we will easily run into undefined behaviors.
Starting considering the explicit grid doesn't necessarily mean that we will be inside the explicit grid. Read until the end to understand this part.
From the specification:
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).
And
When grid items are positioned outside of these bounds, the grid container generates implicit grid tracks by adding implicit grid lines to the grid... ref
Let's have some examples to better understand.
Here is a grid with 3 explicit rows (4 lines) and no implicit row created:
.box {
display:grid;
grid-gap:5px;
grid-template-rows:repeat(3,50px);
grid-template-columns:repeat(3,1fr);
grid-auto-rows:200px; /* This wil never be used */
border:1px solid;
}
.a {
grid-row:span 2/3;
background:red;
}
.b {
grid-row:span 2/4;
background:red;
}
.c {
grid-row:1/4;
background:red;
}
<div class="box">
<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
</div>
The .a ends at line 3 and span back 2 rows. Same for .b but ending at line 4. For .c we start from 1 to 4.
Let's create an implicit grid by making the .c until 5
.box {
display:grid;
grid-gap:5px;
grid-template-rows:repeat(3,50px);
grid-template-columns:repeat(3,1fr);
grid-auto-rows:200px; /* This is used for the implicit row */
border:1px solid;
}
.a {
grid-row:span 2/3;
background:red;
}
.b {
grid-row:span 2/4;
background:red;
}
.c {
grid-row:1/5;
background:red;
}
<div class="box">
<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
</div>
Note how the .a and .b didn't move which is trivial in this case because we added a line at the bottom so will not see any trick around the explicit grid.
Now let's add an implicit grid at the beginning:
.box {
display:grid;
grid-gap:5px;
grid-template-rows:repeat(3,50px);
grid-template-columns:repeat(3,1fr);
grid-auto-rows:200px; /* This is used for the implicit row */
border:1px solid;
}
.a {
grid-row:span 2/3;
background:red;
}
.b {
grid-row:span 2/4;
background:red;
}
.c {
grid-row:span 4/4;
background:red;
}
<div class="box">
<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
</div>
Now it's intresting because we can see that .a and .b are no more starting from 3 and 4 considering the implicit grid but they do inside the explicit one!
For .c we start at the line 4 but we need 4 rows and our grid contain only 3 so we generate implicit rows and we continue the count there.
Basically, we have the follwing steps:
We have the explicit grid
We place items inside the explicit grid (we start the count). Until now there is no implicit grid
we start counting and if we reach the edges and there is no more rows we add new ones. The implicit grid is created.
Another example:
.box {
display:grid;
grid-gap:5px;
grid-template-rows:repeat(3,50px);
grid-template-columns:repeat(3,1fr);
grid-auto-rows:10px; /* This is used for the implicit row */
border:1px solid;
}
.a {
grid-row: 2/span 6;
background:red;
}
.b {
grid-row:span 2/4;
background:red;
}
.c {
grid-row:span 4/4;
background:red;
}
<div class="box">
<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
</div>
If you inspect the code you can easily visualize the explicit grid (50px height for rows) and the implicit ones (20px height for rows). We start the count considering the explicit grid and we may end in the implicit one.
In the above, we always considered the starting (or ending) to be a number that we can easily identify inside the explicit grid and only the span is larger. Let's now consider the case where the starting (or ending) is also a bigger number than the lines defined in the explicit grid.
.box {
display:grid;
grid-gap:5px;
grid-template-rows:repeat(3,50px);
grid-template-columns:repeat(3,1fr);
grid-auto-rows:10px; /* This is used for the implicit row */
border:1px solid;
}
.a {
grid-row: 1/span 2;
background:red;
}
.b {
grid-row:span 10/8;
background:red;
}
.c {
grid-row:span 2/4;
background:red;
}
<div class="box">
<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
</div>
In this case, we defined the .b to end at 8 and span back 10 but there is no line number 8 in the explicit grid. We will then create extra implicit lines in order to have the line 8 and we will count from there and go back 10 rows.
So we started the count from the implicit grid!?
Yes and no. We used the explicit grid to count the lines in order to identify the line 8 (so our reference is always the explicit grid) then when we reach the edge we added more lines to reach 8 then from that point we went back and we created more implicit lines at the beginning.
If we consider the final result of the implicit grid, we cannot say that we started the count from the implicit grid (you can clearly see that it's not the line number 8 in the implicit grid but the last one, the line number 11) but if we consider the fact that the line 8 was created inside the implicit grid then we can say that our starting point is an implicit grid line.
Our reference is always the explicit grid in order to identify the starting/ending line but we may end having our line as an implicit grid line and not an explicit grid line.
box4 is where things really get interesting. It ends on the fifth row line, which is to say the second implicit grid line. It spans back three lines—and yet, it still starts on the same grid line as box3. This happens because spans have to start counting within the explicit grid. Once they start, they can continue on into the implicit grid (as happened with box2), but they cannot start counting within the implicit grid.
the book said "spans back three lines" -- shouldn't it be span back 4 or 2 lines?
Here it's a matter of language. From the specification:
grid span
How many grid tracks the grid item occupies in each axis
Then
Grid track is a generic term for a grid column or grid row—in other words, it is the space between two adjacent grid lines.
So span 4 means 4 rows or 4 columns which is clear but if we try to use this with lines it can be ambiguous because we will have 5 lines involved and our element will cross only 3 of them and we have 2 at the edges. Maybe the book is refering to the 3 middle lines and omitting the ones at the edges when saying "spans back three lines". In other words, the element is overlapping (spanning back) only 3 lines.

Css Sorting and categorization of random data

Say you have the following data points that are dumped from a database or file, the size of each item representing the amount of something (hours, pizzas, whatever)...
And you would like to organize the data into categories like so using CSS...
How would you do it?
I tried the grid shepard method using grid-rowbut get this instead...
Thanks!
P.S. follow up question... My intuition is that CSS would be faster than JS (...moving each item into the appropriate flexbox that could then sort row members horizontally). Is that true? Is CSS likely to be faster in a dataset of possibly thousands of elements?
You can use the flexbox with order.
This is a guide for the flexbox: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
check this sample:
.wrapper {
display: flex;
flex-flow: row wrap;
}
.wrapper > * {
padding: 10px;
flex: 1 0 100%;
}
.o-1,.o-3,.o-5,.o-7 {
background: gold;
}
.o-2,.o-4,.o-6 {
background: hotpink;
}
.o-1 { order: 1; }
.o-2 { order: 2; }
.o-3 { order: 3; }
.o-4 { order: 4; }
.o-5 { order: 5; }
.o-6 { order: 6; }
.o-7 { order: 7; }
<div class="wrapper">
<aside class="o-1">1</aside>
<aside class="o-3">3</aside>
<aside class="o-2">2</aside>
<aside class="o-4">4</aside>
<aside class="o-7">7</aside>
<aside class="o-5">5</aside>
<aside class="o-6">6</aside>
</div>

CSS Grid auto placement in IE/EDGE

While using the old CSS grid spec that is supported by IE 11 and EDGE. Is it possible for the grid items to be auto placed like the current spec?
i.e. to not have to define the column on a grid item:
.item:nth-child(1) {
-ms-grid-column: 1;
}
.item:nth-child(2) {
-ms-grid-column: 2;
}
.item:nth-child(n) {
-ms-grid-column: n;
}
https://codepen.io/JoeHastings/pen/mMPoqB
The answer is NO (unfortunately).
Old specs section about auto-placement has such preamble
This section describes early thinking around automatic placement of Grid Items. Multiple algorithms are possible for such a feature. One is proposed here.
Run this code in IE/Edge and you'll see a lot of rows with 1 in console because IE/Edge stacks all grid items in first cell and you can't force IE/Edge to place grid items automatically. Setting -ms-grid-column and -ms-grid-row to auto won't change anything, because this value is not supported (as you can see in MSDN links). Demo:
var gridItems = document.querySelectorAll(".grid__item");
for (var i = 0; i < gridItems.length; i++) {
var gridItem = gridItems[i];
console.log(window.getComputedStyle(gridItem)["-ms-grid-row"]);
console.log(window.getComputedStyle(gridItem)["-ms-grid-column"]);
}
.grid {
display: -ms-grid;
-ms-grid-columns: 100px 100px 100px;
-ms-grid-rows: 100px 100px 100px;
}
.grid__item {
-ms-grid-row: auto;
-ms-grid-column: auto;
background-color: tomato;
color: white;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
}
<div class="grid">
<div class="grid__item">One</div>
<div class="grid__item">Two</div>
<div class="grid__item">Three</div>
<div class="grid__item">Four</div>
<div class="grid__item">Five</div>
<div class="grid__item">Six</div>
<div class="grid__item">Seven</div>
<div class="grid__item">Eight</div>
<div class="grid__item">Nine</div>
</div>

Resources