How to divide divs into rows equally - html

Apologies if this has been asked before but I couldn't find exactly the same question.
I have the following:
.main{
display: inline-flex;
flex-direction: row;
}
<div class = "main">
<div class="sub-div">1 </div>
<div class="sub-div">2 </div>
<div class="sub-div">3 </div>
<div class="sub-div">4 </div>
<div class="sub-div">5 </div>
<div class="sub-div">6 </div>
</div>
What this does is it displays all the divs in same line. But how can I divide the divs such that there will be 3 divs on top row and 3 divs on bottom row?
Additionally, if the screen size gets smaller, how can I divide the divs such that there will be 2 divs on top row, 2 on middle row, and 2 on last row?
Can I do it without changing HTML structure or using Javascript?

Use flex-wrap: wrap on .main to allow its children to wrap onto multiple lines. Then, you could explicitly set the width equal to calc(100% / n) where n is the number of children you want per row.
To change the layout from 2 divs per row to 3 divs per row, use a media query, as shown in the code snippet below.
.main {
display: inline-flex;
flex-wrap: wrap;
}
.sub-div {
width: calc(100% / 2);
}
#media screen and (min-width: 600px) {
.sub-div {
width: calc(100% / 3);
}
}
<div class="main">
<div class="sub-div">1 </div>
<div class="sub-div">2 </div>
<div class="sub-div">3 </div>
<div class="sub-div">4 </div>
<div class="sub-div">5 </div>
<div class="sub-div">6 </div>
</div>

You may achieve it with Grid and media query.
.main {
display: grid;
/* grid-gap: 50px 100px; */
grid-template-columns: auto auto auto;
}
#media only screen and (max-width: 768px) {
.main {
grid-template-columns: auto auto;
}
}
<div class="main">
<div class="sub-div">1 </div>
<div class="sub-div">2 </div>
<div class="sub-div">3 </div>
<div class="sub-div">4 </div>
<div class="sub-div">5 </div>
<div class="sub-div">6 </div>
</div>

Related

Single row grid container with unknown number of children and pre-specified number of columns overflowing

I'm trying to create something similar to the netflix slider. Right now, I have a single row grid container with overflow set to auto and I want to have 5 columns showing at any point in time. I'm using javascript to dynamically calculate the width of the container so that I can smoothly scroll through the items using navigation buttons as well as free scroll using trackpad (or touchpad). However, I'm having trouble getting the grid container to align perfectly, and the grid-gap property is messing up the alignment of the children within the grid container. Just to show you a short snippet of my code right now:
.container{
display: grid;
grid-gap: 0.75rem;
grid-auto-flow: column;
grid-auto-columns: 20%;
overflow: auto;
}
.child {
display: flex;
background: red;
height: 150px;
}
<div class="container">
<div class="child">
card 1
</div>
<div class="child">
card 2
</div>
<div class="child">
card 3
</div>
<div class="child">
card 4
</div>
<div class="child">
card 5
</div>
<div class="child">
card 6
</div>
<div class="child">
card 7
</div>
<div class="child">
card 8
</div>
<div class="child">
card 9
</div>
<div class="child">
card 10
</div>
</div>
As you can see, the fifth column in view always overflows. I know this is an issue with percentage based values not taking into account the grid-gap, and I know that I should be using the fr unit to avoid this problem, but obviously I can't use fr because I don't have a fixed number of children. The number of children is supposed to be unknown. I have tried hacking it by removing the grid-gap and giving each child a margin instead, and then using the pseudo class nth-child(5n) to remove the extra right margin from the last child in view, but that introduces a whole new problem where when you free scroll through the items, there would be no gap between every 5th and 6th child. Is there a proper way to do this that is not janky.
I've spent a couple of hours fiddling with it and looking for a solution online, with no luck. I'd appreciate any help. Thanks in advance.
Use calc().
You have to deduct 4 gaps and divide it between 5 elements =>
4 * 0.75 / 5 = 0.6
=>
grid-gap: 0.75rem;
grid-auto-columns: calc(20% - 0.6rem);
Demo:
.container {
display: grid;
grid-gap: 0.75rem;
grid-auto-flow: column;
grid-auto-columns: calc(20% - 0.6rem);
overflow: auto;
}
.child {
display: flex;
background: red;
height: 150px;
}
<div class="container">
<div class="child">
card 1
</div>
<div class="child">
card 2
</div>
<div class="child">
card 3
</div>
<div class="child">
card 4
</div>
<div class="child">
card 5
</div>
<div class="child">
card 6
</div>
<div class="child">
card 7
</div>
<div class="child">
card 8
</div>
<div class="child">
card 9
</div>
<div class="child">
card 10
</div>
</div>
Dynamically:
.container {
--gap: 0.75rem;
--items: 6;
display: grid;
grid-gap: var(--gap);
grid-auto-flow: column;
grid-auto-columns: calc(
100% / var(--items) - var(--gap) * (var(--items) - 1) / var(--items)
);
overflow: auto;
}
Now change the value of --items and --gap.
And let's add responsiveness:
.container {
--gap: 0.75rem;
--items: 2;
display: grid;
grid-gap: var(--gap);
grid-auto-flow: column;
grid-auto-columns: calc(
100% / var(--items) - var(--gap) * (var(--items) - 1) / var(--items)
);
overflow: auto;
}
.child {
display: flex;
background: red;
height: 150px;
}
#media (min-width: 540px) {
.container { --items: 3 }
}
#media (min-width: 768px) {
.container { --items: 4 }
}
#media (min-width: 992px) {
.container { --items: 5 }
}
#media (min-width: 1200px) {
.container { --items: 6 }
}
#media (min-width: 1500px) {
.container { --items: 8 }
}
<div class="container">
<div class="child">
card 1
</div>
<div class="child">
card 2
</div>
<div class="child">
card 3
</div>
<div class="child">
card 4
</div>
<div class="child">
card 5
</div>
<div class="child">
card 6
</div>
<div class="child">
card 7
</div>
<div class="child">
card 8
</div>
<div class="child">
card 9
</div>
<div class="child">
card 10
</div>
</div>

