Move central div to bottom on mobile - html

On desktop side columns will take a max-width of 175px leaving the center dynamic but on mobile I want to have a 2x1 grid. Also top columns will be 50% width but the height will depend on their content.
How could I move this center element to the bottom taking 100% of the width and leave the side columns next to each other taking each 50% of the width? I don't want to have hidden and duplicated elements.
#container {
display: flex;
}
.column.left,
.column.right {
max-width: 175px;
}
.column.center {
flex: 1;
text-align: center;
background-color: red;
}
.column.left,
.column.right {
text-align: center;
}
<body>
<div id="container">
<div class="column left">this is a long long long long label</div>
<div class="column center">center</div>
<div class="column right">short label</div>
</div>
</body>

You can use flex-wrap and the order property:
#container {
display: flex;
}
.column.left,
.column.right {
max-width: 175px;
}
.column.center {
flex: 1;
text-align: center;
background-color: red;
}
.column.left,
.column.right {
text-align: center;
}
#media all and (max-width: 600px) {
#container {
flex-wrap: wrap;
}
.column.left, .column.right {
max-width: none;
width: 50%;
}
.column.left {
order: 1;
}
.column.right {
order: 2;
}
.column.center {
order: 3;
width: 100%;
}
}
<body>
<div id="container">
<div class="column left">this is a long long long long label</div>
<div class="column center">center</div>
<div class="column right">short label</div>
</div>
</body>

You can do this easily enough with CSS grid and a media query. See the snippet below
#container {
display: grid;
grid-gap: 1rem;
grid-template-columns: var(--gridColTemplate, 1fr 1fr);
}
.column { text-align: center }
.column.left, .column.right { max-width: var(--sideMaxWidth, none) }
.column.center {
grid-column: var(--centerCol, 1/-1);
grid-row: var(--centerRow, 2);
background-color: red;
}
#media only screen and (min-width: 600px) {
:root {
--gridColTemplate: auto 1fr auto;
--centerCol: 2/3;
--centerRow: 1;
--sideMaxWidth: 175px;
}
}
<div id="container">
<div class="column left">this is a long long long long label</div>
<div class="column center">center</div>
<div class="column right">short label</div>
</div>

Related

reallocate items inside a div with responsive pages

