Flexbox grid last row wrong margins between items - css

I'm still experimenting with flexbox, but there's this case I can't solve.
I have this grid
<div class="wrapper">
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
With the following CSS:
.wrapper{
width:100%;
oveflow:hidden;
margin: 0;
padding:0;
}
.container {
width:100%;
display: flex;
flex: 1;
flex-flow: row wrap;
justify-content: space-between;
margin: 0;
}
.container .item {
display: block;
margin: 0 0 30px;
width: 29.666%;
}
But when I use this, items in the last row are rendered with a different "space between" them than the items in the previous row.
Look at this picture. This is what I get:
Trying to fix this, the only thing I can find is to set items' lateral margin as auto, like this
.container .item {
display: block;
margin: 0 auto 30px;
width: 29.666%;
}
But now, I have lateral margins between the items and the container border, like this:
Why does this happen?
Is there a way to solve this other than having negative lateral margins on the container div?

I didn't see the same result you did, but your margins looks a bit weird. I stripped it down to this:
.wrapper {
background: blue;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin: 0;
}
.item {
background: yellow;
height: 30px;
width: 29.666%;
}
.item:not(:nth-last-child(-n+3)) {
margin-bottom: 30px;
}
<div class="wrapper">
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>

I see no defaults.
I did modify a bit your css code given so things can be seen and margin set only from second line:
.wrapper {
}
.container {
width: 100%;
background: #3197D3;
display: flex;
flex-flow: row wrap;
justify-content: space-between;
margin: 0;
}
.container .item {
display: block;
width: 29.666%;
min-height: 100px;
background: #FFFF4D;
}
.item:nth-child(3) ~.item {
margin-top: 30px;
}
/* demo purpose*/
.container:before {
content:'.'attr(class);
width:100%;/* will span whole line */
padding:0.15em;
font-size:3em;
text-align:center;
color:white;
background:rgba(255,255,255,0.5);
}
.container .item {
display: flex;
}
.item:before {
content: attr(class);
margin: auto;
color:#3197D3;
font-size:2em;
}
<div class="wrapper">
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>

Related

Making a container fit the width of its children

I'm trying to dynamically size a container to the width of its children when they're broken down into columns and rows. This can easily be achieved by writing a bunch of media queries and hard coding the width of the container, but I'm looking for a way to do it dynamically with CSS.
In this example id like .container to only be a multiple of the width of a fixed size .item (200px, 410px, 620px, 830px, etc) and not include the empty space to the right.
.container {
background-color: #555;
display: flex;
flex-wrap: wrap;
gap: 10px;
max-width: 1000px;
padding: 10px;
}
.item {
aspect-ratio: 1;
background-color: #ddd;
width: 200px;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
If the width of items are fixed you can try like below:
.wrapper {
display: grid;
grid-template-columns: repeat(auto-fill,200px); /* same as width of items */
gap: 10px;
padding: 10px; /* move the padding here */
}
.container {
grid-column: 1/-1; /* take all the columns */
background-color: #555;
outline: 10px solid #555; /* use outline to cover the padding */
display: flex;
flex-wrap: wrap;
gap: inherit;
}
.item {
aspect-ratio: 1;
background-color: #ddd;
width: 200px;
}
<div class="wrapper">
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>

CSS Grid make the column gap identical

How do I make the gap/space between items to be the same?
I've tried grid-column-gap but the gap will be different since the number of items on each row are different, where the first and third row is bigger and the second row is the ideal gap space.
Another option is to make the width of the items to be 100% and add a margin but this way their width will be different as items on the second row will be smaller.
What I wanna achieve is:
Center all of the items in the div #innerContainer
Make the gap between items to be the same
The width of each item is the same
#container {
display: flex;
align-items: center;
justify-content: center;
}
#innerContainer {
display: grid;
grid-template-columns: repeat(60, 1fr);
grid-column-gap: 9px;
grid-row-gap: 9px;
}
.item {
width: 90px;
height: 90px;
background-color: blue;
grid-column: auto / span 15;
}
.item:nth-child(4)~.item {
grid-column: auto / span 12
}
<div id="container">
<div id="innerContainer">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
Flexbox now offers a gap property. We could use that along with multiple rows of content to make it work.
You should use flexbox for the flexible-sizing. It is because grid is meant to be table-like. And it does not really want to have you mess around with the spaces too much. It is always aligned to some raster.
It should work accross all major browsers (see: https://caniuse.com/?search=gap) but you could always fallback to using paddings instead or space the items evenly and make the rows wide enough so that the available space is equal to the n * n_size + (n-1) * gap.
#container {
border: 1px dashed black;
/* just for visuals */
display: flex;
align-items: center;
justify-content: center;
}
#innerContainer {
display: flex;
flex-flow: column nowrap;
gap: 10px;
}
.row {
display: flex;
justify-content: center;
gap: 10px;
}
.item {
width: 90px;
height: 90px;
background-color: blue;
}
<div id="container">
<div id="innerContainer">
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
</div>
Drop CSS grid and use flexbox:
#innerContainer {
display: flex;
align-items: center;
justify-content: center;
flex-wrap:wrap;
gap: 4.5px 9px;
}
/* pseudo element will seperate your elements*/
#innerContainer:before,
#innerContainer:after {
content:"";
flex-basis:100%
}
#innerContainer:after {
order:2;
}
/**/
/* 4 items before the "before" */
.item:nth-child(-n + 4) {
order:-1;
}
/* 4 items after the "after" */
.item:nth-last-child(-n + 4) {
order:3;
}
.item {
width: 90px;
height: 90px;
background-color: blue;
}
<div id="container">
<div id="innerContainer">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>

