How to make masonry layout with flexbox - html

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

Related

Responsive CSS: shift middle div rows to a separate column after reaching media query break point

I am using CSS media queries to create a responsive layout.
In my current HTML layout I use flexbox to align rows of divs:
<div id="page">
<div id="div-1">DIV 1</div>
<div id="div-2">DIV 2</div>
<div id="div-3">DIV 3</div>
<div id="div-4">DIV 4</div>
</div>
and CSS:
#page {
display: flex;
flex-direction: column;
align-items: center;
}
[id^="div-"] {
border: 1px solid #ccc;
margin-bottom: 10px;
width: 50vw;
}
#div-1 {
height: 50px;
}
#div-2 {
height: 70px;
}
#div-3 {
height: 150px;
}
#div-4 {
height: 100px;
}
Here is Jsfiddle for you to tinker with.
It is what I am after for a smaller viewports, but would like to switch things around on the next media query break point to have 2 middle divs to shift to a separate column on the right like this:
How do I achieve that? It is pretty obvious for me how to shift last few div rows to another column, but not sure how to tackle the middle rows...
Is there a way to do it by using flexbox or grid?
Another possible solution is to use the order-property in combination with Flexbox (drawback: you'll need a tiny bit of extra html and set the height of the #page container; advantages: flexible div heights and gap sizes):
#page {
display: flex;
flex-direction: column;
align-items: center;
}
[id^="div-"] {
border: 1px solid #ccc;
margin-bottom: 10px;
width: 50vw;
}
#div-1 {
height: 50px;
background: lightgreen;
}
#div-2 {
height: 70px;
background: lightyellow;
}
#div-3 {
height: 150px;
background: lightcoral;
}
#div-4 {
height: 100px;
background: lightblue;
}
#media (max-width: 1000px) {
#page {
flex-wrap: wrap;
height: 300px;
}
#small-screen-spacer {
order: 3;
align-self: stretch;
flex: 1 0 100%;
margin-right: 10px;
}
#div-1 {
order: 1;
}
#div-2 {
order: 4;
}
#div-3 {
order: 5;
}
#div-4 {
order: 2;
}
}
<div id="page">
<div id="div-1">DIV 1</div>
<div id="div-2">DIV 2</div>
<div id="div-3">DIV 3</div>
<div id="div-4">DIV 4</div>
<div id="small-screen-spacer"> </div>
</div>
The #small-screen-spacer will fill the entire available vertical space, so that all elements that come after the spacer (defined by the order property) are moved to the second column. Additionally you can set the desired gap between the two columns by setting margin-right on the spacer to your desired value.
Fiddle

Aligning Div Boxes CSS

I would like to align the divs in a way that on the left there is a big expandable box like the ones you can see and the 2 aligning ones one on top of the other on the right right by it
Imagine, instead of boxes 1&3 that you see in the code I've written, to have them as 1 big box and the other 2&4 stay as they are aligned to the big box.
I've been playing around with the flex command but cant get it to work
every time I use the flex commands it doesn't align properly and becomes a pyramid
If someone can help me out and explain it to me I'd appreciate it as I don't understand how to get it to work
.Scrollbox h1 {
color: #ffffff !important;
}
.scroll-con {
display: flex;
flex-flow: row wrap;
justify-content: center;
}
.Scrollbox {
background: rgba(130, 164, 179,0.6);
border: 1px solid #ffffff;
overflow-y: scroll;
width: 300px;
height: 250px;
transition: height 1s ease;
overflow: hidden;
text-align: center;
color: #ffffff;
font-family: 'Poiret One', cursive;
}
.Scrollbox:hover {
height: 500px;
overflow-y: hidden;
}
<div class="scroll-con">
<div class="Scrollbox">1</div>
<div class="Scrollbox">2</div>
<div class="Scrollbox">3</div>
<div class="Scrollbox">4</div>
</div>
From what I understand, you want 1&3 to be one long box and 2&4 to be 2 individual boxes beside it?
You can split them up into two different divs where 2&4 will be children of the right div.
.container {
display: flex;
}
.container>div {
flex: 1;
}
.left {
background: pink;
min-height: 500px
}
.right {
display: flex;
flex-direction: column;
background: blue;
}
.right>div {
flex: 1;
}
.right>div:first-child {
background: yellow;
}
.right>div:last-child {
background: gold;
}
<div class="container">
<div class="left">1 & 3</div>
<div class="right">
<div>2</div>
<div>4</div>
</div>
</div>
Alternatively, you can use grid to do the same.
.container {
display: grid;
grid-template-columns: 1fr 1fr;
}
.left {
min-height: 500px;
grid-row-start: 1;
grid-row-end: 3;
background-color: pink;
}
.left+div {
background-color: yellow;
}
.left+div+div {
background-color: gold;
}
<div class="container">
<div class="left">1 & 3</div>
<div>2</div>
<div>4</div>
</div>

Flexbox layout multiple items wrapping around a big one

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>

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

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>