Wildhammer
Wildhammer

Reputation: 2175

Flexbox layout multiple items wrapping around a big one

I want to have a layout like the following image using flexbox:

enter image description here

I have tried the following but no luck so far:

.flex-container {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  width: 200px;
}

.flex-item:not(:first-child) {
  width: calc(100% - 50px);
  background-color: gray;
  border: 1px solid blue;
}

.flex-item:first-child {
  width: 50px;
  height: 100%;
  border: 1px solid blue;
  background-color: red;
}
<div class="flex-container">
  <div class="flex-item">item 1(long)</div>
  <div class="flex-item">item 2</div>
  <div class="flex-item">item 3</div>
  <div class="flex-item">item 4</div>  
</div>

I need the DOM structure to be like this, otherwise, I already know how to achieve this with nested elements.

Upvotes: 2

Views: 1194

Answers (2)

tamarin
tamarin

Reputation: 549

CSS Grid Layout would be more appropriate.

body {
  background: white;
  color: #323232;
  font-weight: 300;
  height: 100vh;
  margin: 0;
  font-family: Helvetica neue, roboto;
}

.flex-container {
  display: grid;
  width: 90%;
  height: 50%;
  margin: 2rem auto;
  text-align: center;
  grid-template-columns: 25% 75%;
  grid-template-rows: 25% 25% 50%;
}

.flex-item1 {
  grid-column: 1 / 2;
  grid-row: 1 / 4;
  background: pink;
}

.flex-item2 {
  grid-column: 2 / 3;
  grid-row: 1 / 2;
  background: lightcoral;
}

.flex-item3 {
  grid-column: 2 / 3;
  grid-row: 2 / 3;
  background: lemonchiffon;
}

.flex-item4 {
  grid-column: 2 / 3;
  grid-row: 3 / 4;
  background: lightblue;
}
<div class="flex-container">
  <div class="flex-item1">item 1</div>
  <div class="flex-item2">item 2</div>
  <div class="flex-item3">item 3</div>
  <div class="flex-item4">item 4</div>
</div>

Although you can do it with Flexbox. It makes more sense to me to make a small change to the DOM structure. Your items 2, 3, 4 would be wrapped in an other Flex container.

body {
  background: white;
  color: #323232;
  font-weight: 300;
  height: 100vh;
  margin: 0;
  display: flex;
  font-family: Helvetica neue, roboto;
}

.flex-container {
  display: flex;
  flex-wrap: wrap;
  width: 90%;
  height: 50%;
  margin: 1rem;
  text-align: center;
}

.flex-itemA {
  width: 25%;
  background: pink;
}

/* Flex-itemB and Flex container itself for the next 3 elements */

.flex-itemB {
  width: 75%;
  display: flex;
  flex-wrap: wrap;
  flex-direction: column;
  background: lightblue;
}

.flex-itemB1,
.flex-itemB2 {
  height: 25%;
}

.flex-itemB1 {
  background: lightcoral;
}

.flex-itemB2 {
  background: lemonchiffon;
}
<div class="flex-container">
  <div class="flex-itemA">item A</div>
  <div class="flex-itemB">
    <div class="flex-itemB1">item B1</div>
    <div class="flex-itemB2">item B2</div>
    <div class="flex-itemB3">item B3</div>
  </div>
</div>

EDIT based on @lawrence-witt answer

Or if you really want to keep the DOM structure that way and use Flexbox and not Grid. Then you could do it like @lawrence-witt suggested. I kept the :nth-child selectors although it would be easy to add a class for each element and avoid increasing the specificity.

body {
  background: white;
  color: #323232;
  font-weight: 300;
  height: 100vh;
  margin: 0;
  font-family: Helvetica neue, roboto;
}

.flex-container {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  width: 90%;
  height: 50%;
  margin: 2rem 5%;
  text-align: center;
}

.flex-item {
  height: 100%;
  width: 25%;
  background: pink;
}

.flex-item:nth-child(n + 2) {
  width: 75%;
  /* This will make the height = 25% since the last element will have flex-grow: 1 */
  flex: 0.5;
}

.flex-item:nth-child(2) {
  background: lightcoral;
}

.flex-item:nth-child(3) {
  background: lemonchiffon;
}

.flex-item:nth-child(4) {
  flex: 1;
  background: lightblue;
}
<div class="flex-container">
  <div class="flex-item">item 1</div>
  <div class="flex-item">item 2</div>
  <div class="flex-item">item 3</div>
  <div class="flex-item">item 4</div>
</div>

Upvotes: 1

lawrence-witt
lawrence-witt

Reputation: 9354

It's possible using your DOM structure but I doubt it will be particularly useful, and as others have said, you're better off using Grid or even just inline elements. The trouble is you are trying to mix two layout contexts and flexbox isn't really built for that.

Give the container a explicit height and the wider elements a flex:1 property, which will make them grow to fill the remaining space. I used nth-child as it is more readable to me and your original code didn't actually behave as you intended when put into codepen.

.flex-container {
  border: 1px solid green;
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  width: 200px;
  height: 200px;
}

.flex-item {
  height: 100%;
  width: 50px;
  background-color: gray;
  border: 1px solid blue;
}

.flex-item:nth-child(n+2) {
  width: calc(100% - 50px);
  flex: 1;
  border: 1px solid blue;
  background-color: red;
}
<div class="flex-container">
  <div class="flex-item">item 1(long)</div>
  <div class="flex-item">item 2</div>
  <div class="flex-item">item 3</div>
  <div class="flex-item">item 4</div>  
</div>

Upvotes: 1

Related Questions