I have started using css grids recently (https://css-tricks.com/snippets/css/complete-guide-grid/) but feel like I'm not fully exploiting them. My question concerns a situation where my displayed data is dynamic. For instance I want to display this:
In short:
each row of data can contain one or two columns
rows are separated by a divider row
data is dynamic, the css grid layout cannot be pre-set
It feels like css grids have a way of doing this (without dynamically generating the css), using repeat etc... but I don't really see how.
This is done under React, some dynamic rows are generated using a map, the code would looks something like this:
return (
<div>
<div> 1A </div>
<div> 1B </div>
<MyDivider />
<div> 2 </div>
<MyDivider />
{myData.map((row) => (
<div key={row.key}>
<div>{row.A}</div>
<div>{row.B}</div>
<MyDivider />
</div>
))}
// more rows, with single (merged) or dual columns...
</div>
);
Is it possible to do this without dynamically generating the grid css? Namely, defining grid settings for single / dual column lines, and divider lines? Also, due to the map function, under React, my dynamic content ends up wrapped in a div; do I need to break this down in multiple map's so that I don't have this wrapping div?
Thanks!
Edit 10/24/2020:
To clarify things, I don't want to literally display what is in this drawing, this is just a layout, with each '1A', '1B' block corresponding to some data cell, just like in a tabulator.
Assuming that you know if a row is single or dual columns.
You can make a cell span by using grid-column like the sample below
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 5px; // for demonstration purposes
}
.grid-item {
background-color: lightblue; // for demonstration purposes
}
.grid-item.single {
grid-column: span 2;
}
.grid-separator {
grid-column: span 2;
background-color: lightgreen; // for demonstration purposes
}
<div class="grid">
<div class="grid-item">A1</div>
<div class="grid-item">B1</div>
<div class="grid-separator">Separator</div>
<div class="grid-item single">A2</div>
<div class="grid-separator">Separator</div>
<div class="grid-item">A3</div>
<div class="grid-item">B3</div>
<div class="grid-separator">Separator</div>
<div class="grid-item">A4</div>
<div class="grid-item">B4</div>
<div class="grid-separator">Separator</div>
<div class="grid-item single">A5</div>
</div>
Note that you need to add the single class under some condition
Please show us your css or you can do it like that way...
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
}
.grid div {
text-align: center;
background: #ddd;
margin: 2px;
padding: 10px;
}
.mydevider {
grid-column: 1 / span 2;
}
<div class="grid" >
<div> 1A </div>
<div> 1B </div>
<div class="mydevider">
<div> 1A </div>
</div>
<div> 1B </div>
<div> 1B </div>
<div class="mydevider">
<div> 1A </div>
</div>
<div> 1B </div>
<div> 2 </div>
</div>
Related
Is there a way to make a grid item span over all free space automatically?
For example here, the grid has two rows and two columns. In the first row, the second column is free, so I'd like to span the item (as it does when I explicitly set grid-column-end: 3).
But I do not want to calculate the spans, but let the browser do that.
Edit: to make the question clearer: the container is only allowed to specify the number of rows and cols. The items are only allowed to specify the row- and col-start but no spans and -ends so if there is an item with row-start: i followed by an item with row-start: i+1+n and no items with row-start: i+1+m with m<n the item with row-start: i should automatically span n.
<div style="display: grid; gap: 5px;">
<div style="
background: red;
grid-row-start: 1;
grid-column-start: 1;
/* how can I get rid of this */
grid-column-end: 3;">1 1</div>
<div style="
background: red;
grid-row-start: 2;
grid-column-start: 1;">2 1</div>
<div style="
background: red;
grid-row-start: 2;
grid-column-start: 2;">2 2</div>
</div>
First off,
You cannot automatically span grid items over all free space.
Without explicitly setting the grid-column-end property or calculating the spans yourself - it would be best to resort to something like flexbox.
The way the grid layout works is that it uses the grid-row-start, grid-column-start, grid-row-end and grid-column-end properties to determine the size and position of each item within the grid. Without explicitly setting these properties, the browser will not know how to position the items and you'll inevitably end up with a gigantic mess. Grids aren't like flexbox since they aren't really smart and require you to explicitly set nearly every aspect of it in order to be rendered properly. They aren't really responsive either.
If you do decide to continue sticking with CSS Grid, one option would be to use JavaScript to calculate the spans based on the position of the other items on the grid and then set these properties dynamically. This is more complex and will add more overhead to your code - making it less easy to maintain
Another option (which I personally recommend) is to use flexbox instead of CSS Grid. A flexbox automatically adjusts the size of its flex items based on the available space, and you can use the flex-wrap property to wrap the items onto new rows if there is not enough space.
Flexbox is also responsive. CSS Grid is not.
Therefore, the pros weigh out the cons when compared to CSS Grid.
I don't know if that can totally suits you but one way would be to defined class with span 2 and span1.
.row {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: 1fr auto;
}
.col1 {
grid-column: auto / span 2;
}
.col2 {
grid-column: auto / span 1;
}
<div class="row">
<div class="col1" style="background-color: red">
1 1
</div>
</div>
<div class="row">
<div class="col2" style="background-color: green">
2 1
</div>
<div class="col2" style="background-color: blue">
2 2
</div>
</div>
we can put css grid-column: 1 / -1; as it will automatically fill the cell to the last. So basically we don't need to provide grid-column-end with specific number.
.row {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.col-stretch{
grid-column: 1 / -1;
}
<div class="row">
<div class="col-stretch" style="background-color: red">
1 1
</div>
</div>
<div class="row">
<div class="col" style="background-color: green">
2 1
</div>
<div class="col" style="background-color: gray">
2 2
</div>
<div class="col" style="background-color: yellow">
2 3
</div>
</div>
The best way I can describe this is via picture, since I am unable to do this using CSSGrid. I am starting with 3 columns, each column has n rows. Using CSSGrid, how can I collapse these columns into one column so that all of the members of the first column are above (see second image) the members of the next column?
Codepen to illustrate the problem: https://codepen.io/robbyjm0/pen/dydjVYa
Starting Point: 3 columns
Desired outcome
Uhm the way I see the structure of this is that you have 3 columns:
<div class='container'>
<div class='column'>
<div class='column__row'>
</div>
<div class='column__row'>
</div>
....
</div>
<div class='column'>
<div class='column__row'>
</div>
<div class='column__row'>
</div>
....
</div>
<div class='column'>
<div class='column__row'>
</div>
<div class='column__row'>
</div>
....
</div>
</div>
Each column can either be a grid or a flex-box (I would personally go with flex since they are just a bunch of rows)
.column {
display: flex;
flex-direction: row;
}
And to to make them display as 3 equal size columns next to each other:
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
To make them appear as 1 column stacking on 1 on top another:
.container {
display: grid;
grid-template-rows: 1fr 1fr 1fr;
}
If you don't have control over the order of the html elements, you can give each column a unique id and use grid-template-area
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 :
I’d like to set up a page with multiple entries consisting of some description text and associated icons. The icons may vary in size and should be aligned. (I use letters “i” and “w” instead of icons for simplicity in my examples below.)
When the page is wide enough, I’d like them to be set up as a grid with a wide left column and the icons to the right, that should take as few horizontal space as possible. Here is an example with two “icons”.
.container {
display: grid;
grid-template-columns: 1fr repeat(2, max-content);
}
.content {
justify-self: center;
}
<div class="container">
<div class="header">Some text</div>
<div class="content">i</div>
<div class="content">i</div>
<div class="header">Some more text</div>
<div class="content">w</div>
<div class="content">w</div>
</div>
When the viewport is too small, so that the left column would be reduced, say, below 200 px, I’d like to switch responsively the layout and have it displayed as a stack, as in this example.
.container {
display: grid;
grid-template-columns: 1fr repeat(2, max-content) 1fr;
}
.container > div {
justify-self: center;
}
.header {
grid-column-start: 1;
grid-column-end: 5;
}
.content1 {
grid-column-start: 2;
}
.content2 {
grid-column-start: 3;
}
<div class="container">
<div class="header">Some text</div>
<div class="content1">i</div>
<div class="content2">i</div>
<div class="header">Some more text</div>
<div class="content1">w</div>
<div class="content2">w</div>
</div>
This approach works, but there are multiple aspects that I’d like to improve, if possible.
My web site uses bootstrap, so I could use their “row” and “col” functionalities (or other bootstrap concepts). Designing my own grid instead feels like not using the right tools for the job. But I could not find out how to design such a grid with bootstrap’s rows and columns. (This question raises a similar problem.)
My approach requires an explicit media query and uses two completely different designs depending on the available space. This feels more complex than necessary. Is it possible to make better use of the grid module responsiveness (or any other appropriate html or css trickery), so that the icons would automatically flow below the text when the viewport is too small? I thought about using auto-fill, but as my columns do not all have the same size, I ignore how to proceed.
My current design for the small viewport case uses classes content1, content2, and so on, and repetitive CSS instructions to place them in successive columns. This problem will be exacerbated if I want more icons. Can I avoid such repetition?
The display classes are responsive. Therefore you can use d-flex d-md-grid on the container. When it switches to display:flex the grid-template-columns will be ignored.
.container {
grid-template-columns: 1fr repeat(2, max-content);
}
.content {
justify-self: center;
}
<div class="container d-flex d-md-grid flex-wrap align-items-center justify-content-center text-md-start text-center">
<div class="header w-100">Some text</div>
<div class="content">i</div>
<div class="content">i</div>
<div class="header w-100">Some more text</div>
<div class="content">w</div>
<div class="content">w</div>
</div>
Demo on Codeply
Of course, you could use d-sm-grid, d-lg-grid or d-xl-grid instead of d-md-grid to set the breakpoint as desired.
I made an example in codepen:
Link: https://codepen.io/ScottFSchmidt/pen/gGVPrm
How do I get section B to float left on XS/mobile phone (sections A, C, D will hide). Getting all sections to "stack float left" would also work.
The problem seems to be that section A, C, & D are still "there" but hidden causing section B to "float right".
<div class="container-fluid wrapper">
<div class="box a hidden-sm hidden-xs"> <b><u>Definitions <br></b></u>
Grid-gap: provides an easy way for spacing our content <br>
Grid: generates a block-level grid <br>
Inline-grid: generates an inline-level grid<br>
Subgrid: Indicate that you want the sizes of its rows/columns to be taken from its parent rather than specifying its own.
</div>
<div class="box b">
<b><u>Conclusion <br></b></u>
Flexbox and Gridbox can be used together (with bootrap). <br>
Flexbox is best for single rows while Grixbox is best for "2 dimensions" <br>
Non-flexible measurements such as px can make boxes not appear on screen if the screen width is less than the px size. <br>
<br><b>Sources</b><br>https://css-tricks.com/snippets/css/complete-guide-grid/<br>
https://gridbyexample.com/examples/<br>
https://css-tricks.com/things-ive-learned-css-grid-layout/<br>
https://css-tricks.com/snippets/css/complete-guide-grid/<br>
https://www.w3schools.com/css/css_rwd_grid.asp<br>
</div>
<div class="box c hidden-sm hidden-xs"><b><u>What is flexbox? </b></u><br>
Flexbox is for one dimensional layout (row or column).
<br>"Flexbox is a single dimension – in a row OR a column." -Rachel Andrews <br>
Warning: Flexbox is NOT compatible with some IE verisions.
</div>
<div class="box d hidden-sm hidden-xs">
<b><u>What is gridbox? <br></b></u>
Grid is designed to be used with flexbox, not instead of it. CSS grid is for two dimensional layout. <br>"Grid is for layout of items in two dimensions – rows AND columns" - Rachel Andrews.
</div>
</div>
If you -1 please explain why for better posting in future as I am new to forms.
Thanks!
You can do that using flex. Check the code below.
.container {
display: flex;
flex-direction: column;
}
.left{
order: 2;
}
.right {
order: 1;
}
.container > div {
flex: 1;
}
.container img {
width: 100%;
display: block
}
.innerBottom {
display: flex;
flex-direction: column
}
#media(min-width: 768px){
.container {
flex-direction: row;
}
.innerBottom {
flex-direction: row
}
.left{
order: 1;
}
.right {
order: 2;
}
}
<div class="container">
<div class="right">
<img src="http://www.placehold.it/600x200/cccccc?text=Right">
</div>
<div class="left">
<div>
<img src="http://www.placehold.it/1200x200/cc0000?text=Left">
</div>
<div class="innerBottom">
<div>
<img src="http://www.placehold.it/600x200/azazaz?text=LeftBottomLeft">
</div>
<div>
<img src="http://www.placehold.it/600x200/ddd?text=LeftBottomRight">
</div>
</div>
</div>
</div>