Interlave two or more rows using CSS Grid - css

I want to create a layout like this:
test
test
test
data-0
data-1
data-2
test
test
test
data-3
data-4
data-5
But the following code produces this:
test
test
test
test
test
test
data-0
data-1
data-2
data-3
data-4
data-5
This example shows the layout for 3 columns, 2*2 rows and 6 items. In practice I want to work with 10 columns, auto*2 rows and a dynamic amount of items. As a bonus, it would be awesome, if the amount of column adjusts based on the available width. The problem seems to be that overflowing elements are just added to the first row with a given name, instead of the first non-full row with a given name.
Current implementation using react:
const items = new Array(6)
.fill(undefined)
.map((e, i) => ({ title: "test", data: "data-" + i }));
const root = (
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(3, auto)",
gridTemplateRows: "repeat(2, [title] auto [data] auto)",
}}
>
{items.map((item, i) => (
<React.Fragment key={i}>
<div style={{ gridRow: "title" }}>{item.title}</div>
<div style={{ gridRow: "data" }}>{item.data}</div>
</React.Fragment>
))}
</div>
);
ReactDOM.render(root, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Right at the end of: Subgrid - CSS - MDN
There is a short video example: Don't Wait to Use Subgrid for Better Card Layouts
The presented card layout can be used without change for the required use case. The grid components will have a row span of 2 (instead of 3).
As of writing this answer, the subgrid feature only works in firefox. The implementation for other major browsers has started. As a workaround, the video suggests using grid-template-rows: ...; before grid-template-rows: subgrid;.
.table {
display: grid;
grid-template-columns: repeat(3, 5em);
grid-template-rows: repeat(2, auto auto);
gap: 1em;
}
.card {
display: grid;
grid-row: span 2;
gap: 0;
/* fallback for browsers that don't support subgrid with following behaviour:
1fr: title gets as much space as it needs
auto: data is assumed to be of fixed size
*/
grid-template-rows: 1fr auto;
/* enable subgrid */
grid-template-rows: subgrid;
}
.title {
background: lightblue;
}
<div class="table">
<div class="card"><div class="title">long<br />title-0</div><div class="data">data-0</div></div>
<div class="card"><div class="title">title-1</div><div class="data">data-1</div></div>
<div class="card"><div class="title">title-2</div><div class="data">data-2</div></div>
<div class="card"><div class="title">title-3</div><div class="data">data-3</div></div>
<div class="card"><div class="title">title-4</div><div class="data">data-4</div></div>
<div class="card"><div class="title">title-5</div><div class="data">data-5</div></div>
</div>

You need some more css classes, you need to set positions using nth-child selector:
.container {
display: grid;
grid-template-columns: repeat(3, 50px);
grid-auto-rows: 30px;
grid-auto-flow: column dense;
}
.title:nth-child(6n+3) {
grid-column: 2;
background-color: lightblue;
}
.data:nth-child(6n+4) {
grid-column: 2;
background-color: lightblue;
}
.title:nth-child(6n+5) {
grid-column: 3;
background-color: lightgreen;
}
.data:nth-child(6n) {
grid-column: 3;
background-color: lightgreen;
}
<div class="container">
<div class="title">title1</div>
<div class="data">data1</div>
<div class="title">title2</div>
<div class="data">data2</div>
<div class="title">title3</div>
<div class="data">data3</div>
<div class="title">title4</div>
<div class="data">data4</div>
<div class="title">title5</div>
<div class="data">data5</div>
<div class="title">title6</div>
<div class="data">data6</div>
<div class="title">title7</div>
<div class="data">data7</div>
<div class="title">title8</div>
<div class="data">data8</div>
<div class="title">title9</div>
<div class="data">data9</div>
<div class="title">title10</div>
<div class="data">data10</div>
<div class="title">title</div>
<div class="data">data</div>
<div class="title">title</div>
<div class="data">data</div>
</div>

Related

CSS grid: place items in "at least column 2"

I have the following HTML:
<div class="grid">
<div class="special-1"></div>
<div class="special-1"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
</div>
.special-1 is always in the first column. The number of .special-2s is variable, generated by user data. So approximately:
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 1fr;
}
.special-1 {
grid-column-start: 1;
}
Now the thing is, I want .special-2 to always be in at least the second column. But it can also show in the third or fourth. Is there anyway I can do this? grid-column-start: 2; doesn't work because it will put all of them in column 2 and none in the other columns. So I basically want grid-column: :not(1) or grid-column: 2 | 3 | 4 if you know what I mean. Any ideas?
You can approximate it like below. However, the order may not be like want.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* only 3 explicit clumns */
grid-auto-columns: 1fr; /* this will size the implicit one */
grid-auto-flow: dense; /* make sure to fill all the tracks */
grid-auto-rows: 100px;
grid-gap: 5px;
}
.special-2 {
background: red;
}
.special-1 {
/* place the #1 outside the explict column
it will create an implicit column at the start
*/
grid-column-end: -4;
background: blue;
}
/* place all the #2 in the implicit columns
pay attention to the order, they can be scrambled
*/
.special-2:nth-child(3n+1) {grid-column:1}
.special-2:nth-child(3n+2) {grid-column:2}
.special-2:nth-child(3n+3) {grid-column:3}
<div class="grid">
<div class="special-1"></div>
<div class="special-1"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
</div>
The above will make only 3 columns if there is no element special-1
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* only 3 explicit clumns */
grid-auto-columns: 1fr; /* this will size the implicit one */
grid-auto-flow: dense; /* make sure to fill all the tracks */
grid-auto-rows: 100px;
grid-gap: 5px;
}
.special-2 {
background: red;
}
.special-1 {
/* place the #1 outside the explict column
it will create an implicit column at the start
*/
grid-column-end: -4;
background: blue;
}
/* place all the #2 in the implicit columns
pay attention to the order, they can be scrambled
*/
.special-2:nth-child(3n+1) {grid-column:1}
.special-2:nth-child(3n+2) {grid-column:2}
.special-2:nth-child(3n+3) {grid-column:3}
<div class="grid">
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
</div>
Use the below if you will always have element special-1
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-flow: dense;
grid-auto-rows: 100px;
grid-gap: 5px;
}
.special-2 {
background: red;
}
.special-1 {
grid-column: 1;
background: blue;
}
.special-2:nth-child(3n+1) {grid-column:2}
.special-2:nth-child(3n+2) {grid-column:3}
.special-2:nth-child(3n+3) {grid-column:4}
<div class="grid">
<div class="special-1"></div>
<div class="special-1"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
</div>
The answer by Temani Afif surely works, but isn't very scalable. In my usecase there's some different column counts for different viewport sizes. I eventually decided to implement this differently. Note: if by the time you read this CSS Grid Level 2 has been released, this could trivially be solved using css subgrid: caniuse
Now for my solution: I changed my html as such:
<div class="grid">
<div class="special-1s"
<div class="special-1"></div>
<div class="special-1"></div>
</div>
<div class="special-2s">
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
</div>
</div>
And my css now looks somewhat like below. I forced the rows to a fixed height, which was okay in my use case and made this solution possible.
.grid {
display: flex;
}
.grid > div {
display: grid;
grid-auto-rows: 100px;
}
.special-1s {
flex: 1;
grid-template-columns: 1fr;
}
.special-2s {
--columns: 2;
flex: var(--columns);
grid-template-columns: repeat(var(--columns), 1fr);
#media (min-width: 1000px) {
--columns: 3;
}
}
By using the flex-property, I can fake consistent column-sizes across the width of .grid. The --columns property allows me to easily define extra media-queries. (I actually have a few more than shown here.)

