Make multiple splits in CSS grid - html

I was wondering if it was possible to use CSS grids to do intermediate splits in columns, is this doable? Below a graphical example.
Thank you very much.
EDIT: I am using display: grid, what I am trying to achieve is something responsive that would put each cell under each other on a mobile device.

I just created the example you need but try to make sure that you assign proper names (instead of col-x or row-y) like navigation or sidebar for example.
I'd recommend to just double the amount of rows and assign the amount of rows twice for the left column. Especially take a look at grid-template-areas in the .grid CSS.
To change the way the grid looks on smaller devices you can apply #media queries on the .grid class to adjust the grid-* attributes.
This sample below is not the shortest nor the smartest solution for that but it's the most visual i guess.
.grid {
height: 200px;
display: grid;
grid-template-columns: 80% auto;
grid-template-rows: repeat(6, 1fr);
grid-template-areas:
"col-1-row-1 col-2-row-1-1"
"col-1-row-1 col-2-row-1-2"
"col-1-row-2 col-2-row-2-1"
"col-1-row-2 col-2-row-2-2"
"col-1-row-3 col-2-row-3-1"
"col-1-row-3 col-2-row-3-2"
}
.col-1-row-1,
.col-1-row-2,
.col-1-row-3,
.col-2-row-1-1,
.col-2-row-1-2,
.col-2-row-2-1,
.col-2-row-2-2,
.col-2-row-3-1,
.col-2-row-3-2 {
justify-self: center;
align-self: center;
}
.col-1-row-1 {
grid-area: col-1-row-1;
}
.col-1-row-2 {
grid-area: col-1-row-2;
}
.col-1-row-3 {
grid-area: col-1-row-3;
}
.col-2-row-1-1 {
grid-area: col-2-row-1-1;
}
.col-2-row-1-2 {
grid-area: col-2-row-1-2;
}
.col-2-row-2-1 {
grid-area: col-2-row-2-1;
}
.col-2-row-2-2 {
grid-area: col-2-row-2-2;
}
.col-2-row-3-1 {
grid-area: col-2-row-3-1;
}
.col-2-row-3-2 {
grid-area: col-2-row-3-2;
}
<div class="grid">
<div class="col-1-row-1">Col 1 Row 1</div>
<div class="col-1-row-2">Col 1 Row 2</div>
<div class="col-1-row-3">Col 1 Row 3</div>
<div class="col-2-row-1-1">Col 2 Row 1.1</div>
<div class="col-2-row-1-2">Col 2 Row 1.2</div>
<div class="col-2-row-2-1">Col 2 Row 2.1</div>
<div class="col-2-row-2-2">Col 2 Row 2.2</div>
<div class="col-2-row-3-1">Col 2 Row 3.1</div>
<div class="col-2-row-3-2">Col 2 Row 3.2</div>
</div>

You can just set a new html element below (for example) column2/row1 and set the style as display: grid; grid-template: auto/auto; align-self: center

Related

CSS Grid - Stretch dynamic number of colums to fit row width

