Flexbox layout multiple items wrapping around a big one - html

I want to have a layout like the following image using flexbox:
I have tried the following but no luck so far:
.flex-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 200px;
}
.flex-item:not(:first-child) {
width: calc(100% - 50px);
background-color: gray;
border: 1px solid blue;
}
.flex-item:first-child {
width: 50px;
height: 100%;
border: 1px solid blue;
background-color: red;
}
<div class="flex-container">
<div class="flex-item">item 1(long)</div>
<div class="flex-item">item 2</div>
<div class="flex-item">item 3</div>
<div class="flex-item">item 4</div>
</div>
I need the DOM structure to be like this, otherwise, I already know how to achieve this with nested elements.

It's possible using your DOM structure but I doubt it will be particularly useful, and as others have said, you're better off using Grid or even just inline elements. The trouble is you are trying to mix two layout contexts and flexbox isn't really built for that.
Give the container a explicit height and the wider elements a flex:1 property, which will make them grow to fill the remaining space. I used nth-child as it is more readable to me and your original code didn't actually behave as you intended when put into codepen.
.flex-container {
border: 1px solid green;
display: flex;
flex-direction: column;
flex-wrap: wrap;
width: 200px;
height: 200px;
}
.flex-item {
height: 100%;
width: 50px;
background-color: gray;
border: 1px solid blue;
}
.flex-item:nth-child(n+2) {
width: calc(100% - 50px);
flex: 1;
border: 1px solid blue;
background-color: red;
}
<div class="flex-container">
<div class="flex-item">item 1(long)</div>
<div class="flex-item">item 2</div>
<div class="flex-item">item 3</div>
<div class="flex-item">item 4</div>
</div>

CSS Grid Layout would be more appropriate.
body {
background: white;
color: #323232;
font-weight: 300;
height: 100vh;
margin: 0;
font-family: Helvetica neue, roboto;
}
.flex-container {
display: grid;
width: 90%;
height: 50%;
margin: 2rem auto;
text-align: center;
grid-template-columns: 25% 75%;
grid-template-rows: 25% 25% 50%;
}
.flex-item1 {
grid-column: 1 / 2;
grid-row: 1 / 4;
background: pink;
}
.flex-item2 {
grid-column: 2 / 3;
grid-row: 1 / 2;
background: lightcoral;
}
.flex-item3 {
grid-column: 2 / 3;
grid-row: 2 / 3;
background: lemonchiffon;
}
.flex-item4 {
grid-column: 2 / 3;
grid-row: 3 / 4;
background: lightblue;
}
<div class="flex-container">
<div class="flex-item1">item 1</div>
<div class="flex-item2">item 2</div>
<div class="flex-item3">item 3</div>
<div class="flex-item4">item 4</div>
</div>
Although you can do it with Flexbox.
It makes more sense to me to make a small change to the DOM structure.
Your items 2, 3, 4 would be wrapped in an other Flex container.
body {
background: white;
color: #323232;
font-weight: 300;
height: 100vh;
margin: 0;
display: flex;
font-family: Helvetica neue, roboto;
}
.flex-container {
display: flex;
flex-wrap: wrap;
width: 90%;
height: 50%;
margin: 1rem;
text-align: center;
}
.flex-itemA {
width: 25%;
background: pink;
}
/* Flex-itemB and Flex container itself for the next 3 elements */
.flex-itemB {
width: 75%;
display: flex;
flex-wrap: wrap;
flex-direction: column;
background: lightblue;
}
.flex-itemB1,
.flex-itemB2 {
height: 25%;
}
.flex-itemB1 {
background: lightcoral;
}
.flex-itemB2 {
background: lemonchiffon;
}
<div class="flex-container">
<div class="flex-itemA">item A</div>
<div class="flex-itemB">
<div class="flex-itemB1">item B1</div>
<div class="flex-itemB2">item B2</div>
<div class="flex-itemB3">item B3</div>
</div>
</div>
EDIT based on #lawrence-witt answer
Or if you really want to keep the DOM structure that way and use Flexbox and not Grid. Then you could do it like #lawrence-witt suggested.
I kept the :nth-child selectors although it would be easy to add a class for each element and avoid increasing the specificity.
body {
background: white;
color: #323232;
font-weight: 300;
height: 100vh;
margin: 0;
font-family: Helvetica neue, roboto;
}
.flex-container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
width: 90%;
height: 50%;
margin: 2rem 5%;
text-align: center;
}
.flex-item {
height: 100%;
width: 25%;
background: pink;
}
.flex-item:nth-child(n + 2) {
width: 75%;
/* This will make the height = 25% since the last element will have flex-grow: 1 */
flex: 0.5;
}
.flex-item:nth-child(2) {
background: lightcoral;
}
.flex-item:nth-child(3) {
background: lemonchiffon;
}
.flex-item:nth-child(4) {
flex: 1;
background: lightblue;
}
<div class="flex-container">
<div class="flex-item">item 1</div>
<div class="flex-item">item 2</div>
<div class="flex-item">item 3</div>
<div class="flex-item">item 4</div>
</div>