How to place nested DIVs in a CSS grid with variable row heights?

I need to place 4 div containers in a 2 by 2 matrix. The width of the columns must be equal (and is therefore fixed), while the height of the rows must adapt itself to the content of the cells (and is therefore variable).
This is simple to do as long as the markup structure looks something like this:
<div class="wrapper">
<div class="cell a1">...</div>
<div class="cell a2">...</div>
<div class="cell b1">...</div>
<div class="cell b2">...</div>
</div>
The corresponding CSS would look like this:
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
}
Unfortunately, my markup (which I cannot change easily) contains the cells in a nested markup structure:
<div class="wrapper">
<div class="container">
<div class="cell a1">...</div>
<div class="cell a2">...</div>
</div>
<div class="container">
<div class="cell b1">...</div>
<div class="cell b2">...</div>
</div>
</div>
As long as the height of the two rows can be equal, declaring .container as secondary grid solves the issue. But since the row height must be adjusted according to the cell content, this doesn't work.
Is there a way to place all four div.cell in the same grid defined by div.wrapper, although they are not direct child elements?
What you are looking for is Subgrid, feature currently (December 2021) only tested on Firefox Nightly.
Info about this CSS attribute (from the Mozilla Web Docs page) :
When you add display: grid to a grid container, only the direct children become grid items and can then be placed on the grid that you have created.
You can "nest" grids by making a grid item a grid container. These grids however are independent of the parent grid and of each other, meaning that they do not take their track sizing from the parent grid. This makes it difficult to line nested grid items up with the main grid.
For example, if you use grid-template-columns: subgrid and the nested grid spans three column tracks of the parent, the nested grid will have three column tracks of the same size as the parent grid.
When the feature will be available and supported by multiple browsers this example below will work (I guess):
html, body {
width: 100%;
height: 100%;
margin: 0;
}
.wrapper {
display: grid;
height: 100vh;
width: 100vw;
background: grey;
grid-auto-flow: rows;
grid-template-columns: auto auto;
grid-template-rows: auto auto;
}
.container {
display: grid;
grid-column: 1 / 3;
grid-row: 1 / 3;
grid-template-columns: subgrid;
grid-template-rows: subgrid;
}
.a1{
background-color: blue;
grid-row: 1;
grid-column: 1;
}
.a2{
background-color: yellow;
grid-row: 1;
grid-column: 2;
}
.b1 {
background-color: red;
grid-row: 2;
grid-column: 1;
}
.b2 {
background-color: green;
grid-row: 2;
grid-column: 2;
}
<div class="wrapper">
<div class="container a">
<div class="cell a1">A1</div>
<div class="cell a2">A2</div>
</div>
<div class="container a">
<div class="cell b1">B1</div>
<div class="cell b2">B2</div>
</div>
</div>
And will render something like this :

min-content with justify-self: right?

Is it possible with CSS3 grid to create such a layout?
All of the elements should be as wide as their content. And the last two elements should be right-aligned.
However, the way I've tried does not work. As soon as a column has min-content, the justify-self property doesn't do anything.
.grid {
display: grid;
grid-template-areas: "first second third";
grid-template-columns: min-content min-content min-content;
}
.first {
grid-area: first;
}
.second {
grid-area: second;
justify-self: right; /* doesn't work */
}
.third {
grid-area: third; /* doesn't work */
}
<div class="grid">
<div class="first">first</div>
<div class="second">second</div>
<div class="third">third</div>
</div>
This is only a downsized version of a much more complex layout, therefore:
I'm not looking for workarounds like creating wrappers
Stretching .first is not an option because there will be calculations on that element
The reason I chose grid over flex is because of the gaps
Using flexbox may be more appropriate? note the margin-right on the first element in container 1, and margin-left on the second element in container 2. auto margins are quite powerful in flex containers.
.container-1,
.container-2 {
display: flex;
background: lightgray;
}
.item {
width: max-content;
margin: 1em;
padding: 1em;
background: white;
}
.container-1 .item-1 {
margin-right: auto;
}
.container-2 .item-2 {
margin-left: auto;
}
<div class="container-1">
<div class="item item-1">
item 1
</div>
<div class="item item-2">
item 2
</div>
<div class="item item-3">
item 3
</div>
</div>
<div class="container-2">
<div class="item item-1">
item 1
</div>
<div class="item item-2">
item 2
</div>
<div class="item item-3">
item 3
</div>
</div>

How can I make a responsive css grid (display: grid) that has three equal boxes on the top row and then the rest of the rows are two equal size boxes?

