Flex box - arrange items in column with multiple elements on each row if space allows - css

I want to arrange some items of variable width in a column, where the column has a fixed width. But I want elements to stack up next to each other if the total width of a group of elements doesn't exceed the column width. In the snippet, I want to add CSS to the first container so that that the 2nd and 3rd divs will be next to each other, as they are in the second container. I don't know the element widths in advance so I can't just do this manually by putting divs around the elements that should be adjacent. I want to also accomplish with just flexbox, not CSS grids if that's also a solution.
.container {
width: 500px;
border: 1px solid red;
}
.flexcol {
display: flex;
flex-direction: column;
align-items: center;
}
.inlineblock > div {
display: inline-block;
}
.item {
border: 1px solid black;
font-size: 24pt;
text-align: center;
}
.item1, .item4 {
width: 400px;
}
.item2, .item3 {
width: 200px;
}
<div class="container flexcol">
<div class="item item1">
Item 1
</div>
<div class="item item2">
Item 2
</div>
<div class="item item3">
Item 3
</div>
<div class="item item4">
Item 4
</div>
</div>
<div class="container flexcol">
<div class="item item1">
Item 1
</div>
<div class="inlineblock">
<div class="item item2">
Item 2
</div>
<div class="item item3">
Item 3
</div>
</div>
<div class="item item4">
Item 4
</div>
</div>
Fiddle here of same code: https://jsfiddle.net/6ycer7b0/1/

You can do this with a row direction and enable the wrap to simulate a column behavior as the elements will stack above each other:
.container {
width: 500px;
border: 1px solid red;
}
.flexcol {
display: flex;
flex-direction: row;
flex-wrap:wrap;
justify-content: center;
}
.item {
border: 1px solid black;
font-size: 24pt;
text-align: center;
}
.item1, .item4 {
width: 400px;
}
.item2, .item3 {
width: 200px;
}
<div class="container flexcol">
<div class="item item1">
Item 1
</div>
<div class="item item2">
Item 2
</div>
<div class="item item3">
Item 3
</div>
<div class="item item4">
Item 4
</div>
</div>

Related

Flexbox: row with inline-flex + fixed item width (fixing flex-basis bug): not working inside column layout