Is it possible to use CSS grid to auto-fit the columns in a row to always take up the whole width?
I know this would be possible if you knew the number of columns, but is it possible with a dynamic number of columns?
Image for reference of what I'd like to achieve.
column example image
This is what I have so far, but you can see that the lower row item doesn't take up all the row width.
.wrapper {
display: grid;
grid-template-columns: 200px 200px;
column-gap: 20px;
}
.grid {
border: solid #FF8181 1px;
display: grid;
grid-template-rows: 40px repeat(8, minmax(0, 1fr));
width: 200px;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
}
.row-item {
background: #FFC555;
border: 1px solid #835600;
width: 100%;
height: 40px;
}
.item-1, .item-1 {
grid-row: 2 / span 1;
}
.item-2 {
grid-row: 6 / span 1;
font-size: 12px;
}
<div class='wrapper'>
<div class='grid'>
<div class='row-item item-1'></div>
<div class='row-item item-1'></div>
<div class='row-item item-2'>I'm too short</div>
</div>
<div class='grid'>
<div class='row-item item-1'></div>
<div class='row-item item-1'></div>
<div class='row-item item-1'></div>
<div class='row-item item-2'>Should be the whole width</div>
</div>
</div>
you can stretch a element in a grid over the whole width by using:
grid-column: 1 / all;
or
grid-column: 1 / -1;
unfortunately does it affect the other elements in the same grid.
A solution like "span last-column" doesnt exist yet, but is already discussed: https://github.com/w3c/csswg-drafts/issues/2402
Maybe they will implement the function soon. Good luck anyways
What I need doesn't exist yet. This does indeed look like the latest update:
A solution like "span last-column" doesnt exist yet, but is already
discussed: https://github.com/w3c/csswg-drafts/issues/2402
I calculated overlapping grid items and rendered items in subgrids based on this answer:
Group multiple overlapping timeblocks that may not have a direct overlap
With js its different.
Find the grid
Get the Style of the Grid
Filter the Style of the Grid for grid-template-columns
Set the Element grid-column to the number of columns
Your code will look like this:
window.addEventListener("DOMContentLoaded", function(){
// find the elemen by the class and safe it as grid
let grid = document.querySelector(".grid")
// sage the style of the element
const gridComputedStyle = window.getComputedStyle(grid);
// get the grid-template-columns style poperty of the element and format them to a useful value
const gridColumnCount = gridComputedStyle.getPropertyValue("grid-template-columns").split(" ").length
// log for debug
console.log(gridColumnCount)
// set the column style of the element to the span of the variable
document.getElementById("item-2").style.gridColumn = "1 / span " + gridColumnCount;
// set the row style because it got overwritten by the line above
document.getElementById("item-2").style.gridRow = "6 / span " + gridColumnCount;
})
note that i have changed your html and css too:
html:
<div class='grid'>
<div class='row-item item-1'></div>
<div class='row-item item-1'></div>
<div class='row-item item-1'></div>
<div class="row-item" id="item-2">whole width</div>
</div>
Just deleted the wrapper for a better overview
CSS:
.grid {
border: solid #FF8181 1px;
display: grid;
grid-template-rows: repeat(8, minmax(0, 1fr));
width: 300px;
grid-template-columns: repeat(auto, minmax(0, 1fr));
}
.row-item {
background: #FFC555;
border: 1px solid #835600;
width: 100%;
height: 40px;
}
.item-1, .item-1 {
grid-row: 2;
}
.item-2 {
grid-row: 5;
font-size: 12px;
}
I had to change the grid-template-value of grid from auto-fit to auto. Otherwise there would be a bug when counting the columns

Divide a grid with an undetermined number of columns

I would like to divide a grid with an undetermined number of columns.
An example:
grid-template-columns: repeat(auto-fill, 90px);
A number of columns are created, but I don't know how many.
I would like to know if there is a way to fill out these columns proportionally.
Let's say:
.whatever-1 {
// would take up 2/3 of the grid columns
}
and
.whatever-2 {
// would take up 1/3 of the grid columns
}
But I am not even close to the answer. Please give me a hand.
This is just an example that I did to help me to explain my problem.
.container {
display: grid;
grid-template-columns: repeat(auto-fill, 90px);
width: 100%;
height: 100vh;
color: white;
}
.whatever-1 {
grid-column: 1 / span 2;
background-color: blue;
}
.whatever-2 {
grid-column: 3 / span 5;
background-color: red;
}
<div class="container">
<div class="whatever-1">Whatever 1</div>
<div class="whatever-2">Whatever 2</div>
</div>
If you want to divide it into 1/3 and 2/3 then it means that the first one will be equal to half the width of the second one thus we have a relation of 2x. In this case simply make one of them span two columns without the need of defining any column template:
.container {
display: grid;
grid-auto-flow:column;
height: 100vh;
color: white;
}
.whatever-1 {
background-color: blue;
}
.whatever-2 {
grid-column: span 2;
background-color: red;
}
<div class="container">
<div class="whatever-1">Whatever 1</div>
<div class="whatever-2">Whatever 2</div>
</div>

Set column to different span dynamically using CSS grid [duplicate]