I edited literally a few lines from the code taken from this question link
#wrap {
margin: 20px auto;
width: 80%;
}
.separator {
margin-top: 30px;
}
.row {
height: 30px; margin-bottom: 10px; background-color: green;
}
.left,
.right {
width: 33%; height: 30px; line-height: 30px;
display: inline-block;
text-align: center;
background-color: grey;
}
.left { margin-right: 10px; }
.right { margin-left: 10px; }
.center {
min-height: 30px; line-height: 30px;
text-align: center;
background-color: blue;
display: inline-block;
width: 30%;
}
<div id="wrap">
<div class="left">left</div>
<div class="center">center</div>
<div class="right">right</div>
<div class="separator"></div>
<div class="left">left</div>
<div class="center">center</div>
<div class="right">right</div>
</div>
when the sizes of the windows becomes too small, for example on mobile, it will become a mess. When this happens, how can I reallocate items vertically, one items for line, where the left will be the first, the center the second, and so.
I'm actually using this in React, just to know.
With display grid
You can use grid-template-areas on the grid parent selectors and grid-areas on the grid children selectors to place the elements in the order you want them to be displayed in the document despite their order in the HTML. You just change the grid properties in your media query.
#cont {
display: grid;
grid-auto-columns: 1fr;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: auto;
grid-template-areas:
"left-1 center-1 right-1"
"left-2 center-2 right-2";
gap: .5rem;
}
.box {
height: 100px;
background: blue;
margin: 0.5rem;
}
.left-1 {
grid-area: left-1;
}
.left-2 {
grid-area: left-2;
}
.center-1 {
grid-area: center-1;
}
.center-2 {
grid-area: center-2;
}
.right-1 {
grid-area: right-1;
}
.right-2 {
grid-area: right-2;
}
/* mobile */
#media screen and (max-width: 700px) {
#cont {
display: grid;
grid-auto-columns: auto;
grid-template-columns: auto;
grid-template-rows: auto;
grid-template-areas:
"left-1"
"left-2"
"center-1"
"center-2"
"right-1"
"right-2";
gap: .5rem;
}
}
<div id="cont">
<div class="left-1 box">left</div>
<div class="center-1 box">center</div>
<div class="right-1 box">right</div>
<div class="left-2 box">left</div>
<div class="center-2 box">center</div>
<div class="right-2 box">right</div>
</div>
You could do the following without using display grid and just adding a media query:
You however do not have control of re-ordering the elements like you do with grid or flex display, you could use box-ordinal-group to change the order of the elements, however it has been taken out of the standard with the introduction of flex - order and grid.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#cont {
width: calc(100% - 5px);
height: auto;
margin: 5px;
}
.box {
min-width: calc(33.3% - 5px);
height: 100px;
background: blue;
display: inline-block;
}
.box~.box {
margin-top: 5px;
}
#media screen and (max-width: 700px) {
#cont {
width: calc(100% - 5px);
margin: 5px;
}
.box {
min-width: calc(100% - 5px);
height: 100px;
}
}
<div id="cont">
<div class="box">left</div>
<div class="box">center</div>
<div class="box">right</div>
<div class="box">left</div>
<div class="box">center</div>
<div class="box">right</div>
</div>
Using flex box with order
~ Change the visual order of your content when using Flexbox.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#cont {
display: flex;
flex-wrap: wrap;
}
.box {
min-width: calc(33.3% - 10px);
height: 100px;
background: blue;
display: inline-block;
margin: 5px;
}
#media screen and (max-width: 700px) {
#cont {
width: calc(100% - 10px);
margin: 5px;
}
.box {
min-width: calc(100% - 10px);
height: 100px;
}
.box:nth-of-type(1) {
order: 1;
}
.box:nth-of-type(2) {
order: 3;
}
.box:nth-of-type(3) {
order: 5;
}
.box:nth-of-type(4) {
order: 2;
}
.box:nth-of-type(5) {
order: 4;
}
.box:nth-of-type(6) {
order: 6;
}
}
<div id="cont">
<div class="box">left row 1</div>
<div class="box">center row 1</div>
<div class="box">right row 1</div>
<div class="box">left row 2</div>
<div class="box">center row 2</div>
<div class="box">right row 2</div>
</div>
You can use CSS Flexbox to position the items side-by-side on large screens, and use a media query to detect mobile devices and align the items vertically.
#wrap {
margin: 20px auto;
width: 80%;
}
.row {
display: flex;
}
/* mobile */
#media screen and (max-width: 700px) {
.row {
flex-direction: column;
}
}
.box {
width: 100%;
height: 100px;
background: blue;
margin: 0.5em;
}
<div id="wrap">
<div class="row">
<div class="box">left</div>
<div class="box">center</div>
<div class="box">right</div>
</div>
<div class="row">
<div class="box">left</div>
<div class="box">center</div>
<div class="box">right</div>
</div>
</div>

How do I 'shuffle' a set of divs together using flexbox