Related

CSS: Refactor grid to a flex layout with different childs

I'm currently trying to build a flex layout which should have 3 possible childs:
If the wrapper has just one button, it should have the full width of the wrapper.
If the wrapper has two childs, the last one should have a max width of 75px and the first one must fill the available space.
If the wrapper has at all three childs, the first row should be like at the example before but the last child should be in the next row with 100% width.
I've build this grid layout but I need change it to flex because of the compatibility of some browsers. How can I do this? I've tried a lot but the result is always bad.
.wrapper {
width: 60%;
display: grid;
display: -ms-grid;
grid-template-columns: 1fr 75px;
text-align: center;
position: relative;
margin-bottom: 20px;
}
.wrapper div+div {
margin-left: 6px;
}
.wrapper div {
background-color: yellow;
padding: .6em 1em;
text-align: center;
}
.wrapper div.show.single-button {
grid-column: 1/span 2;
}
.wrapper div.cancel {
grid-column: 1/span 2;
-ms-grid-row: 2;
-ms-grid-column-span: 2;
margin-top: 6px;
margin-left: 0;
}
<div class="wrapper">
<div class="show single-button">Show</div>
</div>
<div class="wrapper">
<div class="show">Show</div>
<div class="invoice">Invoice</div>
</div>
<div class="wrapper">
<div class="show">Show</div>
<div class="invoice">Invoice</div>
<div class="cancel">Cancel</div>
</div>
Judicious use of flex:1 and flex-wrap seems to work.
* {
box-sizing: border-box;
}
.wrapper {
width: 60%;
margin: auto;
display: flex;
flex-wrap: wrap;
margin-bottom: 20px;
justify-content: space-between;
border: 1px solid green;
}
.wrapper div {
background-color: lightblue;
padding: .6em 1em;
text-align: center;
margin: 6px;
}
.wrapper div.show {
flex: 1;
}
.wrapper div.invoice {
flex: 1;
max-width: 75px;
}
.wrapper div.cancel {
flex: 1 1 100%;
}
<div class="wrapper">
<div class="show single-button">Show</div>
</div>
<div class="wrapper">
<div class="show">Show</div>
<div class="invoice">Invoice</div>
</div>
<div class="wrapper">
<div class="show">Show</div>
<div class="invoice">Invoice</div>
<div class="cancel">Cancel</div>
</div>
Flexbox offers everything you need for this. No need to hack around by mixing flexbox with box-model or width properties:
.wrapper {
height: 100px;
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
}
.wrapper > div {
flex: 1;
border: 1px dotted #999;
background-color: aliceblue;
}
.wrapper > div:nth-child(2) {
flex-shrink: 0;
flex-grow: 0;
flex-basis: 75px;
-webkit-flex-basis: 75px;
}
.wrapper > div:nth-child(3) {
flex-shrink: 0;
flex-grow: 0;
flex-basis: 100%;
-webkit-flex-basis: 100%;
}
<div class="wrapper">
<div>1</div>
</div>
<div class="wrapper">
<div>1</div>
<div>2</div>
</div>
<div class="wrapper">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
You can use flex-basis to tell the initial width of the elements and flex-grow to tell if they should take more space, and flex-wrap so it does not force them to fit in one line, something like:
.wrapper {
display: flex;
flex-wrap: wrap; // make it multiline
margin-bottom: 1rem;
}
.wrapper div {
background: yellow;
margin: .2rem;
padding: .2rem;
text-align: center;
}
.invoice {
flex-basis: 75px; // make it have 75px
flex-grow: 0; // can't grow
flex-shrink: 0; // can't shrink
}
.show {
flex-grow: 1; // no basis width, just grow and take the space left from .invoice
}
.cancel {
flex-basis: 100%; // takes the whole width so it must go on a single row
}
https://codepen.io/anon/pen/KJLqVj

How to make flex item height of its parent?

