I'm trying to take content that is laid out with desktop as the priority, and re-flow it for mobile based on that clients wishes, but I'm not able to completely get the elements to fit. Here's the layout on desktop:
+----+----+
| 1 | 2 |
| | |
+---------+
| 3 | 4 |
| | 5 |
+---------+
Elements 4 and 5 are in a wrapping div together because they're each half the height of element 3. Desktop is the priority, so it's be developed with that in mind. Here's what I want to accomplish for mobile:
+----+----+
| 1 |
| |
+---------+
| 4 | 2 |
| 5 | 3 |
+---------+
Using flexbox I've managed to get all the elements repositioned except 2. It just wraps to the next line.
I realize it's not ideal, but is there any way to get the 2nd element to line up under the 3rd to make a 1x2 grid?
Here's the code that I've gotten close with:
#wrapper { display: -webkit-flex; flex-wrap: wrap; align-content: stretch; }
#1 { order: 1; flex: 1 100%; }
#2 { order: 3; flex: 3 50%; }
#3 { order: 4; flex: 4 50%; }
#4&5wrapper { order: 2; flex: 2 50%; }
Is this even possible?
I believe the answer to your question is no. Here's why:
The desktop layout is not a problem with flexbox. You simply need to wrap boxes 4 and 5 in a nested column-direction flex container, and you're done.
.inner-container {
display: flex;
flex-direction: column;
order: 4;
flex: 0 1 calc(50% - 10px - 2px); /* width less margin less borders */
}
However, for the mobile layout, which requires boxes 2 and 3 to adjust from a row-based wrap in the outer flex container to a column-direction stack in an inner flex container (like with boxes 4 and 5), you would need to change the mark-up.
The best you can do (or at least I could do) without changing the mark-up is this:
Here's the code from the demos in case you're interested in playing with it:
HTML
<div class="outer-container">
<span class="box box1">1</span>
<span class="box box2">2</span>
<span class="box box3">3</span>
<div class="inner-container">
<span class="box box4">4</span>
<span class="box box5">5</span>
</div>
</div>
CSS (includes non-essential decorative styles)
body { display: flex; align-items: flex-start; }
.outer-container {
display: flex;
flex-wrap: wrap;
padding: 6px 0;
background-color: #f5f5f5;
border: 1px solid #ccc;
width: 250px;
box-sizing: border-box;
margin-right: 15px;
}
.box {
height: 100px;
width: 50px;
margin: 5px;
background-color: lightgreen;
border: 1px solid #ccc;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.2em;
}
.inner-container {
display: flex;
flex-direction: column;
order: 4;
flex: 0 1 calc(50% - 10px - 2px); /* width less margin less borders */
}
.box1 { flex: 0 1 calc(50% - 10px - 2px); order: 1; }
.box2 { flex: 0 1 calc(50% - 10px - 2px); order: 2; }
.box3 { flex: 0 1 calc(50% - 10px - 2px); order: 3; }
.box4 { flex: 0 1 calc(50% - 10px - 2px); width: 100%; }
.box5 { flex: 0 1 calc(50% - 10px - 2px); width: 100%; }
div.outer-container:last-of-type > .box1 { flex: 0 1 calc(100% - 10px - 2px); order: 1;}
div.outer-container:last-of-type > .inner-container { order: 2; margin-right: 10px; }
div.outer-container:last-of-type > .box2 { order: 3; }
div.outer-container:last-of-type > .box3 { order: 4; flex: 1 1 100%; }
DEMO: http://jsfiddle.net/dLgjuyw6/1/ (Firefox)
EDIT: Just noticed that demo code (as pictured) works in FF but not Chrome. As this demo is solely for illustration purposes and non-essential to the answer I'm not pursuing browser compatibility.
I realise you've already accepted no as an answer but I'm not yet convinced.
I think the following solution answers the question. Note that I've chosen the flex-bases(?) so that their sum is 100% but you don't have to do that. That way there is some leeway to adjust to the content.
To see what happens watch the solution in full screen and make your browser window narrow (< 600 px). Or watch it on codepen.
#one {background: lightblue;}
#two {background: skyblue;}
#three {background: blue;}
#four {background: aqua;}
#five {background: lime;}
div {flex: 1;}
#wrapper {width: 100%; height: 95vh; border: 1px solid;}
#media (min-width:600px){
#wrapper {
display: flex;
flex-flow: column wrap;
padding: 0;
margin: 0;
}
div {width: 50%;}
#one{
order: 1;
flex-basis: 66%;
}
#two{
order: 2;
flex-basis: 66%;
}
#three{
order: 1;
flex-basis: 34%;
}
#four{
order: 2;
flex-basis: 17%;
}
#five{
order: 2;
flex-basis: 17%
}
}
#media (max-width:599px){
#wrapper {
display: flex;
flex-flow: row wrap;
padding: 0;
margin: 0;
}
#one{
order: 1;
flex-basis: 100%;
min-height: 40vh;
}
#two{
order: 3;
flex-basis: 50%;
}
#three{
order: 5;
flex-basis: 50%;
}
#four{
order: 2;
flex-basis: 50%;
}
#five{
order: 4;
flex-basis: 50%
}
}
<div id='wrapper'>
<div id='one'>1</div>
<div id='two'>2</div>
<div id='three'>3</div>
<div id='four'>4</div>
<div id='five'>5</div>
</div>
- UPDATED -
The answer is YES,
...but with some trickery and maybe not the flexing you might like with 2 flex wrappers side-by-side. Splitting the problem in two, a regular flex container (1&2&3) and some relative positioning of a second (4&5) you will get the required layout.
I am not sure this is the best to do or even the answer you wanted, but maybe it is just what you needed.
Have a look at the snippet and let me know!
/* Global stuff */
html, body { box-sizing: border-box; height: 100%; width: 100%;
margin: 0; padding: 0; border: 0 }
*, *:before,
*:after { box-sizing: inherit }
/* Solution */
#w1 { display: flex;
flex-flow: column wrap; /* vertical arrangement */
align-items: flex-end; /* forces 2&3 to lower right */
with: 100%; height: 100% } /* full screen */
#i1 { width: 100%; height: 50% } /* full width, half height of #w1 */
#i2,#i3 { width: 50%; height: 25% } /* half width, half height of #w1 */
#w2 { display: flex; flex-direction: column;/* stay unchanged */
position: absolute; top: 50%; /* nasty, but works */
width: 50%; height: 50% } /* stay unchanged */
#i4,#i5 { width: 100%; height: 50% } /* stay unchanged */
#i1::before { content: 'mobile '}
#media all and (min-width: 720px) {
#i1::before { content: 'desktop '}
/* become equal in size */
#i1,#i2,#i3 { width: 50%; height: 50% } /* half width, half height of #w1 */
#w1 { flex-flow: row wrap; /* horizontal arrangement */
justify: content: flex-start }/* forces 1&2&3 to upper left */
#w2 { right: 0 } /* reposition, nasty, but works */
}
/* Demo */
#w1 *, #w2 * { text-align: center; font-size: 40px; font-weight: bold;
background: #f0f0f0; border: 2px solid black }
<div id="w1">
<div id="i1">1</div>
<div id="i2">2</div>
<div id="i3">3</div>
</div>
<div id="w2">
<div id="i4">4</div>
<div id="i5">5</div>
</div>
Related
I have a pretty simple page setup in the following manner using flexboxes:
The blue div is supposed to make up 25% in height and the violet div 75%. In case there are too many lines in the blue div, it should stay the same size an show a scrollbar. This works for a few lines, but breaks at some point and the blue div overflows and grows into the violet one. I'm new to flexboxes, so I don't really understand why this is happening. Would I be better off not using flexboxes? Thankful for any hints or pointer at this point.
This is the code I use (run in full page):
function lines(noLines) {
var text = "line</br>".repeat(noLines);
document.getElementById("lower").innerHTML = text;
}
* {
box-sizing: border-box;
}
.body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
#static1 {
height: 30px;
width: 100%;
background-color: red;
}
#static2 {
height: 40px;
width: 100%;
background-color: orange;
}
#content {
display: flex;
flex: 1;
}
#left {
width: 40%;
background-color: yellow;
}
#right {
width: 60%;
display: flex;
flex-direction: column;
}
#upper {
flex: 3 0;
background-color: violet;
}
#lower {
flex: 1;
background-color: blue;
overflow: auto;
}
<div class="body">
<div id="static1">Some static div</div>
<div id="static2">Another static div. Flexbox below fills rest of remaining screen.</div>
<div id="content">
<div id="left">
Left part, fixed width in percentage.</br>
Click to enter lines into the bottom right:</br>
<button onclick=lines(20)>Few Lines</button>
<button onclick=lines(200)>Many Lines</button>
</div>
<div id="right">
<div id="upper">Flexbox with flex=3.</div>
<div id="lower">Flexbox with flex=1.</div>
</div>
</div>
</div>
For the overflow property to work properly, the container needs an actual height or max-height. Flex heights (you have flex: 1 on .content) won't cut it.
In order for overflow to have an effect, the block-level container
must have either a set height (height or max-height) or
white-space set to nowrap. ~ MDN
Since you already know the height of the primary container (100vh) and the first two rows (30px and 40px), the rest is simple using the calc() function.
function lines(noLines) {
var text = "line</br>".repeat(noLines);
document.getElementById("lower").innerHTML = text;
}
.body {
display: flex;
flex-direction: column;
height: 100vh; /* adjustment */
}
#static1 {
flex-shrink: 0; /* disable shrinking */
height: 30px;
/* width: 100%; */
background-color: red;
}
#static2 {
flex-shrink: 0; /* disable shrinking */
height: 40px;
/* width: 100%; */
background-color: orange;
}
#content {
height: calc(100vh - 70px); /* new */
display: flex;
/* flex: 1; */ /* may work in some browsers, but not reliable */
}
#left {
width: 40%;
background-color: yellow;
}
#right {
width: 60%;
display: flex;
flex-direction: column;
}
#upper {
flex: 3 0;
background-color: violet;
}
#lower {
flex: 1;
background-color: aqua; /* adjusted for illustration */
overflow: auto;
}
body {
margin: 0; /* new; override browser default */
}
* {
box-sizing: border-box;
}
<div class="body">
<div id="static1">Some static div</div>
<div id="static2">Another static div. Flexbox below fills rest of remaining screen.</div>
<div id="content">
<div id="left">
Left part, fixed width in percentage.<br> Click to enter lines into the bottom right:<br>
<button onclick=lines(20)>Few Lines</button>
<button onclick=lines(200)>Many Lines</button>
</div>
<div id="right">
<div id="upper">Flexbox with flex=3.</div>
<div id="lower">Flexbox with flex=1.</div>
</div>
</div>
</div>
jsFiddle demo
I hope this is what you mean, but If I'm wrong, apologies. The problem I can see lies in the way you are using flex: 1 & flex: 3 to define the proportions of the right column, without specifying to what height their parent container has, i.e. #right has no height, so the box can always expand as it gets more filled with content.
Please try this, I hope this works and if I can answer anything else, just ask please.
The only thing I changed was your CSS and added max-height: calc(100vh - 70px); to the #right div. And changed overflow: auto; to overflow-y: scroll;
* {
box-sizing: border-box;
}
.body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
#static1 {
height: 30px;
width: 100%;
background-color: red;
}
#static2 {
height: 40px;
width: 100%;
background-color: orange;
}
#content {
display: flex;
flex: 1;
}
#left {
width: 40%;
background-color: yellow;
}
#right {
width: 60%;
display: flex;
flex-direction: column;
max-height: calc(100vh - 70px);
}
#upper {
flex: 3;
height: 75%;
background-color: violet;
}
#lower {
flex: 1;
height: 25%;
background-color: blue;
overflow-y: scroll;
}
Change the top part of CSS to this:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
I have a .container
with 2 columns and in the second column .right I want to have 2 rows
but the first row .content should use the remaining space
this is the HTML markup
<div class="container">
<div class="left">left</div>
<div class="right">
<div class="content">content</div>
<div class="footer">footer</div>
</div>
</div>
this is the CSS
.container {
display: flex;
flex-direction: row;
height: 400px; /* the remaining space in the screen will be nice */
}
.left {
width: 300px;
}
.right {
flex-grow: 1;
}
.content {
align-self: stretch; /* this is not doing anything*/
}
.footer {
}
and this is how it should look
+--------+----------------------+
| | |
| | |
| | content |
| left | |
| | |
| +----------------------+
| | footer |
+--------+----------------------+
You can use a full height flexbox container:
nest a column flexbox inside for the right element,
use flex-grow: 1 on content to occupy remaining space.
See demo below:
body {
margin: 0;
}
.container {
display: flex;
/* flex-direction: row; <-- omit this, its default */
height: 100vh; /* full height */
}
.left {
width: 300px;
border: 1px solid;
}
.right {
flex-grow: 1; /* occupy remaining space */
display: flex;
flex-direction: column; /* column flexbox */
border: 1px solid;
}
.content {
flex-grow: 1; /* occupy remaining space */
border-bottom: 1px solid;
}
<div class="container">
<div class="left">left</div>
<div class="right">
<div class="content">content</div>
<div class="footer">footer</div>
</div>
</div>
Flexbox works just fine here but I wanted to point out this is also a perfect situation for CSS Grid. With grid you can get rid of the extra container div.right.
<div class="container">
<div class="left">left</div>
<div class="content">content</div>
<div class="footer">footer</div>
</div>
.container {
display: grid;
grid-template-columns: 300px 1fr;
grid-template-rows: 1fr 100px; /* change the 100px to your desired height of the footer*/
grid-template-areas:
'left content'
'left footer';
height: 400px; /* height OP selected */
}
.left {
grid-area: left;
}
.content {
grid-area: content;
}
.footer {
grid-area: footer;
}
A CodePen just for fun.
Working example jsfiddle
.container {
display: flex;
flex-direction: row;
height: 100VH;
}
.left {
width: 200px;
border: 1px solid;
}
.right {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.content {
border: 1px solid #333;
border-bottom: 0;
flex-grow: 1;
}
.footer {
border: 2px solid;
height: 100px;
}
I have created a form with some text elements aligned vertically like so:
They are centered horizontally and vertically on the page using flexbox:
.my-class {
display: flex;
justify-content: center;
align-items: center;
flex-direction:column;
}
What I'm trying to do now is maintain this alignment (i.e. keep everything that's on the page already exactly where it is) while adding some elements on either side of the first text box. I tried wrapping everything in a div but since the elements on either side of the text box are not the same width, the text box loses its alignment:
As you can see, the long text boxes are now out of alignment. How can I add elements before and after the first text box without moving where it is?
Assuming it is the url/pass/button that are the one's to be centered, and the https/path stick on each side, I would do it like this, where I use a flex row container and pseudo elements to break each group of item into lines of their own.
With this markup one also have full control to move around the items based on screen width's etc.
The 2 main things making this work is the pseudo elements, that, with their full width, force them into rows of their own, and at the same time push content down, together with the order property, enable to position them before the pass and auth respectively.
Stack snippet
.flex {
display: flex;
flex-flow: row wrap;
justify-content: center; /* horiz. center items */
align-content: center; /* vert. center wrapped items */
/*align-items: center;*/ /* vert. center unwrapped items */
}
.flex div:nth-child(1),
.flex div:nth-child(3) { /* https/path item */
flex: 1; /* share space left equal */
}
.flex div:nth-child(2),
.flex div:nth-child(4) { /* url/pass item */
flex-basis: 300px; /* need equal width */
}
.flex::before { /* 1st line breaker */
content: ''; width: 100%;
order: 1;
}
.flex div:nth-child(4) {
order: 2;
}
.flex::after { /* 2nd line breaker */
content: ''; width: 100%;
order: 3;
}
.flex div:nth-child(5) {
order: 4;
}
/* styling */
.flex {
height: 200px;
border: 1px solid red;
}
.flex span {
display: inline-block;
border: 1px solid gray;
padding: 2px;
margin: 2px;
}
.flex div:nth-child(2) span,
.flex div:nth-child(4) span {
width: calc(100% - 10px);
}
.flex div:nth-child(1) {
text-align: right;
}
<div class="flex">
<div><span>http(s)</span></div>
<div><span>url</span></div>
<div><span>path</span></div>
<div><span>***</span></div>
<div><span>authenticate</span></div>
</div>
If the width of the url/pass should scale with parent's width, use percent combined with CSS Calc.
Stack snippet
.flex {
display: flex;
flex-flow: row wrap;
justify-content: center;
align-content: center;
}
.flex div:nth-child(1),
.flex div:nth-child(3) {
flex: 1;
}
.flex div:nth-child(2),
.flex div:nth-child(4) {
flex-basis: 60%;
}
.flex::before {
content: ''; width: 100%;
order: 1;
}
.flex div:nth-child(4) {
order: 2;
}
.flex::after {
content: ''; width: 100%;
order: 3;
}
.flex div:nth-child(5) {
order: 4;
}
/* styling */
.flex {
height: 200px;
border: 1px solid red;
}
.flex span {
display: inline-block;
border: 1px solid gray;
padding: 2px;
margin: 2px;
}
.flex div:nth-child(2) span,
.flex div:nth-child(4) span {
width: calc(100% - 10px);
}
.flex div:nth-child(1) {
text-align: right;
}
<div class="flex">
<div><span>http(s)</span></div>
<div><span>url</span></div>
<div><span>path</span></div>
<div><span>***</span></div>
<div><span>authenticate</span></div>
</div>
Another option would be to keep the initial flex column direction, and with an extra wrapper use absolute positioning for the http(s)/path items.
.flex {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.flex > div:nth-child(-n+2) {
position: relative;
width: 60%;
}
.flex div div:nth-child(1) {
position: absolute;
top: 0;
right: 100%;
}
.flex div div:nth-child(3) {
position: absolute;
top: 0;
left: 100%;
}
/* styling */
.flex {
height: 200px;
border: 1px solid red;
}
.flex span {
display: inline-block;
width: calc(100% - 10px);
border: 1px solid gray;
padding: 2px;
margin: 2px;
}
.flex div div:nth-child(1) {
text-align: right;
}
.flex div div:nth-child(1),
.flex div div:nth-child(3) {
width: auto;
}
<div class="flex">
<div>
<div><span>http(s)</span></div>
<div><span>url</span></div>
<div><span>path</span></div>
</div>
<div><span>***</span></div>
<div><span>authenticate</span></div>
</div>
Updated (based on another question with a similar need)
One can also keep the simpler markup, with no extra wrapper, and use inline-flex combine with making the flex parent also a flex container.
Stack snippet
body {
text-align: center;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.flex {
position: relative;
display: inline-flex;
flex-direction: column;
align-items: center;
}
.flex div:nth-child(2),
.flex div:nth-child(4) {
width: 300px;
}
.flex div:nth-child(1) {
position: absolute;
top: 0;
right: 100%;
}
.flex div:nth-child(3) {
position: absolute;
top: 0;
left: 100%;
}
/* styling */
.flex {
border: 1px solid red;
}
.flex span {
display: inline-block;
width: calc(100% - 10px);
border: 1px solid gray;
padding: 2px;
margin: 2px;
text-align: left;
}
.flex div div:nth-child(1) {
text-align: right;
}
.flex div div:nth-child(1),
.flex div div:nth-child(3) {
width: auto;
}
<div class="flex">
<div><span>http(s)</span></div>
<div><span>url</span></div>
<div><span>path</span></div>
<div><span>***</span></div>
<div><span>authenticate</span></div>
</div>
I currently have a model like this
.parent
.child1
.child2
.child3
.child4
The request is: 'parent' is a row that takes full width of the device.
In big screen, there is one row with 4 children.
In smaller screen, there are 2 rows with 2 columns each.
And in the extra small screen there are 1 column with 4 rows.
Is there any way that I can achieve the request using only Flexbox? (because I hate Boostrap so much...)
I tried flex-wrap: wrap for the parent and flex: 1 for the children, but failed :(
Thank you :)
This is a SCSS mixin that does that:
#mixin n-columns($min-width, $gutter, $last-equal:false, $max-cols:5, $selector:'.colItem'){
display: flex;
flex-wrap: wrap;
margin-left: -$gutter;
// margin-top: -$gutter;
position: relative;
top: -$gutter;
> #{$selector} {
flex: 1 0 auto;
margin-left: $gutter;
margin-top: $gutter;
#if $last-equal {
#for $i from 2 through $max-cols {
$screen-width: ($min-width*$i)+($gutter*$i);
$column-width: (100%/$i);
#media( min-width: $screen-width) {
max-width: calc(#{$column-width} - #{$gutter});
}
}
$column-width: (100%/$max-cols);
#media( min-width: $min-width*$max-cols) {
min-width: calc(#{$column-width} - #{$gutter});
}
}
}
}
You use it like so:
.parent{
#include n-columns(200px, 3px, true, 5);
}
You'll understand all the things it can do after you use it with different settings and see the results, it's pretty straightforward.
.container {
display: flex;
}
.box {
flex: 1;
}
#media ( max-width: 800px ) {
.container { flex-wrap: wrap; }
.box { flex: 0 0 50%; box-sizing: border-box; }
}
#media ( max-width: 500px ) {
.box { flex-basis: 100%; }
}
/* non-essential decorative styles */
.box {
height: 50px;
background-color: lightgreen;
border: 1px solid #ccc;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.2em;
}
<div class="container">
<div class="box"><span>1</span></div>
<div class="box"><span>2</span></div>
<div class="box"><span>3</span></div>
<div class="box"><span>4</span></div>
</div>
jsFiddle
I want to separate my page into a few columns. Actually I need only three but I may need a few more later.
So I used this:
<style>
.container {
display: flex;
}
.column {
flex: 1;
/*for demo purposes only */
background: #f2f2f2;
/*border: 1px solid #e6e6e6;*/
box-sizing: border-box;
padding-left: 100px;
padding-right: 100px;
padding-top: 50px;
text-align: center;
}
.column-one {
order: 1;
}
.column-two {
order: 2;
}
.column-three {
order: 3;
}
</style>
So my page is perfectly separated in three distinct and equal columns. But I want the second smaller than the others (50%).
I already tried to reduce the width of .column-two class but it did not work.
Is it possible to do this with this king of code ?
I really like this structure because if I have to add one more column I do not have to change the whole CSS. That is why I want to keep this code.
.container {
display: flex;
min-height: 200px;
}
.column {
background: #f2f2f2;
border: 1px solid black;
margin: 3px;
}
.column-one {
flex: 1;
}
.column-two {
flex: 0.5;
}
.column-three {
flex: 1;
}
<div class="container">
<div class="column column-one"></div>
<div class="column column-two"></div>
<div class="column column-three"></div>
</div>
If You want to use four column later then.
.column-one {
flex: 1;
}
.column-two {
flex: 0.5;
}
.column-three {
flex: 0.5;
}
.column-four {
flex: 1;
}
If you add flex: 1 to column-two and flex: 2 to other two columns, column-two will be half width or 50% width of other two column. Also you don't need to use order if you are not going to change it.
.container {
display: flex;
min-height: 100vh;
flex-direction: row;
}
.column {
/*for demo purposes only */
background: #f2f2f2;
/*border: 1px solid #e6e6e6;*/
box-sizing: border-box;
padding-top: 50px;
text-align: center;
border: 1px solid black;
margin: 5px;
}
.column-one {
flex: 2;
}
.column-two {
flex: 1;
}
.column-three {
flex: 2;
}
<div class="container">
<div class="column column-one"></div>
<div class="column column-two"></div>
<div class="column column-three"></div>
</div>