I'm learning responsive web-development using media queries and I want to know if it's possible to move an element from one div to another without using a script.
On a desktop display it's arranged like
---------
div1 | | div2
div3 | image | div4
div5 | | div6
---------
which is what I was going for.
and on mobile screens I want
---------
| |
| image |
| |
---------
div1
div2
div3
div4
div5
div6
But I can't seem to move divs past their parent divs.
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset = "UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shuffle</title>
</head>
<body>
<div class="header">
<h1>Shuffle</h1>
</div>
<div id="main">
<div class="leftBox">
<div class="boxItem" id="first">
<p>1</p>
</div>
<div class="boxItem" id="third">
<p>3</p>
</div>
<div class="boxItem" id="fifth">
<p>5</p>
</div>
</div>
<div class="image">
<img src="https://images.unsplash.com/photo-1605842581240-a0e2527d200b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ">
</div>
<div class="rightBox">
<div class="boxItem" id="second">
<p>2<p>
</div>
<div class="boxItem" id="fourth">
<p>4</p>
</div>
<div class="boxItem" id="sixth">
<p>6</p>
</div>
</div>
</div>
</body>
</html>
CSS
/*
_______________________________________
MOBILE SCREEN
_______________________________________
*/
* {
box-sizing: border-box;
font-family: "Comic Sans MS", Times, serif;
}
#main {
display: -webkit-flex;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
html {
text-align: center;
}
img{
display: inline-block;
max-width: 100%;
height: auto;
}
.image {
display: inline-block;
width: auto;
max-width: 50%;
border: 1px solid gold;
align-self:center;
order: -1;
}
.header {
text-align: center;
}
.boxItem{
border: 1px solid blue;
padding: 2px;
margin: 5px;
}
.leftBox{
display: flex;
flex-direction: column;
width: 50%;
}
.rightBox{
display: flex;
flex-direction: column;
width: 50%;
}
#first{
order: 1;
}
#second{
order: 2;
}
#third{
order: 3;
}
#fourth{
order: 4;
}
#fifth{
order: 5;
}
#sixth{
order: 6;
}
/*
_______________________________________
DESKTOP SCREEN
_______________________________________
*/
#media only screen and (min-width: 768px) {
#first{
order: 1;
}
#second{
order: 2;
}
#third{
order: 3;
}
#fourth{
order: 4;
}
#fifth{
order: 5;
}
#sixth{
order: 6;
}
img{
align-self: center;
}
.image{
align-self: center;
justify-content: center;
align-items: center;
order: 0;
}
#main{
flex-direction: row;
justify-content: space-between;
}
.leftBox{
display:flex;
flex-direction: column;
justify-content: space-between;
min-height: 100%;
align-self: stretch;
}
.rightBox{
display:flex;
flex-direction: column;
justify-content: space-between;
min-height: 100%;
align-self: stretch;
}
}
If it's not possible to do this with css as it's written here, then is there another way to achieve this style using one flexbox?
I also have this on codepen https://codepen.io/johntarvis/pen/LYRYVmd?editors=1100 if that helps.
You can do it like this. by using columns property and set it to 2. It's really difficult to achieve your approach without jquery. but in the image part you can set it to absolute and make it center part.
Here's a sample code.
.wrapper {
position: relative;
width: 100%;
max-width: 1000px;
margin: 0 auto;
padding: 0;
}
#main {
position: relative;
}
.column {
-webkit-columns: 2;
columns: 2;
text-align: center;
display: flex;
justify-content: space-between;
width: 100%;
flex-wrap: wrap;
}
.sub_col {
display: block;
width: 34%;
min-height: 100px;
border: 1px solid red;
margin: 0 0 1rem;
position: relative;
z-index: 15;
}
.image {
width: 30%;
margin: 0 auto;
padding: 0;
left: 0;
right: 0;
top: 50%;
position: absolute;
transform: translateY(-50%);
}
.image img {
max-width: 100%;
}
#media only screen and (max-width:768px) {
.column {
-webkit-columns: 1;
columns: 1;
}
.sub_col {
width: 100%;
}
.image {
top: 0;
transform: none;
margin-bottom: 1rem;
position: relative;
width: 100%;
}
}
<div class="wrapper">
<div id="main">
<div class="image">
<img src="https://images.unsplash.com/photo-1605842581240-a0e2527d200b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ">
</div>
<div class="column">
<div class="sub_col">1</div>
<div class="sub_col">2</div>
<div class="sub_col">3</div>
<div class="sub_col">4</div>
</div>
</div>
</div>
In this way your div elements are still in tact by numbering 1,2,3,4 and so on.
You can use #media queries and flexbox to achieve the desired result:
#main {
display: flex;
width: 100%;
flex-direction: row;
height: 300px;
}
#main > div { width: 33%;}
.leftBox, .rightBox {
height: 100%; text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
}
img {max-width: 100%; height: auto;}
#media screen and (max-width: 768px) {
#main {
flex-direction: column;
height: auto;
}
#main > div { width: 100%;}
.image {order: -1;}
}
<div class="header">
<h1>Shuffle</h1>
</div>
<div id="main">
<div class="leftBox">
<div class="boxItem" id="first">
<p>1</p>
</div>
<div class="boxItem" id="third">
<p>3</p>
</div>
<div class="boxItem" id="fifth">
<p>5</p>
</div>
</div>
<div class="image">
<img src="https://images.unsplash.com/photo-1605842581240-a0e2527d200b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ">
</div>
<div class="rightBox">
<div class="boxItem" id="second">
<p>2<p>
</div>
<div class="boxItem" id="fourth">
<p>4</p>
</div>
<div class="boxItem" id="sixth">
<p>6</p>
</div>
</div>
</div>
Read more about ordering elements using flex on MDN
This SO related: how can I reorder HTML using media queries?
CSS Grid might be the way to go.
You can maintain a more logical HTML ordering of 1 - 6 and use CSS to organize the display. This also avoids messing around with absolute positioning. The one downside is if you have a dynamic number of rows and want to span the image across all of them, in which case see this answer: https://stackoverflow.com/a/42240114/4665
You will only need to give minimal styling for mobile then replace the code in your desktop media query with the below. Then tweak the style as needed.
#main {
/*Set Grid*/
display: grid;
/*We want 3 equal columns*/
grid-template-columns: 1fr 1fr 1fr;
/*And 3 equal rows -- Can be omitted*/
grid-template-rows: 1fr 1fr 1fr;
/*With a named area for the picture in the middle cell*/
/*grid-template-areas: ". Pic ." ". Pic ." ". Pic .";*/
}
#main>.image {
/*Place the image in column 2*/
grid-column-start: 2;
/*Place the image in the first row, span 3 rows*/
grid-row: 1 / span 3;
/*If Using named area, assign to the named area*/
/*grid-area: Pic; */
}
/*Just for demo purposes, don't move this into your media query!*/
.image>img {
width: 200px;
}
.boxItem {
text-align: center;
}
<div class="header">
<h1>Shuffle</h1>
</div>
<div id="main">
<div class="image">
<img src="https://images.unsplash.com/photo-1605842581240-a0e2527d200b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ">
</div>
<div class="boxItem"><p>1</p></div>
<div class="boxItem"><p>2</p></div>
<div class="boxItem"><p>3</p></div>
<div class="boxItem"><p>4</p></div>
<div class="boxItem"><p>5</p></div>
<div class="boxItem"><p>6</p></div>
</div>

change block order with flex

How to do this kind of markup? So when the resolution is lower than 640px the container number 2 goes to the bottom.
I know that I should use #media (max-width:600px) {}
but I don't really understand how to get the block #2 to bottom from "column right"
Thanks
My example fiddle is https://jsfiddle.net/benderlio/tewzvLxf/3/
#container {
display: flex;
}
.column.left {
width: 60%;
flex: 0 0 1;
background-color: red;
}
.column.right {
padding-top: 30px;
text-align: center;
width: 40%;
flex: 0 0 1;
}
.box {
border: 1px solid red;
margin: 20px;
padding: 20px;
}
<div id="container">
<div class="column left">
</div>
<div class="column right">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
</div>
</div>
I would use grid with a media query and removing the column divs:
* {
box-sizing: border-box;
}
#container {
width: 100%;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-areas:
"a b"
"a c"
"a d";
}
.box {
border: 1px solid red;
margin: 20px;
padding: 20px;
}
.box1 {
grid-area: a;
}
.box2 {
grid-area: b;
}
.box3 {
grid-area: c;
}
.box4 {
grid-area: d;
}
#media (max-width:640px) {
/* adding the commented out areas will allow box1 to keep it's height like in your images so there is a space below box 4 */
#container {
/* grid-template-rows: 1fr 1fr 1fr; */
grid-template-areas:
"a b"
"a d"
/* "a ." */
"c c";
}
}
<div id="container">
<div class="box box1">
1
</div>
<div class="box box2">2</div>
<div class="box box3">3</div>
<div class="box box4">4</div>
</div>
You can do it without changing your structure, like this
#media only screen and (max-width: 640px) {
#container {
flex-direction: column-reverse;
}
}
You can set the flex-direction to have a reverse outcome on columned box.
EDIT
So after the confusion was set out, this is the least I can think of to closely produce what you want. You need to set the container's position to be relative and set the 2nd box's position to be absolute, but this rather a dirty way to do it.
body {}
#container {
display: flex;
}
.column.left {
width: 60%;
flex: 0 0 1;
background-color: red;
}
.column.right {
padding-top: 30px;
text-align: center;
width: 40%;
flex: 0 0 1;
}
.box {
border: 1px solid red;
margin: 20px;
padding: 20px;
}
#media only screen and (max-width: 640px) {
#container {
position: relative;
}
.box:nth-child(2) {
width: 90%;
position: absolute;
bottom: -100px;
left: -20px;
}
}
<div id="container">
<div class="column left">
1
</div>
<div class="column right">
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
</div>
</div>