I have a simple nav type layout using flex to space items with different widths horizontally. The items have lines between but as they different heights because the content had different heights
How can I make the items the height of the parent so all dividing lines go to the top of the parent.
https://codepen.io/anon/pen/zbEMVd
* {
font-family: sans-serif;
}
.wrap {
border: 1px solid grey;
display: flex;
padding: 5px;
justify-content: space-between;
max-width: 1200px;
}
.item:not(:last-of-type) {
border-right: 2px solid red;
}
.item-1 {
width: 150px;
}
.item-2 {
width: 50px;
}
.item-3 {
width: 50px;
}
.item-4 {
width: 50px;
}
.item-5 {
flex: 1;
}
.item-6 {
width: 50px;
}
<div class="wrap">
<div class="item item-1">One</div>
<div class="item item-2">Two Two Two Two Two </div>
<div class="item item-3">Three Three Three</div>
<div class="item item-4">Four</div>
<div class="item item-5">Five</div>
<div class="item item-6">Six</div>
</div>
Remove align-self from .item, and add display:flex; align-items: flex-end; instead.
.item{
display: flex;
align-items: flex-end;
/* ... */
}
https://codepen.io/anon/pen/ZPXVra
Make the item element a reverse-column flexbox (and remove align-self: flex-end from it) to get the effect - see demo below:
* {
font-family: sans-serif;
}
.wrap {
border: 1px solid grey;
display: flex;
padding: 5px;
justify-content: space-between;
max-width: 1200px;
}
.item {
/* align-self: flex-end; */
display: flex; /* ADDED */
flex-direction: column-reverse; /* ADDED */
background: #ddd;
}
.item:not(:last-of-type) {
border-right: 2px solid red;
}
.item-1 {
width: 150px;
}
.item-2 {
width: 50px;
}
.item-3 {
width: 50px;
}
.item-4 {
width: 50px;
}
.item-5 {
flex: 1;
}
.item-6 {
width: 50px;
}
<div class="wrap">
<div class="item item-1">One</div>
<div class="item item-2">Two Two Two Two Two </div>
<div class="item item-3">Three Three Three</div>
<div class="item item-4">Four</div>
<div class="item item-5">Five</div>
<div class="item item-6">Six</div>
</div>

Flex last row to take available vertical space [duplicate]

This question already has answers here:
Stretch columns in two columns layout with shared header using flexbox
(2 answers)
Closed 4 years ago.
I have this layout, where a row wrap flex container has a first child with 100% width and 2 more children on the second row. The container has a fixed height and the first child (Filters block below) is collapsible (i.e. has 2 possibles values for height).
I would like the blocks on the last line to take all available height in all cases (filters block collapsed or expanded), but I can't find a solution.
I've tried various combinations of height, align-items/align-self: stretch, to no avail. Setting the pdt/list blocks height to 100% makes them effectively 100% of parent container, so they overflow due to the filters.
I know I could achieve it by making the first container flex column and throw in a second one with flex row,but I'd like to keep the current markup if possible. Any idea?
JSfiddle: https://jsfiddle.net/Lp4j6cfw/34/
HTML
<div id="lp-tag">
<div id="header">HEADER</div>
<div id="lp-ctnr">
<div id="filters" onclick="toggle()">FILTERS</div>
<div id="pdt">PDT</div>
<div id="list">LIST</div>
</div>
CSS
#lp-tag{
display: flex;
flex-flow: column nowrap;
width: 350px;
margin: auto;
border: 1px solid red;
height: 250px;
}
#header{
background: lightblue;
height: 80px;
}
#lp-ctnr{
display: flex;
flex-flow: row wrap;
justify-content: space-between;
align-content: flex-start;
align-items: stretch;
border: 1px solid green;
flex: 1;
}
#filters{
width: 100%;
background: lightgreen;
height: 45px;
}
.close{
height: 20px !important;
}
#pdt, #list {
flex: 1;
border: 1px solid blue;
text-align: center;
align-self: stretch;
}
#pdt{
background: yellow;
}
#list{
background: pink;
}
If you are open to alternative layout methods, I'd recommend CSS-Grid
.lp-tag {
width: 250px;
margin: 1em auto;
border: 1px solid red;
height: 250px;
display: inline-grid;
grid-template-rows: auto 1fr;
}
.header {
background: lightblue;
height: 80px;
}
.header.small {
height: 40px;
}
.lp-ctnr {
display: grid;
grid-template-rows: auto 1fr;
grid-template-columns: repeat(2, 1fr);
border: 1px solid green;
flex: 1;
}
.filters {
grid-column: 1 / span 2;
background: lightgreen;
height: 45px;
}
.filters.large {
height: 80px;
}
.pdt,
.list {
border: 1px solid blue;
text-align: center;
}
.pdt {
background: yellow;
}
.list {
background: pink;
}
<div class="lp-tag">
<div class="header">HEADER</div>
<div class="lp-ctnr">
<div class="filters" onclick="toggle()">FILTERS</div>
<div class="pdt">PDT</div>
<div class="list">LIST</div>
</div>
</div>
<div class="lp-tag">
<div class="header small">HEADER</div>
<div class="lp-ctnr">
<div class="filters large" onclick="toggle()">FILTERS</div>
<div class="pdt">PDT</div>
<div class="list">LIST</div>
</div>
</div>
This is the only solution I can see without an intermediary container. https://jsfiddle.net/5j38ouvs/
However, I would probably do like Nandita and add a surrounding container like here: https://jsfiddle.net/8md4oyLx/
CSS
#lp-ctnr{
display: flex;
flex-direction: column;
border: 1px solid green;
height: 550px;
width: 350px;
margin: auto;
}
#filters{
width: 100%;
background: lightgreen;
}
.close{
height: 20px !important;
}
#pdt{
flex-grow: 1;
background: yellow;
}
#list{
flex-grow: 1;
background: pink;
}
.list-container {
flex-grow: 1;
border: 1px solid blue;
text-align: center;
display: flex;
flex-direction: row;
}
HTML
<div id="lp-ctnr">
<div id="filters" onclick="toggle()">FILTERS</div>
<div class="list-container">
<div id="pdt">PDT</div>
<div id="list">LIST</div>
</div>
</div>

