Shaun
Shaun

Reputation: 33

Adjusting grid from desktop to mobile using flexbox

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?

Upvotes: 0

Views: 3599

Answers (3)

woestijnrog
woestijnrog

Reputation: 1579

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>

Upvotes: 1

Rene van der Lende
Rene van der Lende

Reputation: 5281

- 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>

Upvotes: -1

Michael Benjamin
Michael Benjamin

Reputation: 371223

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.

enter image description here

.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:

enter image description here

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.

Upvotes: 3

Related Questions