I want to get a table in CSS with a sticky header (only body scrolls) on Y, and whole table scrolls on X.
According to Michael_B comment on my previous question here there is a flexbox bug and I have to use inline-flex on row + width on cells to make sure my row grows correctly.
This leads me to this result:
html, body {
width: 100%;
min-height: 100vh;
margin: 0;
padding: 0;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
.table {
border: solid black;
width: 60vw;
height: 80vh;
overflow-x: auto;
display: flex;
flex-direction: column;
}
.header {
border: solid cyan;
flex: 0;
}
.body {
flex: 1;
overflow-y: auto;
border: solid yellow;
display: inline-block;
}
.row {
border: solid red;
display: inline-flex;
flex-direction: row;
align-items: center;
}
.cell {
border: solid green;
width: 400px;
height: 50px;
}
<div class="table">
<div class="header">
<div class="row">
<div class="cell">
cell 1
</div>
<div class="cell">
cell 2
</div>
<div class="cell">
cell 3
</div>
</div>
</div>
<div class="body">
<div class="row">
<div class="cell">
cell 1
</div>
<div class="cell">
cell 2
</div>
<div class="cell">
cell 3
</div>
</div>
<div class="row">
<div class="cell">
cell 1
</div>
<div class="cell">
cell 2
</div>
<div class="cell">
cell 3
</div>
</div>
<div class="row">
<div class="cell">
cell 1
</div>
<div class="cell">
cell 2
</div>
<div class="cell">
cell 3
</div>
</div>
</div>
</div>
JsFiddle
As you can see it's not exactly what I want. It seems my rows do not grow according to their content size even with inline-flex, and this happens only when using a flex column layout on the table.
html, body {
width: 100%;
min-height: 100vh;
margin: 0;
padding: 0;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
.table {
border: solid black;
width: 60vw;
height: 80vh;
overflow-x: auto;
}
.header {
border: solid cyan;
flex: 0;
display: inline-block;
height: 50px;
}
.body {
flex: 1;
overflow-y: auto;
border: solid yellow;
display: inline-block;
height: calc(100% - 50px - 16px);
}
.row {
border: solid red;
display: inline-flex;
flex-direction: row;
align-items: center;
}
.cell {
border: solid green;
width: 400px;
height: 50px;
}
By removing flex column, using inline-block and calc computations, I almost get the behavior I want, except I'd like the scrollY scrollbar to always stay visible
<div class="table">
<div class="header">
<div class="row">
<div class="cell">
cell 1
</div>
<div class="cell">
cell 2
</div>
<div class="cell">
cell 3
</div>
</div>
</div>
<div class="body">
<div class="row">
<div class="cell">
cell 1
</div>
<div class="cell">
cell 2
</div>
<div class="cell">
cell 3
</div>
</div>
<div class="row">
<div class="cell">
cell 1
</div>
<div class="cell">
cell 2
</div>
<div class="cell">
cell 3
</div>
</div>
<div class="row">
<div class="cell">
cell 1
</div>
<div class="cell">
cell 2
</div>
<div class="cell">
cell 3
</div>
</div>
</div>
</div>
JsFiddle
Anyone knows how I could achieve the table layout I want? I'm open for no flexbox solution but I don't want a JS based solution for now.

Left and right aligning flex items

I have 4 divs all in the same wrapper, I need the first 3 to align left (text) and the 4th to align right (an image).
There desired effect is as follows.
<one> <
<two> Four
<three> >
The html is basic literally just 4 divs in a wrapper and the css can just be display:flex on the wrapper.
I have partially achieved this by adding flex direction column onto the wrapper so all 4 then stack vertically but I need the 4th to align right as above.
Any help would be hugely appreciated.
Thanks
Using flexbox:
* {
box-sizing: border-box;
}
.container {
height: 500px;
border: 1px solid green;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.item {
flex: 1;
padding: .5rem;
border: 1px dashed lightblue;
}
.one, .two, .three {
flex-basis: 33.333333%;
}
<div class="container">
<div class="item one">1</div>
<div class="item two">2</div>
<div class="item three">3</div>
<div class="item four">4</div>
</div>
Using css-grid:
* {
box-sizing: border-box;
}
.container {
height: 500px;
border: 1px solid green;
display: grid;
}
.item {
padding: .5rem;
border: 1px dashed lightblue;
}
.four {
grid-column: 2;
grid-row: 1 / span 3;
}
<div class="container">
<div class="item one">1</div>
<div class="item two">2</div>
<div class="item three">3</div>
<div class="item four">4</div>
</div>
.wrapper {
display: flex;
flex-direction: row;
}
<div class="wrapper">
<div>
<div>one</div>
<div>two</div>
<div>three</div>
</div>
<div>
<div>four</div>
</div>
</div>

Flex Row space between wrapped items

What is the most dynamic way of having any amount of rows with any amount of items with a set margin between all the items? Right now the only thing that works for me is to wrap each item in a wrapper set the flex basis to the wrapper and the margin to the child. The issues with this is I loose the ability to have each row the same height of the tallest content in the row.
Case 1: Only margin bottom
https://jsfiddle.net/6oaney4e/6/
This works well because the content keep the height of the tallest item on each row
html
<div class="wrapper">
<div class="item">
text
</div>
<div class="item">
text
<br>
line2
</div>
<div class="item">
text
</div>
<div class="item">
text
</div>
</div>
css
.wrapper{
display: flex;
flex-flow: row wrap;
margin: -10px;
padding: 10px;
background: green;
}
.item{
flex: 0 0 50%;
background: orange;
margin-bottom: 10px;
}
Case 2: All around margin
https://jsfiddle.net/6oaney4e/7/
Here for some reason the rows break I'm guessing that's because the row cant fit the items in with the extra margin on the side.
html same as CASE 1
css
.wrapper{
display: flex;
flex-flow: row wrap;
margin: -10px;
padding: 10px;
background: green;
}
.item{
flex: 0 0 50%;
background: orange;
margin: 10px;
}
Case 3: Wrapping items and adding margin to the inner item
https://jsfiddle.net/6oaney4e/8/
That worked but now the items on each row arent really aware of each other and cant have the same height.
html
<div class="wrapper">
<div class="item-wrap">
<div class="item">
text
</div>
</div>
<div class="item-wrap">
<div class="item">
text
<br>
line2
</div>
</div>
<div class="item-wrap">
<div class="item">
text
</div>
</div>
<div class="item-wrap">
<div class="item">
text
</div>
</div>
</div>
css
.wrapper{
display: flex;
flex-flow: row wrap;
margin: -10px;
padding: 10px;
background: green;
}
.item-wrap{
flex: 0 0 50%;
}
.item{
background: orange;
margin: 10px;
}
Is there a way to keep the HTML as in CASE 1 (without the div.item-wrap), have the items on each row the same height as in CASE 1 and have the spacing work like in CASE 3?
Ideally, you do want to use rows and make the .item-wrap divs flex-parents too.
.wrapper {
display: flex;
flex-flow: row wrap;
margin: -10px;
padding: 10px;
background: green;
}
.item-wrap {
flex: 0 0 50%;
display: flex;
}
.item {
background: orange;
margin: 10px;
flex: 1;
}
<div class="wrapper">
<div class="item-wrap">
<div class="item">
text
</div>
</div>
<div class="item-wrap">
<div class="item">
text
<br> line2
</div>
</div>
<div class="item-wrap">
<div class="item">
text
<br> line2
<br> line3
</div>
</div>
<div class="item-wrap">
<div class="item">
text
</div>
</div>
</div>
However if you must retain the existing structure, you'll need to use calc to adjust the flex-basis. Something like this:
.wrapper {
display: flex;
flex-flow: row wrap;
background: green;
justify-content: space-between;
}
.item {
flex-grow:0;
flex-shrink:0;
flex-basis:calc(50% - 10px); /* separate properties for IE11 upport */
background: orange;
margin: 5px;
}
<div class="wrapper">
<div class="item">
text
</div>
<div class="item">
text
<br> line2
</div>
<div class="item">
text
<br> line2
<br> line3
</div>
<div class="item">
text
</div>
</div>

Display Table-Cell: Remove Right and Left Border Space?

I'd like to display four elements side by side. I'm using display: table and display: table-cell to accomplish this:
#container {
width: 960px;
background-color: orange;
height: 400px;
}
#table {
display: table;
table-layout: fixed;
border-spacing: 10px;
width: 100%;
}
div.item {
display: table-cell;
height: 150px;
background-color: blue;
}
<div id="container">
<div id="table">
<div class="item">
Text 1
</div>
<div class="item">
Text 2
</div>
<div class="item">
Text 3
</div>
<div class="item">
Text 4
</div>
</div>
</div>
How can I make the rightmost and leftmost cells flush with the container div's edge and maintain equal spacing between the inner cells?
Thank you!
An easy way would be to use border on cells instead of border-spacing. Then you could cancel out border on the first of last cell. Also, keep the colour same as that of your container.
div.item {
display: table-cell;
border: 10px solid orange; /* Same colour as of the container */
border-left: none; /* reset left border to keep uniform */
}
div.item:last-child { border-right: none; } /* remove from last one */
Example Snippet:
#container {
width: 960px;
background-color: orange;
height: 400px;
}
#table {
display: table;
table-layout: fixed;
width: 100%;
}
div.item {
display: table-cell;
height: 150px;
background-color: blue;
border: 10px solid orange;
border-left: none;
}
div.item:last-child { border-right: none; }
<div id="container">
<div id="table">
<div class="item">
Text 1
</div>
<div class="item">
Text 2
</div>
<div class="item">
Text 3
</div>
<div class="item">
Text 4
</div>
</div>
</div>
You can remove the table div and make container display: flex with space-between justification.
<div id="container">
<div class="item">
Text 1
</div>
<div class="item">
Text 2
</div>
<div class="item">
Text 3
</div>
<div class="item">
Text 4
</div>
</div>
#container {
display: flex;
justify-content: space-between;
}
.item {
background-color: red;
width: 24%;
}
https://jsfiddle.net/kyy7qgLz/2/