How to make masonry layout with flexbox

I want to create masonry layout through flexbox.Child element should appear in the following order and also the child are in diffrent height and same width. I am using lazy loading.
1 2
3 4
5 6
Technically it's possible with flex-flow: column-wrap, but you would need to know the height of the container, so items can wrap properly, plus that you would need to reorder flex items since the default order is vertical.
#import url('https://fonts.googleapis.com/css?family=Montserrat');
html,
body {
margin: 0;
font-family: "Montserrat", sans-serif;
font-size: 16px;
}
.container {
height: 500px;
width: 200px;
display: flex;
padding: 2px;
flex-direction: column;
flex-wrap: wrap;
}
.cell {
color: white;
background: #e91e63;
height: 150px;
width: 100px;
display: flex;
justify-content: center;
align-items: center;
border: 2px solid white;
border-radius: 5px;
}
.c2 {
order: 4;
height: 100px;
}
.c3 {
order: 2;
height: 100px;
}
.c4 {
order: 5;
height: 200px;
}
.c5 {
order: 3;
}
.c6 {
order: 6;
height: 100px;
}
<div class="container">
<div class="cell c1">1</div>
<div class="cell c2">2</div>
<div class="cell c3">3</div>
<div class="cell c4">4</div>
<div class="cell c5">5</div>
<div class="cell c6">6</div>
</div>
For others ending up in this question looking for a css-only masonry solution, I recommend the following answer by Michael_B (the only and most complete at the moment):
CSS-only masonry layout but with elements ordered horizontally

Issue with flexbox

I'm new to flexbox and I created the following in order to have a top-navigation, with three columns below the top-navigation. When I open my HTML file the top-navigation is 80px tall but the three columns are only 50%, less the 80px for the header. I don't understand what is going on? Should the three columns not just fill in the difference?
body {
font: 24px Helvetica;
background: #999999;
}
.layout {
min-height: 100vh;
display: flex;
flex-flow: row;
flex-wrap: wrap;
}
.top-navigation {
width: 100vw;
height: 80px;
background: #cc2936;
}
.left-sidebar {
width: 25%;
background: #dcdcdc;
flexgrow: 1;
}
.main-outlet {
width: 50%;
background: #ffffff;
flexgrow: 1;
}
.right-sidebar {
width: 25%;
background: #dcdcdc;
flexgrow: 1;
}
<div class="layout">
<div class="box top-navigation"></div>
<div class="box left-sidebar"></div>
<div class="box main-outlet"></div>
<div class="box right-sidebar"></div>
</div>
I think you need to change your layout a little bit. You can then set the wrapping div to have flex-direction: column and align-content: stretchto the three columns like this:
body {
font: 24px Helvetica;
background: #999999;
margin: 0;
}
.layout {
min-height: 100vh;
display: flex;
flex-direction: column;
flex-flow: row;
flex-wrap: wrap;
align-content: stretch;
}
.top-navigation {
width: 100vw;
height: 80px;
background: #cc2936;
}
.left-sidebar {
width: 25%;
background: #dcdcdc;
}
.main-outlet {
width: 50%;
background: #ffffff;
}
.right-sidebar {
width: 25%;
background: #dcdcdc;
}
<div class="box top-navigation"></div>
<div class="layout">
<div class="box left-sidebar"></div>
<div class="box main-outlet"></div>
<div class="box right-sidebar"></div>
</div>