How can I auto-flow: column but specify the columns

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>

How to put spacing on flexbox on items in same row?

I have this code and I want two items in the same row with a gap space between them, however this is not working. I want there to be the 12px gap in between the A's and B's. However I don't want to use old techniques like margin/padding.
https://codepen.io/sneaky666/pen/rNxdaOQ
<div class="container">
<div>
AAA
</div>
<div>
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
</div>
</div>
css
.container {
display:inline-flex;
flex-wrap:wrap;
gap:12px;
}
I'm following the tutorial from https://coryrylan.com/blog/css-gap-space-with-flexbox
Does anyone know?
Thanks
If you want to set a specific number of pixels for the gap, you would need to add a margin in between the items since gap is not currently supported. Example:
<div class="container">
<div class="first-item">
AAA
</div>
<div>
BBBBBBBBBBB
</div>
</div>
.container {
display: flex;
flex-direction: row;
}
.first-item {
margin-right: 12px;
}
However, if the size of the gap does not matter, you can use the flexbox alignment properties to spread out your divs in the row evenly. Example:
<div class="container">
<div class="first-item">
AAA
</div>
<div>
BBBBBBBBBBB
</div>
</div>
.container {
display: flex;
flex-direction: row;
justify-content: space-between;
}

place two divs per row

so I have X divs and I want to put 2 divs in one row next to each other. If the screen size width is below n px there should be 1 div per row.
Currently I have this
#container {
display: flex;
}
.box {
width: 50px;
background: red;
}
#media(max-width: 300px) {
#container {
display: block;
}
}
<div id="container">
<div class="box"> 1 </div>
<div class="box"> 2</div>
<div class="box"> 3 </div>
<div class="box"> 4 </div>
</div>
How can I limit the flex box to two divs per row?
Add 50% width on .box and flex-wrap:wrap on the container
Additionally, what you did by changing display: flex to block was not required. Just change the .box elements width to 100%
#container {
display: flex;
flex-wrap: wrap;
}
.box {
width: 50%;
background: red;
}
#media(max-width: 300px) {
.box {
width: 100%;
}
}
<div id="container">
<div class="box"> 1 </div>
<div class="box"> 2</div>
<div class="box"> 3 </div>
<div class="box"> 4 </div>
</div>
Just add a property in your container class like
.container {
display: flex;
flex-wrap: wrap;
}
And in box class just specify the width of your box as 50% like
.box {
width: 50%;
background: red;
}
That should do the trick.
Flex will do a trick for you. flex-wrap: wrap for #container will make children wrap when necessary. .box with 50% and after breakpoint 100%.`
According to MDN:
The CSS flex-wrap property specifies whether flex items are forced into a single line or can be wrapped onto multiple lines. If wrapping is allowed, this property also enables you to control the direction in which lines are stacked.
If you are new to flexbox I recommend this guide.
Snippet
#container {
display: flex;
flex-wrap: wrap;
}
.box {
width: 50%;
background: red;
}
#media(max-width: 300px) {
.box {
width: 100%;
}
}
<div id="container">
<div class="box"> 1 </div>
<div class="box"> 2 </div>
<div class="box"> 3 </div>
<div class="box"> 4 </div>
</div>

Is there a DRY way of Flex ordering?

Newbie to flex use/web development.
I currently have 6 boxes within a flex container ordered like this for mobile devices:
Mobile view
with code more or less like this (CSS not included, but the class “box” is the grey box you see above):
<div class="flex-container">
<div class=“box item” />
<div class=“text item”>
<h4>Text</h4>
</div>
<div class=“box item” />
<div class=“text item”>
<h4>Text</h4>
</div>
<div class=“box item” />
<div class=“text item”>
<h4>Text</h4>
</div>
</div>
This is what I want for mobile devices!
For desktop however, I’d like to achieve this: Desktop View
Currently, the only way I’m achieving this is by using this unappealing flex order css:
item:nth-of-type(1) {order:1;}
item:nth-of-type(2) {order:2;}
item:nth-of-type(3) {order:4;}
item:nth-of-type(4) {order:3;}
item:nth-of-type(5) {order:5;}
item:nth-of-type(6) {order:6;}
My question is, is there a way to achieve my desired goal (i.e. switching the order of items 3 and 4) without having to order every single item in the container creating a yucky, repetitive block of code?
You can accomplish that with only 2 CSS selectors, and with order defaults to 0, we re-position item 3 and 5/6, here done with a media query for screens wider than 600px, to 1 and 2.
.item:nth-of-type(3) { order:1; } /* put 3 after 4 */
.item:nth-of-type(n+5) { order:2; } /* put 5,6 after 3 */
Stack snippet
.flex-container { display: flex; flex-wrap: wrap; }
.item { height: 50px; flex-basis: 100%; }
.box { background: lightgray; }
#media (min-width: 600px) {
.item { flex-basis: 50%; }
.item:nth-of-type(3) { order:1; } /* put 3 after 4 */
.item:nth-of-type(n+5) { order:2; } /* put 5,6 after 3 */
}
<div class="flex-container">
<div class="box item"></div>
<div class="text item">
<h4>Text</h4>
</div>
<div class="box item"></div>
<div class="text item">
<h4>Text</h4>
</div>
<div class="box item"></div>
<div class="text item">
<h4>Text</h4>
</div>
</div>
As far as I know if you reorder items, you need to explicitly order items after the re-ordered items. So you could probably do:
item:nth-of-type(3) {order:4;}
item:nth-of-type(4) {order:3;}
item:nth-of-type(5) {order:5;}
item:nth-of-type(6) {order:6;}
If you only ever want to switch those two, you could wrap them in another flex container and simply switch ordering within. That way your outer flow won't have to be redefined, and you could set up the container to be reused in other areas where you need to achieve the same thing.
<div class="container">
<div class="item one">One</div>
<div class="item two">Two</div>
<div class="item three">Three</div>
<div class="switch">
<div class="item four">Four</div>
<div class="item five">Five</div>
</div>
<div class="item six">Six</div>
<div class="item seven">Seven</div>
</div>
.item {
flex: 1 0 100%;
line-height: 39px;
height: 40px;
width: 100%;
background: #cecece;
margin-bottom: 10px;
text-align: center;
}
.container {
display: flex;
flex-wrap: wrap;
}
.switch {
display: flex;
flex-wrap: wrap;
flex: 1 0 100%;
}
#media (min-width: 400px) {
.switch .item:nth-of-type(1) {
order: 2;
}
}
fiddle
In addition to TripWire's answer, you need not to set a different order number for every <div> after the 4th one.
.item:nth-of-type(3), .item:nth-of-type(4) ~ .item {order:100;}
.item:nth-of-type(4) {order:50;}
A pen: https://codepen.io/israfel/pen/eEbWWG