Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Have certain items stack along the cross axis within a row/column of a flex layout with only CSS

Tags:

html

css

flexbox

At one point I was toying with a CSS spec suggestion for this, but then I figured there is probably already a solution that I am missing. An example of the kind of layout I'm talking about would look something like this:

+-----------+---+
|     1     | 6 |
+---+---+---+   |
| 2 | 3 | 4 +---+
+---+---+---+ 7 |
|     5     |   |
+-----------+---+

The problem is those three boxes in the middle of the left column are stacked along the cross axis, and I can't find a mechanism in CSS to do this. I know this could be done with a div wrapped around those 3 items that is a row direction flex layout, but that approach breaks the flexibility of a flex layout because those items can no longer be re-ordered around the outer layout and a column/row break can no longer happen between them. So, how can this be achieved this with only CSS, so that the flex layout stays flexable?

HTML:

<div id="flex-layout">
<div id="item1">1</div>
<div id="item2">2</div>
<div id="item3">3</div>
<div id="item4">4</div>
<div id="item5">5</div>
<div id="item6">6</div>
<div id="item7">7</div>
</div>

CSS:

#flex-layout {
    display: flex;
    flex-direction: column;
    height: 300px;
    width: 400px;
    flex-wrap: wrap;
    align-items: stretch;
}

#item1 {
    flex: 0 0 100px;
    width: 300px;
}

#item2 {
    flex: 0 0 100px;
    width: 100px;
}

#item3 {
    flex: 0 0 100px;
    width: 100px;
}

#item4 {
    flex: 0 0 100px;
    width: 100px;
}

#item5 {
    flex: 0 0 100px;
}

#item6 {
    flex: 0 0 150px;
    width: 100px;
}

#item7 {
    flex: 0 0 150px;
}
like image 600
sigilbaram Avatar asked May 27 '15 17:05

sigilbaram


1 Answers

Using multiple flex containers would be easier.

But if you want a single container, you can still do it, with those assumptions:

  • The width of 6 and 7 is known
  • The height of 2, 3 and 4 is known

Then, you can

  • Use a row layout

    ┌─┬─┬─┬─┬─┬─┬─┐
    │1│2│3│4│5│6│7│
    └─┴─┴─┴─┴─┴─┴─┘
    
  • Reorder the flex items: 1,6,2,3,4,5,7

    ┌─┬─┬─┬─┬─┬─┬─┐
    │1│6│2│3│4│5│7│
    └─┴─┴─┴─┴─┴─┴─┘
    
  • Allow line breaks with flex-wrap: wrap.

  • Use pseudo elements to force a line break before 2 and after 4

    ┌─┬─┐
    │1│6│
    ├─┼─┼─┐
    │2│3│4│
    ├─┼─┼─┘
    │5│7│
    └─┴─┘
    
  • Use flex-grow: 0 on 6 and 7. Use flex-grow: 1 on the other ones.

    ┌─────────┬─┐
    │    1    │6│
    ├───┬───┬─┴─┤
    │ 2 │ 3 │ 4 │
    ├───┴───┴─┬─┤
    │    5    │7│
    └─────────┴─┘
    
  • Set the desired with, w, to 6 and 7. Add margin-right: w to 4

    ┌─────┬───┐
    │  1  │ 6 │
    ├─┬─┬─┼───┘
    │2│3│4│
    ├─┴─┴─┼───┐
    │  5  │ 7 │
    └─────┴───┘
    
  • Set the desired height, h, to 2, 3 and 4. Add margin-bottom: -h/2 to 6, and margin-top: -h/2 to 7.

    ┌─────┬───┐
    │  1  │ 6 │
    ├─┬─┬─┤   │
    │2│3│4├───┤
    ├─┴─┴─┤ 7 │
    │  5  │   │
    └─────┴───┘
    
  • Additionally, it might be a good idea adding a width or max-width to 2,3,4. Otherwise, if their content is wide enough, they will be placed in different lines, breaking the layout.

Here is the code:

#flex-layout {
  display: flex; /* Magic begins */
  flex-wrap: wrap; /* Multiline */
}
#item1 { order: 1; }
#item6 { order: 2; }
#item2 { order: 3; }
#item3 { order: 4; }
#item4 { order: 5; }
#item5 { order: 6; }
#item7 { order: 7; }
#flex-layout > div {
  border: 1px solid;
  box-sizing: border-box;
}
#item2, #item3, #item4 {
  height: 50px; /* h */
}
#item6 {
  margin-bottom: -25px; /* -h/2 */
}
#item7 {
  margin-top: -25px; /* -h/2 */
}
#item1, #item2, #item3, #item4, #item5 {
  flex-grow: 1;
}
#item6, #item7 {
  width: 25%; /* w */
  flex-grow: 0;
}
#item4 {
  margin-right: 25%; /* w */
}
#flex-layout:before {
  /* Force line break before #item2 */
  content: '';
  width: 100%;
  order: 3;
}
#flex-layout:after {
  /* Force line break after #item4 */
  content: '';
  width: 100%;
  order: 5;
}
<div id="flex-layout">
  <div id="item1">1</div>
  <div id="item2">2</div>
  <div id="item3">3</div>
  <div id="item4">4</div>
  <div id="item5">5</div>
  <div id="item6">6</div>
  <div id="item7">7</div>
</div>
like image 198
Oriol Avatar answered Nov 29 '22 14:11

Oriol