Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flexbox children, mix of rows and columns

Tags:

html

css

flexbox

I am using flexbox to align my 4 elements in a row.

enter image description here

I then want to break this down for mobile like so:

enter image description here

I have successfully reordered the elements here:

.flexcontainer {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: row;
  flex-direction: row;
  -webkit-align-items: flex-start;
  align-items: flex-start;
  width: 90%;
  margin: 0 auto;
  background: red;
}

.flexcontainer>div {
  height: 100px;
  width: 25%;
  background-color: #E46119;
  border: 1px solid #626262;
  margin: 3px;
}

.flexcontainer>div:nth-of-type(1) {
  -webkit-flex: 1 0 0;
  flex: 1 0 0;
  order: 3;
}

.flexcontainer>div:nth-of-type(2) {
  -webkit-flex: 2 0 0;
  flex: 2 0 0;
  order: 2;
}

.flexcontainer>div:nth-of-type(3) {
  -webkit-flex: 2 0 0;
  flex: 2 0 0;
  order: 1;
}

.flexcontainer>div:nth-of-type(4) {
  -webkit-flex: 1 0 0;
  flex: 1 0 0;
  order: 4;
}
<div class="flexcontainer">
  <div>one</div>
  <div>two</div>
  <div>three</div>
  <div>four</div>
</div>

But I am stuck on how to break down child elements "two" and "three" into their own rows. And then how to make element "one" and "four" each 50% wide on their own row.

Is what I'm trying to do possible without additional HTML markup? Thanks for your advice.

.flexcontainer {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: row;
  flex-direction: row;
  -webkit-align-items: flex-start;
  align-items: flex-start;
  width: 90%;
  margin: 0 auto;
  background: red;
}

.flexcontainer>div {
  height: 100px;
  width: 25%;
  background-color: #E46119;
  border: 1px solid #626262;
  margin: 3px;
}

.flexcontainer>div:nth-of-type(1) {
  -webkit-flex: 1 0 0;
  flex: 1 0 0;
}

.flexcontainer>div:nth-of-type(2) {
  -webkit-flex: 2 0 0;
  flex: 2 0 0;
}

.flexcontainer>div:nth-of-type(3) {
  -webkit-flex: 2 0 0;
  flex: 2 0 0;
}

.flexcontainer>div:nth-of-type(4) {
  -webkit-flex: 1 0 0;
  flex: 1 0 0;
}
<div class="flexcontainer">
  <div>one</div>
  <div>two</div>
  <div>three</div>
  <div>four</div>
</div>
like image 941
nick Avatar asked Oct 20 '15 23:10

nick


2 Answers

The desktop-mobile transition can be achieved with CSS only using flexbox.

No changes are necessary in the HTML.

.flexcontainer {
  display: flex;
  width: 90%;
  margin: 0 auto;
  background: red;
}

.flexcontainer > div {
  flex: 0 0 25%;
  height: 100px;
  background-color: #E46119;
  border: 1px solid #626262;
  margin: 3px;
}

.flexcontainer > div:nth-of-type(1) { flex: 1 0 0; }
.flexcontainer > div:nth-of-type(2) { flex: 2 0 0; }
.flexcontainer > div:nth-of-type(3) { flex: 2 0 0; }
.flexcontainer > div:nth-of-type(4) { flex: 1 0 0; }

@media screen and ( max-width: 500px) {
  .flexcontainer { flex-wrap: wrap; }
  .flexcontainer > div:nth-of-type(1) { order: 3; flex-basis: 34%;  }
  .flexcontainer > div:nth-of-type(2) { order: 2; flex-basis: 70%; }
  .flexcontainer > div:nth-of-type(3) { order: 1; flex-basis: 70%; }
  .flexcontainer > div:nth-of-type(4) { order: 4; flex-basis: 34%;  }

}
<div class="flexcontainer">
  <div>one</div>
  <div>two</div>
  <div>three</div>
  <div>four</div>
</div>

jsFiddle demo

How it works

  • The media query kicks in when the screen is 500px or less.
  • The order property sets the order of items on the screen. The default value is 0 for all items.
  • With flex-wrap: wrap on the container, flex items can now wrap.
  • With flex-grow set to a positive integer, there's no need for flex-basis to be precise. Since flex-grow will consume free space on the row, flex-basis only needs to be large enough to force a wrap.
  • If a precise flex-basis value is preferred, any borders, padding and margins would need to be factored in, maybe using box-sizing: border-box and/or calc (example).
like image 182
Michael Benjamin Avatar answered Oct 22 '22 21:10

Michael Benjamin


You could group "three" and "two" into their own flex box and use flex-wrap to achieve this.

Here is a JSFiddle: https://jsfiddle.net/zw10dzzn/3/

You may have to play around with the margins and the order to get exactly the layout you want.

.flex-container {
    display: flex;
    align-items: flex-start;
    width: 90%;
    margin: 0 auto;
    background: red;
    flex-wrap: wrap; /* allow elements to wrap in mobile view */
}

.flex-container .one, 
.flex-container .two-and-three, 
.flex-container .four {
    background-color: magenta;
}

.flex-container .one, 
.flex-container .four {
    height: 100px;
    margin: 3px;
    flex-grow: 1;
    flex-shrink: 0;
    flex-basis: auto;
}

.flex-container .two-and-three {
    order: 1;
    display: flex;
    flex: 0 1 100%;
    flex-wrap: wrap;
}

.flex-container .two-and-three .two, 
.flex-container .two-and-three .three {
    background-color: #FC0;
    flex: 1 0 100%;
    margin: 3px;
    height: 100px;
}

.flex-container .two-and-three .two {
    order: 2;
}

.flex-container .two-and-three .three {
    order: 1;
}

.flex-container .one {
    order: 3;
}

.flex-container .four {
    order: 4;
}

@media (min-width: 768px) {
    
    .flex-container {
        flex-wrap: nowrap; /* back to single row */
    }
    
    .flex-container .two-and-three {
        flex-grow: 4;
        flex-basis: auto; /* stop spanning the whole row */
        flex-wrap: nowrap; /* back to single row */
    }
    
    .flex-container .two-and-three .two,
    .flex-container .two-and-three .three {
        flex-basis: 50%;
    }
    
    .flex-container .two-and-three .two {
        order: 1;
    }

    .flex-container .two-and-three .three {
        order: 2;
    }
    
    .flex-container .one {
        order: 1;
    }
    
    .flex-container .four {
        order: 4;
    }

}
<div class="flex-container">
    <div class="one">one</div>
    <div class="two-and-three">
        <div class="two">two</div>
        <div class="three">three</div>
    </div>
    <div class="four">four</div>
</div>
like image 21
ratherblue Avatar answered Oct 22 '22 19:10

ratherblue