Reorder columns in reverse order

I have a list of flex items within a flexbox. The order of the items matter, and for accessibility purposes, the items need to show up in the correct order in the dom.
[[itema][itemb][itemc]]
When the flexbox shrinks I would like to have the items wrap in reverse order, e.g. itema wraps first, etc. Is there any way to have the itema wrap first? Thanks!
Edit:
Here is the code
<div class="flex">
<div class="container">
<div class="item">item1</div>
<div class="item orange">item2</div>
<div class="item blue">item3</div>
</div>
<div class="last-item green">menu</div>
</div>
.flex {
display: flex;
background-color: yellow;
height: 80px;
overflow: hidden;
color: #fff;
}
.container {
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
}
.item {
flex: 0 0 auto;
background-color: red;
height: 80px;
padding: 0 40px;
}
.last-item {
width: 40px;
flex: 0 0 auto;
height: 80px;
}
JSFiddle
All the behavior is as desired except I want the first item to wrap first. Any ideas? Thanks!
You can use the flex-direction: column-reverse to get your solution.
#media (max-width: 768px) {
.flex-box {
display: flex;
flex-direction: column-reverse;
}
}
.first,
.second,
.third {
display: inline-block;
}
<div class="flex-box">
<div class="first">
<p>First Box</p>
<img src="http://placehold.it/300x300" />
</div>
<div class="second">
<p>Second Box</p>
<img src="http://placehold.it/300x300" />
</div>
<div class="third">
<p>Third Box</p>
<img src="http://placehold.it/300x300" />
</div>
</div>
JSfiddle Demo
To make item1 wrap first, you can use flex-wrap: wrap-reverse on your flex container.
Try this simplified version of you code:
<div class="container">
<div class="item">item1</div>
<div class="item orange">item2</div>
<div class="item blue">item3</div>
</div>
.container {
display: flex;
flex: 0 1 auto;
flex-wrap: wrap-reverse;
}
.item {
background-color: red;
height: 80px;
padding: 0 40px;
}
.orange {
background-color: orange;
}
.blue {
background-color: blue
}
See the MDN reference for browser support.

Resources