CSS Grid container shrink to items size

I'm trying to get a grid container to not be wider than the width of it's rows, i.e. fit as many columns as it can, and then collapse to the width of it's children.
For example, if the total amount of grid items are 16, the total amount of space the container has is 45px, and each grid item is 10px wide, then the desired outcome is a 4 by 4 grid wide 40px (not 45px).
I tried to set the display to inline-grid but that makes auto-fill give just one column.
.outer {
width: 280px; /* this varies */
background: orange;
resize: both;
overflow: auto;
height: 300px;
padding: 10px;
}
.items {
background: green;
display: grid;
grid-template-columns: repeat(auto-fill, 50px);
justify-content: end;
column-gap: 10px;
row-gap: 10px;
}
.item {
height: 50px;
width: 50px;
background: blue;
}
<div class="outer">
<div class="items">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
The space in this image should not be part of the grid container.
Consider its parent to be a grid container where you apply the same rules to limit the width of your grid:
.outer {
width: 280px; /* this varies */
background: orange;
resize: both;
overflow: auto;
height: 300px;
padding: 10px;
/* added */
display:grid;
grid-template-columns: repeat(auto-fill, 50px);
justify-content: end;
column-gap: 10px;
/**/
}
.items {
grid-column:1/-1; /* take all the columns */
background: green;
display: grid; /* you can use "inherit" here */
grid-template-columns: repeat(auto-fill, 50px); /* you can use "inherit" here */
column-gap: 10px; /* you can use "inherit" here */
row-gap: 10px;
}
.item {
height: 50px;
width: 50px;
background: blue;
}
<div class="outer">
<div class="items">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>

Display list of elements as table with CSS only?

Display list as a table with 3 columns, with the items evenly spread, and having 10px margin from every sides and between rows.
.item { width: 10px; height: 10px; background: black; }
.grid { background: #eee; }
<div class="grid">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Should be rendered as
It is possible to achieve that with flex layout, but it would require more complicated HTML, see example below.
.item { width: 10px; height: 10px; background: black; }
.grid { background: #eee; padding: 10px 10px 0 10px; }
.row { display: flex; justify-content: space-between; padding: 0 0 10px 0; }
<div class="grid">
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
I wonder if it would be possible to do with pure CSS, without adding any additional HTML elements. The size of .items shouldn't be changed, and it's unknown.
You can do this with flexbox by adding a hidden element between the first and last row that will be width:100% thus you will avoid changing the html:
.item {
width: 10px;
height: 10px;
background: black;
}
.row {
display: flex;
justify-content: space-between;
padding: 10px;
background: #eee;
flex-wrap: wrap;
}
.row:after {
content: "";
width: 100%;
height: 10px;
}
.row :nth-child(n + 4) {
order: 1;
}
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
If the number of element is unknown you can consider CSS grid:
.item {
width: 10px;
height: 10px;
background: black;
}
.row {
display: grid;
grid-template-columns: repeat(3,1fr);
grid-gap: 10px;
padding:10px;
background: #eee;
}
.row :nth-child(3n + 2) {
margin:auto;
}
.row :nth-child(3n + 3) {
margin-left:auto;
}
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Temani Afif has written a good answer. The flexbox one will work only for 2 to 3 rows but the grid version will work for more items.
The grid CSS can be simplified further by using space-between, thus getting rid of margins and n-th child selectors.
.item {
width: 10px;
height: 10px;
background: black;
}
.row {
display: grid;
grid-template-columns: repeat(3,10px); /*change this*/
grid-gap: 10px;
padding:10px;
background: #eee;
justify-content: space-between; /*add this*/
}
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>

icons, which are equally distributed across the grid

Is it possible to equally distribute the icons using only CSS?
I want to get this result (I have not reputation to post images)
http://i.stack.imgur.com/bC0Aw.jpg
I found a solution using javascript:
_http://jsfiddle.net/VLr45/59
I tried flexbox, but got this
http://jsfiddle.net/egns7cj1/
http://jsfiddle.net/egns7cj1/2/
http://i.stack.imgur.com/h8tU2.jpg
Wrap the items inside another parent which would have flex-basis: 33%. Change the value based on the screen size using media queries.
.flex-container {
flex-flow: row wrap;
display: flex;
width: 100%;
height: 320px;
border: 1px #eee solid;
background: #ffd54f;
}
.inner-container {
display: flex;
flex: 0 0 33%;
justify-content: center;
}
.item {
flex: 0 1 auto;
width: 100px;
height: 100px;
background: #fff;
border: 1px #777 solid;
}
#media (min-width: 991px) {
.inner-container {
flex: 0 0 25%;
}
}
#media (min-width: 1200px) {
.inner-container {
flex: 0 0 20%;
}
}
<div class="flex-container">
<div class="inner-container">
<div class="item"></div>
</div>
<div class="inner-container">
<div class="item"></div>
</div>
<div class="inner-container">
<div class="item"></div>
</div>
<div class="inner-container">
<div class="item"></div>
</div>
<div class="inner-container">
<div class="item"></div>
</div>
<div class="inner-container">
<div class="item"></div>
</div>
<div class="inner-container">
<div class="item"></div>
</div>
<div class="inner-container">
<div class="item"></div>
</div>
</div>

Resources