Staggering items in CSS with flexbox

I'm trying to create a staggered layout in CSS, where each row only has one item, but it will be offset from the items on the other rows.
I've made two working examples so far, but I'm wondering if there is a better way to achieve this.
Here's an example I made using spacer divs :
http://cdpn.io/e/EWrMgL
HTML :
<div class="container">
<div class="row">
<div class="spacer">(spacer)</div>
<div class="content col-2">content 2</div>
<div class="spacer">(spacer)</div>
</div>
<div class="row">
<div class="content col-1">content 1</div>
<div class="spacer">(spacer)</div>
<div class="spacer">(spacer)</div>
</div>
<div class="row">
<div class="spacer">(spacer)</div>
<div class="spacer">(spacer)</div>
<div class="content col-3">content 3</div>
</div>
<div class="row">
<div class="spacer">(spacer)</div>
<div class="spacer">(spacer)</div>
<div class="content col-3">content 3</div>
</div>
</div>
CSS :
.container, .spacer, .content, .row {
display: flex;
}
.container {
flex-flow: row wrap;
font-family: sans-serif;
}
.row {
width: 100%
}
.spacer {
color: #2c3e50;
flex: 1 1 0%;
}
.content {
color: #ecf0f1;
flex : 3 1 0%;
flex-basis: 60%
}
.content, .spacer {
justify-content: center;
align-items: center;
min-height: 5em;
margin: 0.5em;
}
.col-1 {
background-color: #96858F;
}
.col-2 {
background-color:#9099A2;
}
.col-3 {
background-color: #6d7993;
}
I made another attempt at a cleaner version using margins instead of wrapping
each item in a row : http://cdpn.io/e/xqMBmB
HTML:
<div class="container">
<div class="content col-2">content 2</div>
<div class="content col-1">content 1</div>
<div class="content col-3">content 3</div>
<div class="content col-3">content 3</div>
</div>
CSS:
.container, .content{
display: flex;
}
.container {
flex-flow: row wrap;
font-family: sans-serif;
}
.content {
color: #ecf0f1;
flex: 0 1 70%;
justify-content: center;
align-items: center;
min-height: 5em;
margin-bottom: 0.5em;
}
.col-1 {
background-color: #96858F;
}
.col-2 {
background-color:#9099A2;
margin-left: 7.5%;
}
.col-3 {
background-color: #6d7993;
margin-left: 15%;
}
Is there a cleaner way to do this than enclosing each row in a div or changing the margins? I'll want to add #media rules to make the rows 100% width below a certain screen size.
If you change to flex-direction: column and remove wrap, the 2:nd will behave as the 1:st
I also added a media query to drop the left margin at a small width.
With this markup it's also easier to change order and layout, and is most likely the best way to do it.
.container, .content{
display: flex;
}
.container {
flex-direction: column;
font-family: sans-serif;
}
.content {
color: #ecf0f1;
justify-content: center;
align-items: center;
min-height: 5em;
margin-bottom: 0.5em;
}
.col-1 {
background-color: #96858F;
}
.col-2 {
background-color:#9099A2;
margin-left: 7.5%;
}
.col-3 {
background-color: #6d7993;
margin-left: 15%;
}
#media screen and (max-width: 500px) {
.content {
margin-left: 0;
}
}
<div class="container">
<div class="content col-2">content 2</div>
<div class="content col-1">content 1</div>
<div class="content col-3">content 3</div>
<div class="content col-3">content 3</div>
</div>
You can also size them, like this
.container, .content{
display: flex;
}
.container {
flex-direction: column;
font-family: sans-serif;
}
.content {
width: 70%;
color: #ecf0f1;
justify-content: center;
align-items: center;
min-height: 5em;
margin-bottom: 0.5em;
}
.col-1 {
background-color: #96858F;
}
.col-2 {
background-color:#9099A2;
margin-left: 7.5%;
}
.col-3 {
background-color: #6d7993;
margin-left: 15%;
}
#media screen and (max-width: 500px) {
.content {
margin-left: 0;
width: 100%;
}
}
<div class="container">
<div class="content col-2">content 2</div>
<div class="content col-1">content 1</div>
<div class="content col-3">content 3</div>
<div class="content col-3">content 3</div>
</div>
Your second version of the code is fine, it's simple enough. What I would suggest though instead of using specific margins is to use auto margins, change the size of the items to 100% and then specify a max-width so that on smaller devices they will be full width and on larger screens they won't. Here's an example of the CSS for this:
.container, .content{
display: flex;
}
.container {
flex-flow: row wrap;
font-family: sans-serif;
}
.content {
color: #ecf0f1;
flex: 0 1 100%; /* CHANGE */
max-width: 800px; /* CHANGE */
justify-content: center;
align-items: center;
min-height: 5em;
margin-bottom: 0.5em;
}
.col-1 {
background-color: #96858F;
}
.col-2 {
background-color:#9099A2;
margin-left: auto; /* CHANGE */
margin-right: auto; /* CHANGE */
}
.col-3 {
background-color: #6d7993;
margin-left: auto;
}
Maybe that is the result you're after? If not something along those lines should be fine. Using media queries as you suggested would also work, changing the flex size from 70% to 100% below a certain width. You could do that by adding something like the below to the end of your CSS:
#media screen and (max-width: 600px) {
.content {
flex: 0 1 100%;
}
}
Hopefully this will point you in the right direction.