I want to make a responsive css grid that looks like this:
box | box | box
b o x | b o x
b o x | b o x
and then when the screen size gets small enough, all of the columns collapse into a single column with each box on its own row.
Is this possible?
Edit:
For anyone who has this issue, changing the number of columns via media query from 6 to 1 was not working. However, I had the idea to make all of the items span 6 columns at the break point and that worked perfectly. This is an addition to the answer given by lawrence-witt and paulie-d.
By using a SIX column grid
.item {
height: 3em;
border: 2px solid green;
}
.container {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-gap: 4px;
width: 90%;
margin: 1em auto;
}
.item:nth-child(1),
.item:nth-child(2),
.item:nth-child(3) {
grid-column: span 2
}
.item:nth-child(4),
.item:nth-child(5) {
grid-column: span 3;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
After that it's just a matter of adding a media query to collapse the grid to a single column at the appropriate breakpoint,
Here is one way to do it using the nth-child feature. To make the grid responsive you simply add a media query that changes the styling of cells and grid at a certain screen width.
.grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
}
.cell {
border: 1px solid black;
grid-column: span 3;
}
.cell:nth-child(1),
.cell:nth-child(2),
.cell:nth-child(3){
grid-column: span 2;
}
<div class="grid">
<div class="cell">One</div>
<div class="cell">Two</div>
<div class="cell">Three</div>
<div class="cell">Four</div>
<div class="cell">Five</div>
<div class="cell">Six</div>
<div class="cell">Seven</div>
</div>
By using CSS flex layout and CSS media query you can achieve your need. See the below code, if the screen size reduced to 600px then it will change to single column layout.
.container {
display: flex;
width: 100%;
justify-content: space-between;
flex-direction: row;
margin-bottom: 10px;
}
.three-box-row > div {
width: 33%;
height: 50px;
}
.two-box-row > div {
width: 49%;
height: 50px;
}
#media only screen and (max-width: 600px) {
.container {
flex-direction: column;
}
.container > div {
width: 100%;
}
}
<body>
<div class="three-box-row container">
<div style="background-color: red;">Box 1</div>
<div style="background-color: green;">Box 2</div>
<div style="background-color: blue;">Box 3</div>
</div>
<div class="two-box-row container">
<div style="background-color: red;">Box 1</div>
<div style="background-color: green;">Box 2</div>
</div>
<div class="two-box-row container">
<div style="background-color: green;">Box 1</div>
<div style="background-color: blue;">Box 2</div>
</div>
</body>

How to update html with js object values

This might not be a recommended question to ask as i am not supplying code examples, but i am trying to replicate the design in the image below. I am looking for somewhere with an example of the layout so i can copy the HTML and CSS straight from it (4 boxes aligned with 1 big box) can anyone point me in the right direction or what technology i can look at? I have looked at css-grid but i am struggling to find a similar example of what i am looking for?
Thanks!
Try using Bootstrap. This layout will be very easy to develop using Bootstrap's two column layout.
CSS Grid should be able to do the trick. Quick whip up below, you can obviously adjust the column and row gaps as well as add the 100% row at the bottom for the red button in your example... this should be a good starting point for you though.
HTML:
<div class="container">
<div class="Header">
<div class="TopHeader" style="background-color:red;">
</div>
<div class="BottomHeader" style="background-color:blue;">
</div>
</div>
<div class="LeftCol" ">
<div class="Square1 " style="background-color:orange; "></div>
<div class="Square2 " style="background-color:green; "></div>
<div class="Square3 " style="background-color:orange; "></div>
<div class="Square4 " style="background-color:green; "></div>
</div>
<div class="RightCol " style="background-color:yellow; ">
</div>
</div>
CSS:
.TopHeader {
grid-area: topheader;
}
.BottomHeader {
grid-area: bottomheader;
}
.Square1 {
grid-area: square1;
}
.Square2 {
grid-area: square2;
}
.Square3 {
grid-area: square3;
}
.Square4 {
grid-area: square4;
}
.container {
display: grid;
grid-template-columns: 50% 50%;
grid-template-rows: 250px 800px;
align-content: space-around;
grid-template-areas: "header header" "leftcol rightcol";
}
.Header {
grid-area: header;
display: grid;
grid-template-columns: 100%;
grid-template-rows: 50% 50%;
align-content: space-around;
grid-template-areas: "topheader" "bottomheader";
}
.LeftCol {
grid-area: leftcol;
display: grid;
grid-template-columns: 100%;
grid-template-rows: 25% 25% 25% 25%;
grid-template-areas: "square1" "square2" "square3" "square4";
}
.RightCol {
grid-area: rightcol;
}

Resources