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.
Related
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>
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>
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>
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
I am trying to achieve a flexbox based transition from this (mobile):
To this (desktop):
However I am struggling to stack the two side panels vertically, my own code generates the main, search and other in a single row. I have not inserted webkit code for the sake of brevity.
Code:
p {
padding: 10px;
}
.flex-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.header {
flex: 1 0 100%;
background-color: pink;
}
.search {
flex: 1 100%;
background-color: yellow;
}
.main {
flex: 1 100%;
background-color: lightblue;
}
.other {
flex: 1;
background-color: Red;
}
#media (min-width: 600px) {
.flex-container {} .search {
flex: 1 0;
order: 2
}
.main {
flex: 3 0;
order: 1;
}
.other {
flex: 1 0;
order: 3
}
}
<div class="flex-container">
<div class="header">
<p>header</p>
</div>
<div class="search">
<p>search</p>
</div>
<div class="main">
<p>main</p>
</div>
<div class="other">
<p>other</p>
</div>
</div>
jsFiddle: https://jsfiddle.net/azizn/d2pmdvc4/
The problem here is that you can't really do that with Flexbox if your main elements (#main, #search and #other) are siblings unless you know the fixed height value of #search (hacky solution with position: absolute):
#header, .flex div {
padding: 1em;
box-sizing: border-box;
border: 1px solid red;
margin-bottom: 1em; }
.flex {
display: flex;
flex-direction: column;
position: relative; }
#main { min-height: 300px; order: 2; }
#other { order: 3; }
/* desktop version */
#media (min-width:768px) {
.flex { flex-direction: row; flex-wrap: wrap; }
#main { width: 60%; }
#search { order: 2; width: 40%; height: 100px }
#other { width: 40%; position: absolute; top: 100px; right: 0; }
}
<div id="header">header</div>
<div class="flex">
<div id="main">main</div>
<div id="search">search</div>
<div id="other">other</div>
</div>
jsFiddle: https://jsfiddle.net/azizn/uhwzyr9b/
So logically you could try to wrap #search and #other inside another container but then you couldn't position #content between them because Flexbox can alter order of siblings only... The only workaround for that is probably JavaScript.
Edit: You can achieve your layout by using good old floats instead of Flexbox:
#header, #main, #search, #other {
padding:1em;
box-sizing:border-box;
border:1px solid red;
margin-bottom:1em;
}
#main { min-height: 300px; }
#media (min-width:768px) {
.container { overflow: auto; }
#main { width: 60%; float: left; }
#search { width:40%; float: right; }
#other { width:40%; float: right; }
}
<div id="header">header</div>
<div class="container">
<div id="search">search</div>
<div id="main">main</div>
<div id="other">other</div>
</div>
jsFiddle: https://jsfiddle.net/azizn/g5vxtbed/