I'm trying to create a table using CSS grid, with equal columns based on the content. I want to avoid using <table>. This is a follow-up to this question: Auto-adjusting columns with CSS grid
What I'm trying to achieve:
This table works: https://codepen.io/anon/pen/baExYw
But I want to wrap the each row in a div, which unsurprisingly breaks the table.
This table is broken: https://codepen.io/anon/pen/qpbMgG
app.html
<div class="wrapper">
<div class="box a">col 1</div>
<div class="box b">col 2</div>
<div class="box c">col 3</div>
<!-- Table Row -->
<div class="row">
<div class="box d">short data</div>
<div class="box e">a really long piece of data</div>
<div class="box f">short data</div>
</div>
<!-- Table Row -->
<div class="row">
<div class="box d">short data</div>
<div class="box e">a really long piece of data</div>
<div class="box f">short data</div>
</div>
</div>
app.css
.wrapper {
display: grid;
grid-template-columns: repeat(3, auto);
background-color: #fff;
color: #444;
max-width: 800px;
}
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
}
I'm still very new to CSS Grid, so I'm still having trouble understanding how half of this stuff works behind the scenes.
Any help is appreciated. Thanks in advance.
display: contents is what you need.
contents
These elements don't produce a specific box by themselves. They are
replaced by their pseudo-box and their child boxes.
Add this CSS (example):
.row {
display: contents;
}
More links:
Get Ready for `display: contents;`
Browser support table
In your first example, your data cells are children of the container. Hence, grid properties – which only work between parent and child – work as you expect.
In your second example, you have some data cells that are children of .row containers. These cells are no longer children of .wrapper, the grid container. Therefore, these cells are outside the scope of grid layout, do not recognize grid properties and are rendered as standard block-level elements.
So, basically, grid containers with different child elements render different layouts.
One solution to get your examples to match would be to make the grid items into grid containers having the same properties as the parent. Here's the general idea:
Revised Codepen
.wrapper {
display: grid;
grid-template-columns: repeat(3, minmax(50px, 1fr));
background-color: #fff;
color: #444;
max-width: 800px;
}
.row {
grid-column: 1 / -1;
display: grid;
grid-template-columns: repeat(3, minmax(50px, 1fr));
}
div:nth-child(4) { grid-row-start: 2; }
div:nth-child(5) { grid-row-start: 3; }
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
}
<div class="wrapper">
<div class="box a">col 1</div>
<div class="box b">col 2</div>
<div class="box c">col 3</div>
<!-- Table Row -->
<div class="row">
<div class="box d">short data</div>
<div class="box e">a really long piece of data</div>
<div class="box f">short data</div>
</div>
<!-- Table Row -->
<div class="row">
<div class="box d">short data</div>
<div class="box e">a really long piece of data</div>
<div class="box f">short data</div>
</div>
</div>
It didn't break, it works exactly as intended.
Every child element is using one cell of the grid, if you wrap your header in another div you'll see they swap rows for columns, because each group will use one cell.
If you are going to display tabular data, then you should stick with tables as they are the semantically correct element for that purpose.
However if you are trying to build responsive designs or you really want to use grid I suggest you to read this incredible article about CSS grids.
Take a look in display: subgrid; maybe it is what you are looking for in this scenario.
Actually we should use auto-fit property of css-grid to create table-like layout with css gridNote: here because of auto-fit we do not need to specify the number of columns
#mixin table-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
align-items: center;
}
.tr{
#include table-row();
}
.thead{
#include table-row();
}
<HTML>
<div>
<div class="thead"></div>
<div class="tr"></div>
</div>
</html>
Related
I am creating a Grid layout like the following:
Each row contains 3 items and a divider line after these (and before next row).
Code is as follows:
HTML:
<div class="container">
<div class="item">ab</div> <!-- item -->
<div class="item">cdef</div> <!-- item -->
<div class="item">ghi</div> <!-- item -->
<div class="line"></div> <!-- divider line -->
<div class="item">jkl</div>
<div class="item">mno</div>
<div class="item">pq</div>
<div class="line"></div>
<div class="item">rs</div>
<div class="item">tuvw</div>
<div class="item">xyz</div>
<div class="line"></div>
</div>
CSS:
.container {
display: grid;
grid-template-columns: 1fr 1fr min-content;
}
.line {
grid-column: 1/4;
height: 1px;
width: 100%;
background: blue;
}
The problem is that the height of the divider line has a strange behavior. The following screenshots are taken from different browsers (Chrome & Firefox). Each time, one of the lines is rendered taller:
Inspecting DOM using Chrome Tools, height is always shown as 1px (as written in CSS). Therefore, it seems to be a rendering problem.
I suppose the problem is reproducible with this fiddle: https://jsfiddle.net/o96avqe8/
UPDATE:
The problem has nothing to do with Grid. It even occurs in the following minimal example:
HTML:
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
CSS:
.line {
width: 100%;
height: 1px;
background: blue;
margin-bottom: 20px;
}
I was not able to reproduce the problem you are describing.
Using your code, the layout renders the same on Firefox and Chrome.
But here's an idea that may fix the problem you are seeing: Try a different approach for generating the lines.
remove the lines from the HTML (for the sake of cleaner mark-up, they shouldn't be there anyway)
set a background color on the container
use the grid-row-gap property to generate the lines
.container {
display: grid;
grid-template-columns: 1fr 1fr min-content;
grid-row-gap: 1px;
background-color: blue;
border-bottom: 1px solid blue;
}
.item {
background-color: white;
}
<div class="container">
<div class="item">ab</div>
<div class="item">cdef</div>
<div class="item">ghi</div>
<div class="item">jkl</div>
<div class="item">mno</div>
<div class="item">pq</div>
<div class="item">rs</div>
<div class="item">tuvw</div>
<div class="item">xyz</div>
</div>
I would like to have a CSS grid that renders the items column by column, where the max number of columns and rows is not assumed. I have below the best version that I was able to come up with, although with some workarounds that I am hoping to not be required:
.grid {
display: grid;
grid-auto-flow: column;
grid-template-rows: repeat(4, 1fr);
}
.item {
margin: 1em;
}
.column {
display: contents;
}
.clear {
grid-row-end: -1;
}
<div class="grid">
<div class="column">
<div class="item">A1</div>
<div class="item">A2</div>
<div class="clear"></div>
</div>
<div class="column">
<div class="item">B1</div>
<div class="item">B2</div>
<div class="item">B3</div>
<div class="clear"></div>
</div>
<div class="column">
<div class="item">C1</div>
<div class="clear"></div>
</div>
</div>
As you can see:
The CSS hard codes the number of rows using grid-template-rows. Removing this will cause items to appear in incorrect columns if there are more items than the hard-coded number of rows, while making the hard-coded number extremely large (e.g., 1000) will work for all practical number of items in a column, but cause a large amount of blank space to be added to the bottom of the document.
There is a "clear" div that I'd rather not need in each column to force the auto-placement to the next column.
Note that just having each column lay itself out (using something like flexbox or CSS Columns) will not work, as it is important that the grid items (which may have varying heights) remain aligned with their horizontal neighbors.
You can easily get rid of the clear element by setting the row of the first element of each column then you can consider the trick of a big number of rows but with auto sizing and not 1fr. You won't have any blank space if you don't use row gaps:
.grid {
display: grid;
grid-auto-flow: column;
grid-template-rows: repeat(1000, auto);
}
.item {
margin: 1em;
}
.column {
display: contents;
}
.column .item:first-child {
grid-row: 1;
}
<div class="grid">
<div class="column">
<div class="item">A1</div>
<div class="item">A2</div>
</div>
<div class="column">
<div class="item">B1</div>
<div class="item">B2</div>
<div class="item">B3</div>
</div>
<div class="column">
<div class="item">C1</div>
</div>
</div>
I tried to achieve the masonry style using css with the column layout like the markup below.
I want to know if it's possible to make the .green one to take two columns instead of one?
Thank you in advance!
.parent{
column-gap: 1rem;
column-count: 2;
}
.element{
display:inline-block;
background:red;
width:100%;
height:100px;
}
.green{
background:green;
}
<div class="parent">
<div class="element green">
</div>
<div class="element">
</div>
<div class="element">
</div>
<div class="element">
</div>
</div>
With CSS grid you can use grid-column: span 2:
.wrapper {
display: grid;
grid-gap: 0.5rem;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-flow: dense;
padding: 0.5rem;
}
.box {
background-color: lightblue;
padding: 0.5rem;
}
.a,
.d,
.e,
.f {
background-color: lightcoral;
grid-column: span 2; /* <-- here is the trick */
}
<div class="wrapper">
<div class="box a">A</div>
<div class="box b">B</div>
<div class="box c">C</div>
<div class="box d">D</div>
<div class="box e">E</div>
<div class="box f">F</div>
<div class="box g">G</div>
<div class="box h">H</div>
</div>
Learn more about it here: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column
Regarding masonry style: At the time of writing, Level 3 of the CSS Grid Layout specification includes a masonry value for grid-template-columns and grid-template-rows layout, though browser support is pretty non-existent: https://caniuse.com/?search=masonry
Learn about it here: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Masonry_Layout
So instead I used grid-auto-flow: dense; on the grid, which makes grid item G come before grid item F. It's not really masonry style (placing elements in optimal position based on available vertical space), but it comes close by making the grid dense filling up all available horizontal space with the next grid item that fits that space.
"dense" packing algorithm attempts to fill in holes earlier in the grid
Learn about it here: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow
Oh, if you are new to CSS grid, I recommend watching Wes Bos' talk “CSS Grid in 45 Minutes!”: https://www.youtube.com/watch?v=DCZdCKjnBCs
CSS Grid layout provides a simple, easy and efficient solution.
.parent {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-rows: 100px;
grid-gap: 1rem;
}
.element.green {
grid-column: 1 / -1;
background: green;
}
.element {
background: red;
}
<div class="parent">
<div class="element green"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
</div>
More information:
Make a grid column span the entire row
CSS-only masonry layout
I would say, no you can't make the single .green element take up two columns, becuase you are specifically telling the browser to use two columns. If you need it to span the two columns, then I would suggest using a separate element. Perhaps a more suitable solution for this would be to use the CSS grid layout. The snippet below contains an example of both of these solutions:
.parent {
column-gap: 1rem;
column-count: 2;
}
.element {
display: inline-block;
background: red;
width: 100%;
height: 100px;
}
.green {
background: green;
width: 100%;
height: 100px;
margin-bottom: 1rem;
}
.grid-container {
margin-top: 20px;
display: grid;
grid-template-columns: auto auto;
grid-gap: 1rem;
}
.greenGrid {
background: green;
height: 100px;
grid-column-start: 1;
grid-column-end: 3;
}
.redGrid {
background: red;
height: 100px;
}
<div class="green">
</div>
<div class="parent">
<div class="element">
</div>
<div class="element">
</div>
<div class="element">
</div>
</div>
<div class='grid-container'>
<div class='greenGrid'></div>
<div class='redGrid'></div>
<div class='redGrid'></div>
<div class='redGrid'></div>
</div>
started using CSS grid instead of boostrap, and im having some issue to get it right.
i want to create a grid layout that have 4fr, and 8fr columns (just like boostrap 8 and 4 columns)
and when the divs inside the grid of 4r gets fill its the divs go to a second row just like flex-wrap:wrap.
BUT Its not work its only push it inline one after another, and ignoring the grid boundaries
.home {
display: grid;
grid-template-columns: 4fr 8fr;
grid-template-rows: 100%;
height: 100%;
}
<div class="home">
<div class="col-8">
</div>
<div class="col-4">
<mat-button-toggle-group class="side-menu-button">
<mat-button-toggle>test </mat-button-toggle>
<mat-button-toggle>test</mat-button-toggle>
<mat-button-toggle>test</mat-button-toggle>
<mat-button-toggle>test</mat-button-toggle>
<mat-button-toggle>test</mat-button-toggle>
<mat-button-toggle>test</mat-button-toggle>
<mat-button-toggle>test</mat-button-toggle>
<mat-button-toggle>test</mat-button-toggle>
</mat-button-toggle-group>
</div>
</div>
i even tried changing it to
grid-template-columns: repeat(1, auto-fill, 4fr 8fr);
If you're just wanting to use the grid to have items wrap inside of a div, what you're doing should basically work. Don't forget to tell .col-8 and .col-4 where they belong inside of the grid you've set up, and set the children you want to wrap to inline-block:
* {
box-sizing: border-box;
}
.home {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: 100%;
height: 100%;
width: 100%;
grid-gap: 20px;
}
.col-8 {
grid-area: 1/1/1/9;
}
.col-4 {
grid-area: 1/9/1/13;
}
.bluebox,
.blackbox {
display: inline-block;
width: 50px;
height: 20px;
}
.bluebox {
background-color: blue;
}
.blackbox {
background-color: black;
}
<div class="home">
<div class="col-8">
<div class="bluebox"></div>
<div class="bluebox"></div>
<div class="bluebox"></div>
<div class="bluebox"></div>
<div class="bluebox"></div>
<div class="bluebox"></div>
<div class="bluebox"></div>
<div class="bluebox"></div>
<div class="bluebox"></div>
<div class="bluebox"></div>
</div>
<div class="col-4">
<div class="blackbox"></div>
<div class="blackbox"></div>
<div class="blackbox"></div>
<div class="blackbox"></div>
<div class="blackbox"></div>
<div class="blackbox"></div>
<div class="blackbox"></div>
<div class="blackbox"></div>
<div class="blackbox"></div>
<div class="blackbox"></div>
</div>
</div>
The reason I set up 12 columns instead of one that's 8fr and one that's 4fr is because I'm unclear about whether you're wanting a 12 column usable system like bootstrap (which is the way I implemented it), or literally only two columns. Either way should function for what you are describing in your question, but 12 separate columns is arguably more extensible later-on.
Here's a pen that contains the above code:
https://codepen.io/grantnoe/pen/MdOQOv
grid-area is what I used to set the location of .home's children. The format is as follows:
grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
The only caveat is that you've nested the children you're wanting to wrap inside of secondary element <mat-button-toggle-group>. Consider adjusting the width of that element to 100% to fill the grid's child .col-4.
I'm trying to create a simple flexbox grid with two columns, however with the option of declaring one of the children as "featured" making it twice the height of the normal children, so given the following markup:
<div class="container">
<div class="child featured">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
<div class="child">6</div>
<div class="child">7</div>
</div>
You'd end up with something like this (margins/padding/border for illustrative purposes only):
However I can't seem to get it to work, the children all just stack under the featured child rather than fill the available space.
My basic CSS is:
.container {
display: flex;
flex-flow: column wrap;
}
.child {
flex: 1 0 50%;
height: 50vh;
max-width: 50%;
}
.child.featured {
height: 100vh;
}
Any idea what I'm doing wrong, or if there is a better approach to this (without resorting to JavaScript)?
Flexbox doesn't support such grid. But you can do it using floats:
.child {
float: left;
height: 50vh;
width: 50%;
outline: 1px solid red;
}
.child.featured {
height: 100vh;
}
<div class="container">
<div class="child featured">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
<div class="child">6</div>
<div class="child">7</div>
</div>
Any idea what I'm doing wrong, or if there is a better approach to this (without resorting to JavaScript)?
The problem is that flexbox is not designed to create anything more than simple grids. Once you ask for something like a masonry layout (which is what you're after), you'll need hacks and workarounds for flexbox to complete the task.
However, this layout can be achieved easily in CSS Grid:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
}
.child.featured {
grid-row-end: span 2;
}
/* non-essential decorative styles */
.container {
padding: 10px;
border: 2px solid gray;
background-color: lightgray;
height: 50vh;
}
.child {
background-color: deepskyblue;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.3em;
}
<div class="container">
<div class="child featured">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
<div class="child">6</div>
<div class="child">7</div>
</div>
jsFiddle demo
For a complete explanation of the problem when using flexbox, and how the Grid functions work, see this post:
CSS-only masonry layout but with elements ordered horizontally