I know there are similar questions but this is specifically asking how to do this using CSS Grid Layout.
So we have this basic grid setup:
HTML (with sidebar):
<div class="grid">
<div class="content">
<p>content</p>
</div>
<div class="sidebar">
<p>sidebar</p>
</div>
</div>
CSS:
.grid {
display: grid;
grid-template-columns: 1fr 200px;
}
To create a layout that looks something like this:
| content | sidebar |
If the page doesn't have a sidebar though, ie. the html looks like this but with the same CSS:
HTML (no sidebar):
<div class="grid">
<div class="content">
<p>content</p>
</div>
</div>
The page layout looks like this (dashes represent empty space)
| content | ------- |
I know why it does that, the grid column is still defined in the grid-template-columns rule.
I'm just wondering how to tell the grid that if there is no content, then fill the remaining space similar to how flex-grow works for flexbox.
The desired result would look like this if no sidebar is present.
| content |
Don't define the columns explicitly with grid-template-columns.
Make the columns implicit instead and then use grid-auto-columns to define their widths.
This will allow the first column (.content) to consume all space in the row when the second column (.sidebar) doesn't exist.
.grid {
display: grid;
grid-auto-columns: 1fr 200px;
}
.content {
grid-column: 1;
}
.sidebar {
grid-column: 2;
}
.grid > * {
border: 1px dashed red; /* demo only */
}
<p>With side bar:</p>
<div class="grid">
<div class="content">
<p>content</p>
</div>
<div class="sidebar">
<p>sidebar</p>
</div>
</div>
<p>No side bar:</p>
<div class="grid">
<div class="content">
<p>content</p>
</div>
</div>
You can get closer by using content sizing keywords, something like:
.grid {
display: grid;
grid-template-columns: 1fr fit-content(200px);
}
.sidebar {
width: 100%;
}
The fit-content keyword will look at the size of the content and act like max-content until it gets to the value you pass in.
In reality you probably wouldn't need to stick a size on sidebar as the content is likely to dictate a size of at least 200 pixels (for example) but you can play around with this.
I think I know the definitive answer to this question now. The problem with the answers so far is that they don't explain how to handle a sidebar that is on the left side of the main content (mainly because I didn't ask for it in the original question).
<div class="grid">
<nav>
<p>navigation</p>
</nav>
<main>
<p>content</p>
</main>
<aside>
<p>sidebar</p>
</aside>
</div>
You can use this CSS:
.grid {
display: grid;
grid-template-columns: fit-content(200px) 1fr fit-content(200px);
}
nav, aside {
width: 100%;
}
/* ensures that the content will always be placed in the correct column */
nav { grid-column: 1; }
main { grid-column: 2; }
aside { grid-column: 3; }
This is also a good use case for grid-areas
.grid {
display: grid;
grid-template-columns: fit-content(200px) 1fr fit-content(200px);
grid-template-areas: "nav content sidebar";
}
nav, aside {
width: 100%;
}
/* ensures that the content will always be placed in the correct column */
nav { grid-area: nav; }
main { grid-area: content; }
aside { grid-area: sidebar; }
An IE compatible version would look like this:
.grid {
display: -ms-grid;
display: grid;
-ms-grid-columns: auto 1fr auto;
grid-template-columns: auto 1fr auto;
}
nav, aside {
width: 100%; /* Ensures that if the content exists, it takes up max-width */
max-width: 200px; /* Prevents the content exceeding 200px in width */
}
/* ensures that the content will always be placed in the correct column */
nav {
-ms-grid-column: 1;
grid-column: 1;
}
main {
-ms-grid-column: 2;
grid-column: 2;
}
aside {
-ms-grid-column: 3;
grid-column: 3;
}

What am i doing wrong using grid

I am trying to use grid and make one big box and two different on the right side of it but it is all scrambled up and as i inspect it it doesn't even show the pattern i'm aiming for. What could be wrong because i think i declared everything right.
I am trying to make 3 columns and 2 rows fill them with 2 columns 2 rows main box and the right side to take up the third column 1 small different box each row. But instead as i am inspecting it i get 5 columns and 2 rows -.-
.top-container {
display: grid;
grid-template-areas:
"main-box main-box small-box-a"
"main-box main-box small-box-b";
grid-template-columns: 200px 50px;
grid-template-rows: 300px 50px;
}
.main-box {
grid-area: main-box;
min-height: 300px;
background-color: green;
}
.small-box-a {
grid-area: small-box-a;
}
.small-box-b {
grid-area: big-box-b;
}
<div class="top-container">
<div class="main-box"> This is a big box </div>
<div class="small-box-a"> This is a small box A</div>
<div class="small-box-b"> This is a small box B</div>
</div>
You had a typo..
.small-box-b {
grid-area: big-box-b;
}
should be
.small-box-b {
grid-area: small-box-b;
}
.top-container {
display: grid;
grid-template-areas:
"main-box main-box small-box-a"
"main-box main-box small-box-b";
grid-template-columns: 200px 50px;
grid-template-rows: 300px 50px;
}
.main-box {
grid-area: main-box;
min-height: 300px;
background-color: green;
}
.small-box-a {
grid-area: small-box-a;
}
.small-box-b {
grid-area: small-box-b;
}
<div class="top-container">
<div class="main-box"> This is a big box </div>
<div class="small-box-a"> This is a small box A</div>
<div class="small-box-b"> This is a small box B</div>
</div>

Reverse order of columns in CSS Grid Layout

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: