I'm trying to create a 3 column layout using the CSS Grid spec but I'm running into an issue with rows and element sizing. My columns need to contain an unspecified amount of content with varying heights.
This is my ideal layout:
Problems:
1) If I tell element A and B to use row 1, column 1 then they stack on top of one another rather than B below A.
2) If I specify element B to use the second row, then it gets pushed below element C due to element C making row 1 tall.
3) If I specify element B to use the second row, then element A stretches to fill row 1.
Is there any way to get elements to behave like in the first picture?
The only solution I know of is to create "scaffolding" divs inside the columns like this:
<div class="grid">
<div class="col">
<div class="itemA"></div>
<div class="itemB"></div>
</div>
<div class="col">
<div class="itemC"></div>
</div>
<div class="col">
<div class="itemD"></div>
<div class="itemE"></div>
<div class="itemF"></div>
</div>
</div>
But I dislike doing that and I thought the grid spec was supposed to allow for layout creation without scaffolding.
Questions:
1) Is there any way to prevent elements from stretching to fill the row vertically?
2) Is it possible to put two elements on the same row and have them appear one below the other instead of conflicting?
Here's my 2 pens where I'm attempting to find a solution with and without scaffolding:
Without scaffolding
With scaffolding
The closest i could have, but i do recommend you to use flexbox, You can't only define columns in CSS grids, the browsers will define rows for you, if you don't, and you don't manage them, you will get weird layout.
I hard coded the difference in heights between elements.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.grid {
background: brown;
display: grid;
grid-template-columns: auto 1fr auto;
grid-gap: 10px;
padding: 10px;
}
.grid>div {
background-color: orange;
}
.itemA {
grid-column-start: 1;
grid-row: 1 / 1;
width: 100px;
height: 100px;
}
.itemB {
grid-column-start: 1;
grid-row: 2 / 4;
height: 200px;
}
.itemC {
grid-column-start: 2;
grid-row: 1 / 3;
height: 200px;
}
.itemD {
grid-column-start: 3;
width: 100px;
height: 100px;
}
.itemE {
grid-column-start: 3;
height: 100px;
}
.itemF {
grid-column-start: 3;
grid-row: 3 / 4;
height: 200px;
}
<div class="grid">
<div class="itemA">A</div>
<div class="itemB">B</div>
<div class="itemC">C</div>
<div class="itemD">D</div>
<div class="itemE">E</div>
<div class="itemF">F</div>
</div>
Related
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 :
.container {
display:grid;
grid-template-columns: 200px 200px;
grid-template-rows: 100px 100px;
grid-auto-flow: row;
grid-gap: 5px 5px;
height:500px;
width:50%;
border: 1px solid;
}
.grid-box {
background-color: skyblue;
padding: 10px 5px;
}
.four {
grid-column: span 2/3;
grid-row: 2;
}
<div class="container">
<div class="grid-box one">First</div>
<div class="grid-box two">two</div>
<div class="grid-box three">three</div>
<div class="grid-box four">Four</div>
<div class="grid-box five">Five</div>
<div class="grid-box six">Six</div>
</div>
From what I read, span 2/3 should be invalid, and not work, and indeed, it doesn't seem to work properly, as the item does not start from the 2nd column, however, it does end at the 3rd.
However, using span 2/4, produces the following outcome:
.container {
display:grid;
grid-template-columns: 200px 200px;
grid-template-rows: 100px 100px;
grid-auto-flow: row;
grid-gap: 5px 5px;
height:500px;
width:50%;
border: 1px solid;
}
.grid-box {
background-color: skyblue;
padding: 10px 5px;
}
.four {
grid-column: span 2/4;
grid-row: 2;
}
<div class="container">
<div class="grid-box one">First</div>
<div class="grid-box two">two</div>
<div class="grid-box three">three</div>
<div class="grid-box four">Four</div>
<div class="grid-box five">Five</div>
<div class="grid-box six">Six</div>
</div>
Seemingly it works exactly as expected. The item starts from the 2nd column and ends the 4rth.
So, does it actually work? Why does it work now, but it doesn't work if the item ends at the 3rd column?
From what I read, span 2/3 should be invalid, and not work
No it's valid and it's working fine.
span 2/3 means end at grid line 3 (and not column 3) and span 2 columns back. Same logic for span 2/4. It's the shorthand of:
grid-column-start: span 2;
grid-column-end: 3;
So in both cases your element will take exactly 2 columns and only the ending line with be different.
Worth to note that in your case you defined a 2 columns layout so you have 3 lines (from 1 to 3). The span 2/4 will force the creation of an extra implicit column to have a total of 3 columns and 4 lines. A third column having an auto width like you can notice.
Without the keyword span it would be a different story and still you cannot consider columns
2/4 means start at line 2 and end at line 4 (covering column 2 and column 3)
2/3 means start at line 2 and end at line 3 (covering only column 2)
Related questions if your are missing the concept of lines:
CSS Grids: Relation between grid gaps(gutters) and grid lines
Understanding grid negative values
I have a grid with a dynamically generated number of rows and columns. The cells are placed individually with grid-column-start.
I also have accompanying headings which need span every column. I would expect grid-column-start: 1; grid-column-end: -1 to produce this behaviour. However, it only does so if the number of columns is specified in advance with grid-template-columns.
See the following demonstration:
.grid {
display: grid;
grid-gap: 5px;
}
.grid--three {
grid-template-columns: auto auto auto;
}
.grid--auto {
grid-auto-columns: auto;
}
.grid-heading {
background: pink;
grid-column-start: 1;
grid-column-end: -1;
padding: 5px;
}
.grid-cell {
background: lightblue;
padding: 5px;
}
.grid-cell--1 {
grid-column-start: 1;
}
.grid-cell--2 {
grid-column-start: 2;
}
.grid-cell--3 {
grid-column-start: 3;
}
<h3>Three column grid</h3>
<div class="grid grid--three">
<div class="grid-heading">
My heading
</div>
<div class="grid-cell">
one
</div>
<div class="grid-cell">
two
</div>
<div class="grid-cell">
three
</div>
</div>
<h3>Any column grid</h3>
<div class="grid grid--auto">
<div class="grid-heading">
My heading
</div>
<div class="grid-cell grid-cell--1">
one
</div>
<div class="grid-cell grid-cell--2">
two
</div>
<div class="grid-cell grid-cell--3">
three
</div>
</div>
Is it possible to get full column spanning behaviour without prescribing the number of columns?
Unfortunately, no. This is not possible with the current version of CSS Grid (Level 1).
For a grid area to expand across all columns or rows, using the negative integer method (1 / -1), you'll need an explicit grid container.
From the specification:
7.1. The Explicit Grid
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 here...
8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
If a negative integer is given, it instead counts in reverse, starting
from the end edge of the explicit grid.
position:absolute can do this but it remain a hacky way as you will need an extra element to take the first cell and have your real element on the top filling the whole row. It can be tricky if it's not the first row.
.grid {
display: grid;
grid-gap: 5px;
position:relative; /* Don't forget this */
}
.grid--three {
grid-template-columns: auto auto auto;
}
.grid--auto {
grid-auto-columns: auto;
}
/**/
.grid:before {
content:"\80"; /* Zero width invisible character */
padding:5px;
grid-column: 1/-1;
}
.grid-heading {
background: pink;
position:absolute;
top:0;
left:0;
right:0;
padding: 5px;
}
/**/
.grid-cell {
background: lightblue;
padding: 5px;
}
.grid-cell--1 {
grid-column-start: 1;
}
.grid-cell--2 {
grid-column-start: 2;
}
.grid-cell--3 {
grid-column-start: 3;
}
<h3>Three column grid</h3>
<div class="grid grid--three">
<div class="grid-heading">
My heading
</div>
<div class="grid-cell">
one
</div>
<div class="grid-cell">
two
</div>
<div class="grid-cell">
three
</div>
</div>
<h3>Any column grid</h3>
<div class="grid grid--auto">
<div class="grid-heading">
My heading
</div>
<div class="grid-cell grid-cell--1">
one
</div>
<div class="grid-cell grid-cell--2">
two
</div>
<div class="grid-cell grid-cell--3">
three
</div>
</div>
I'm trying to do what I thought was a simple task but can't seem to figure it out so maybe it's not possible.
I have an object that I'm iterating through and that object has two attributes
export class OptionLine {
lineCode: number;
lineDescription: String;
}
I would like the LineCode to be Column 1 and LineDescription to be Column 2
My most recent attempt looks like:
<div class="config-options-box">
<div *ngFor="let optionLine of optionLines">
<div class="config-options-line-name">{{optionLine.lineCode}}</div>
<div class="config-options-line-choices">{{optionLine.lineDescription}}</div>
</div>
</div>
CSS:
.config-options-box {
display: grid;
grid-template-columns: 300px 600px;
grid-gap: 10px;
}
.config-options-line-name {
grid-column-start: 1;
grid-row-end: span 1
}
.config-options-line-choices {
grid-column-start: 2;
grid-row-end: span 1
}
But this ends up with item 1 having 2 lines in the first cell, item 2 having 2 lines in the second cell (row 1 column 2), etc.
Is this even possible?
Figured out I could display a sub grid inside each column of the main grid. So the main grid is 1 row x 2 columns and then each column has a grid inside that is n rows x 1 column. Probably not the most efficient solution but I should have less than 20 items so I imagine it will be fine.
<div class="config-options-box">
<div class="config-options-names">
<div *ngFor="let optionLine of optionLines">
<div class="config-options-line-name">{{optionLine.lineCode}}</div>
</div>
</div>
<div class="config-options-descriptions">
<div *ngFor="let optionLine of optionLines">
<div class="config-options-line-name">{{optionLine.lineDescription}}</div>
</div>
</div>
</div>
.config-options-box {
display: grid;
grid-template-columns: 300px 600px;
grid-gap: 10px;
}
.config-options-subgrid {
display: grid;
grid-template-columns: auto;
grid-gap: 10px;
}
This question already has answers here:
Make a grid item span to the last row / column in implicit grid
(5 answers)
Closed 4 years ago.
I have a CSS grid with automatic columns and 2 rows, where all but a specific element go into the first row. The special element is placed on the second row, and should fill the whole space used by all columns.
The current implementation is like this:
.TabArea {
display: grid;
grid-auto-columns: auto;
grid-template-rows: auto 1fr;
grid-auto-flow: column;
/* for visualization purposes */
background: gainsboro;
}
.Tab-title {
grid-row: 1;
}
.Tab-body {
grid-row: 2;
grid-column: 1 / -1;
/* for visualization purposes */
background: beige;
}
<div class="TabArea">
<div class="Tab-title">Title A</div>
<div class="Tab-title">Title B</div>
<div class="Tab-title">Title B</div>
<div class="Tab-body">content</div>
</div>
But as you can see, the Tab-body is only placed on the first column, despite the fact there are additional columns. How can I make it span all columns?
The solution in https://stackoverflow.com/a/44052563/1045510 does not work here because the number of columns is variable, and columns would take up space on the end, pushing the Tab-titles to the left.
I think you need to use span in order to make it span across columns as you can only use negative integers in an explicit grid:
.TabArea {
display: grid;
grid-auto-columns: auto;
grid-template-rows: auto 1fr;
grid-auto-flow: column;
/* for visualization purposes */
background: gainsboro;
}
.Tab-title {
grid-row: 1;
}
.Tab-body {
grid-row: 2;
grid-column-start: 1;
grid-column-end: span 3;
/* for visualization purposes */
background: beige;
}
<div class="TabArea">
<div class="Tab-title">Title A</div>
<div class="Tab-title">Title B</div>
<div class="Tab-title">Title B</div>
<div class="Tab-body">content</div>
</div>