How can I use flexbox to achieve a complex, responsive HTML layout?

I have looked into Flexbox to achieve a responsive layout like pictured below. Unfortunately I still have not figured out how to achieve a desktop layout like Figure 1 which rearranges itself to Figure 2 on viewports smaller than 414 pixel.
Figure 1 (desktop viewports)
Figure 2 (mobile viewports)
(scaled version)
Click here for image in original size
My code so far :
.flexbox {
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
width: 100%;
margin: 0;
flex-direction: row;
}
.content-flexbox.one {
flex-basis: calc(66% - 1rem);
order: 2;
}
.content-flexbox.two {
flex-basis: calc(30% - 1rem);
order: 1;
}
.content-flexbox.three {
order: 3;
}
.content-flexbox.four {
order: 4;
}
.content-flexbox {
margin: 1rem;
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
#media only screen and (max-width: 959px) {
.flexbox {
-flex-direction: column;
padding-top: 1rem;
}
.content-flexbox {
margin: 1rem;
flex: 1;
flex-basis: 100%;
}
.content-flexbox.one {
flex-basis: 100%;
order: 1;
}
.content-flexbox.two {
flex-basis: 100%;
order: 2;
}
}
<div class="flexbox">
<div class="content-flexbox one">
<h1 class="posttitle">Lorem ipsum</h1>
<h2 class="subtitle">dolor sit amet</h2>
</div>
<div class="content-flexbox two">
<img src="http://placehold.it/300x300" />
</div>
<div class="content-flexbox three">
<span>Lorem ipsum dolor</span>
</div>
<div id="container-voting" class="content-flexbox four">
<div class="inner-container set">
<span>Lorem ipsum dolor</span>
</div>
<div class="inner-container get">
<span>Lorem ipsum dolor</span>
</div>
</div>
</div>
My question
Is this even possible with flexbox? Is there a better alternative more suited for this layout?
You’re looking for the experimental grid syntax. Flexbox is good for smaller, widget or component layout systems. Grid is for overall page layout, and it’s awesome.
Thing is, grid is only supported in IE, Edge, and the upcoming Safari browsers right now, but Firefox and Chrome support is allegedly just around the corner, and you can start trying it out today by enabling the right developer flag in those browsers.
Here is some sample code, but again, it will only work if your browser supports the new grid syntax.
*{
box-sizing: border-box;
}
.flexbox{
width: 320px;
display: grid;
grid-template-columns: calc(50% - 0.5ch) calc(50% - 0.5ch);
grid-gap: 1ch;
}
.one{
order: 2;
background-color: red;
}
.two{
grid-column: 1 / 3;
order: 1;
background-color: green;
}
.three{
order: 3;
background-color: pink;
}
.four{
display: grid;
grid-column: 1 / 3;
grid-gap: 1ch;
order: 4;
background-color: lavender;
}
.inner-container{
background-color: violet;
}
#media screen and (min-width: 500px){
.flexbox{
width: 500px;
grid-template-columns: calc(33.333% - 0.333ch) calc(33.333% - 0.333ch) calc(33.333% - 0.333ch);
}
.one{
grid-row: 1 / 3;
order: 1;
}
.two{
order: 2;
grid-column: 2 / 4;
}
.three{
order: 3;
}
.four{
grid-column: 3 / 4;
order: 4;
}
}
<div class="flexbox">
<div class="content-flexbox one">
<h1 class="posttitle">Lorem ipsum</h1>
<h2 class="subtitle">dolor sit amet</h2>
</div>
<div class="content-flexbox two">
<img src="http://placehold.it/300x300" />
</div>
<div class="content-flexbox three">
<span>Lorem ipsum dolor</span>
</div>
<div id="container-voting" class="content-flexbox four">
<div class="inner-container set">
<span>Lorem ipsum dolor</span>
</div>
<div class="inner-container get">
<span>Lorem ipsum dolor</span>
</div>
</div>
Although this question explicitly asked for a flexbox approach, there is another way to achive it using simple floats.
A media query allows to rearange the elements in the desired order on viewports less than 414px wide:
.wrap {
background: #d0d0d0;
padding: 1%;
}
.wrap:after {
content: '';
display: block;
clear: both;
}
.el {
float: left;
margin: 1%;
}
.el1 {
width: 31.33%;
padding-bottom: 31.33%;
background: #FF7676;
}
.el2 {
float: right;
width: 64.66%;
padding-bottom: 14.66%;
background: #C2FF76;
}
.el3 {
width: 31.33%;
padding-bottom: 14.66%;
background: #FF9BF7;
}
.el4 {
width: 31.33%;
padding-bottom: 6.33%;
background: #9BA4FF;
}
#media (max-width: 414px) {
.el2, .el4 {
width: 98%;
padding-bottom: 31.33%;
}
.el1, .el3 {
width: 48%;
padding-bottom: 48%;
}
}
<div class="wrap">
<div class="el el2"></div>
<div class="el el1"></div>
<div class="el el3"></div>
<div class="el el4"></div>
<div class="el el4"></div>
</div>
Note that I used padding-bottom to keep the aspect ratio of the elements in this example (more info in this answer).
I don't know what content you intend to put in the blocks but you will need to use absolute positionnig for it if you want to stick with the "padding technique". For plain text content, you can check this fiddle.
The problem is that, if you want to be able to rearrange all items, they must be flex items of the same flex container. But Flexbox does not provide any direct way to make an element occupy more than one flex line.
However, you can use multiple containers and display: contents:
The element itself does not generate any boxes, but its children and
pseudo-elements still generate boxes as normal. For the purposes of
box generation and layout, the element must be treated as if it had
been replaced with its children and pseudo-elements in the document
tree.
/* Desktop */
.container {
display: flex;
flex-wrap: wrap;
}
.container > * {
flex-grow: 1;
}
.item {
margin: 2px;
}
.column {
flex-direction: column;
}
.fill {
width: 100%;
}
/* Mobile */
#media (max-width: 414px) {
.container > .container {
display: contents;
}
.i2 {
order: -1;
}
.i4 {
width: 100%;
}
}
/* Pretty */
.i1 { background: #FF7676; }
.i2 { background: #C2FF76; }
.i3 { background: #FF9BF7; }
.i4 { background: #9BA4FF; }
<div class="container">
<div class="item i1">1</div>
<div class="container">
<div class="item i2 fill">2</div>
<div class="item i3">3</div>
<div class="container column">
<div class="item i4">4a</div>
<div class="item i4">4b</div>
</div>
</div>
</div>
The only problem is that display: contents is not widely supported yet, but you can see it working on Firefox.
I don't think this is possible to do with pure css, but you could use some js and change html structure on resize with wrapAll() and unwrap(). You also need to use media queries to change order and some css when window is < 414px.
$(window).on("resize", function() {
var windowW = $(window).width();
if (windowW < 414) {
if ($('.right, right-inner').length) $('.two, .four').unwrap();
if (!$('.top').length) $('.one, .two, .three').wrapAll('<div class="top"></div>');
} else {
if ($('.top').length) $('.one, .two, .three').unwrap();
if (!$('.right, .right-inner').length) {
$('.three, .four').wrapAll('<div class="right-inner"></div>');
$('.two, .right-inner').wrapAll('<div class="right"></div>');
}
}
}).resize();
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body,
html {
margin: 0;
padding: 0;
font-family: sans-serif;
}
.flexbox {
display: flex;
min-height: 100vh;
color: white;
font-size: 50px;
}
.one {
flex: 1;
background: #FF7676;
margin: 10px;
}
.right {
flex: 2;
display: flex;
flex-direction: column;
margin: 10px;
}
.two {
height: 40%;
display: flex;
margin-bottom: 20px;
margin-right: 10px;
}
.two img {
width: 100%;
margin: 0;
}
.right-inner {
display: flex;
flex: 2;
}
.three,
.four {
flex: 1;
}
.three {
background: #FF9BF7;
margin-right: 10px;
}
.four {
display: flex;
flex-direction: column;
}
.set,
.get {
background: #9BA4FF;
flex: 1;
margin: 5px;
}
.set {
margin-top: 0;
}
.get {
margin-bottom: 0;
}
#media(max-width: 414px) {
.flexbox {
flex-direction: column;
}
.flexbox > * {
flex: 1;
margin: 10px;
}
.get,
.set {
margin: 0;
margin-bottom: 10px;
}
.two {
order: -1;
flex: 0 0 100%;
margin-bottom: 0;
}
.two img {
height: 100px;
}
.one,
.three {
width: 50%;
margin: 0;
}
.one {
margin-right: 10px;
}
.top {
display: flex;
flex-wrap: wrap;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<div class="flexbox">
<div class="content-flexbox one">
<span class="posttitle">1</span>
</div>
<div class="right">
<div class="content-flexbox two">
<img src="http://placehold.it/300x300/C2FF76" />
</div>
<div class="right-inner">
<div class="content-flexbox three">
<span>3</span>
</div>
<div id="container-voting" class="content-flexbox four">
<div class="inner-container set">
<span>4a</span>
</div>
<div class="inner-container get">
<span>4b</span>
</div>
</div>
</div>
</div>
</div>
I see that you can make two containers with floats and like mentioned before use liquid style page with width %. If you aproche mobile viewport use media querys to declair breakpoints.