3

I am trying to create a flex container layout whereby one of the flex items should span two rows. See image below for a better explanation:

enter image description here

Here is my markup:

<div class="container">
    <div class="item item-1">ITEM 1</div>
    <div class="item item-2">ITEM 2</div>
    <div class="item item-3">ITEM 3</div>
</div>

I cannot seem to achieve this, I have tried using flex-wrap and different combinations of the flex property.

I was able to achieve this by putting ITEM 1 & ITEM 2 in a separate <div>, but this presents a problem on a smaller screen, whereby ITEM 3 needs to appear BETWEEN ITEM 1 & ITEM 2. So I would rather keep the markup as is and use the order property to move things around as necessary.

skobaljic
  • 9,379
  • 1
  • 25
  • 51
MAX POWER
  • 5,213
  • 15
  • 89
  • 141
  • 5
    _"but this presents a problem on a smaller screen, whereby ITEM 3 needs to appear BETWEEN ITEM 1 & ITEM 2"_ - in that case, its is better to use CSS Grid which will allow you to individually place the items in any row/column you want. – Yousaf Feb 08 '22 at 10:50
  • Why don't you design mobile-first and then use the grid for screen > 500? – Ali Mustafa Feb 08 '22 at 11:07
  • 2
    This is not a duplicate, because other answers use fixed height. This is a unique solution, which uses `display: contents`, which is a new css feature and this is a perfect usage example. Even more, other questions should lead to this one. Please reopen. – skobaljic Feb 08 '22 at 12:58

3 Answers3

3

You can use display: contents on your extra div to achieve what you want:

* {
  box-sizing: border-box;
}

.container {
  display: flex;
  width: 100%;
}

.holder {
  width: 67%;
}

.item {
  border: 1px solid black;
}

.item-1 {
  margin-bottom: 40px;
}

.item-3 {
  width: 33%;
}

@media screen and (max-width: 700px) {
  .container {
    flex-direction: column;
  }
  .item {
    margin-bottom: 20px;
  }
  .item-1 {
    order: 1;
  }
  .item-2 {
    order: 3;
  }
  .item-3 {
    order: 2;
    width: 100%;
  }
  .holder {
    width: 100%;
    display: contents;
  }
}
<div class="container">
  <div class="holder">
    <div class="item item-1">ITEM 1</div>
    <div class="item item-2">ITEM 2</div>
  </div>
  <div class="item item-3">ITEM 3</div>
</div>
Pete
  • 57,112
  • 28
  • 117
  • 166
  • 2
    Amazing! I didn't know about `display: contents` - that is a game changer! – MAX POWER Feb 08 '22 at 11:31
  • Keep the [accessibility concern](https://developer.mozilla.org/en-US/docs/Web/CSS/display#display_contents) in mind when using `display: contents`. – Yousaf Feb 08 '22 at 11:53
1
  1. You can't achieve it using flexbox. Instead, you should have two parents which are better.

  2. Use Css-grid. Actually, css-grid is the best option in this case.

Flex-Box

* {
  color: #fff;
}

.flex {
  display: flex;
  justify-content: center;
  align-items: center;
}

.child {
  border-radius: 10px;
}

.container {
  display: flex;
  width: 500px;
  height: 200px;
  border: 1px solid #ff0000;
}

.container .first-item {
  flex-direction: column;
  justify-content: space-between;
  align-items: start;
  width: 50%;
  height: 100%;
  margin-right: 10px;
}

.first-item .child {
  width: 100%;
  height: 50%;
  background-color: blue;
}

.first-item .child:first-child {
  margin-bottom: 10px;
}

.container .second-item {
  width: 50%;
  height: 100%;
}

.second-item .child {
  width: 100%;
  height: 100%;
  background-color: blue;
}
<div class="container">
  <div class="first-item flex">
    <div class="child flex">Item 1</div>
    <div class="child flex">Item 2</div>
  </div>
  <div class="second-item flex">
    <div class="child flex">Item 3</div>
  </div>
</div>

Grid

.flex {
  display: flex;
  justify-content: center;
  align-items: center;
}

.child {
  background-color: blue;
  border-radius: 10px;
}

.container {
  display: grid;
  grid-template-columns: 1fr 10px 1fr;
  grid-template-rows: 1fr 10px 1fr;
  grid-template-areas: "c1 . c3"
                       ".  . c3"
                       "c2 . c3";
 width: 500px;
  height: 300px;
  border: 1px solid red;
}

.container .child {
  border: 1px solid blue;
}

.child1 {
  grid-area: c1;
}

.child2 {
  grid-area: c2;
}

.child3 {
  grid-area: c3;
}
<div class="container">
  <div class="child child1 flex">Item 1</div>
  <div class="child child3 flex">Item 3</div>
  <div class="child child2 flex">Item 2</div>
</div>
Amini
  • 1,620
  • 1
  • 8
  • 26
0

i dont know if its a good solution but put two item3 codes one in the individual div (item1&2) and one outside then put the one in the div to display none in non-small screens and switch between them with mediaquery

@media (max-width: 40rem) {
.item3 {
display: none;
}
.mobile-item3{
display: block;
}
}